summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md7
-rw-r--r--application/src/main/java/com/yahoo/application/Application.java6
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/ApplicationConfigProducerRoot.java9
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/CommonConfigsProducer.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java18
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java59
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchClusterTest.java15
-rw-r--r--configdefinitions/src/vespa/CMakeLists.txt2
-rw-r--r--configdefinitions/src/vespa/all-clusters-bucket-spaces.def9
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerApiImpl.java4
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java3
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/IOExceptionUtil.java1
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/UnixPath.java1
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileDeleterTest.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java2
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/duper/ConfigServerApplication.java64
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/duper/ConfigServerHostApplication.java8
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/duper/ConfigServerLikeApplication.java17
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/duper/ControllerApplication.java6
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/duper/ControllerHostApplication.java7
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModelManager.java33
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/duper/HostAdminApplication.java16
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/duper/InfraApplication.java99
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/duper/ProxyHostApplication.java7
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/health/ApplicationHealthMonitorFactory.java12
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/health/HealthMonitorManager.java29
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/model/ApplicationInstanceGenerator.java10
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceId.java6
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/slobrok/SlobrokMonitorManagerImpl.java10
-rw-r--r--service-monitor/src/test/java/com/yahoo/vespa/service/health/ApplicationHealthMonitorTest.java36
-rw-r--r--service-monitor/src/test/java/com/yahoo/vespa/service/health/HealthMonitorManagerTest.java87
-rw-r--r--service-monitor/src/test/java/com/yahoo/vespa/service/model/ApplicationInstanceGeneratorTest.java18
-rw-r--r--service-monitor/src/test/java/com/yahoo/vespa/service/model/ModelGeneratorTest.java7
-rw-r--r--service-monitor/src/test/java/com/yahoo/vespa/service/monitor/ConfigserverUtil.java2
-rw-r--r--service-monitor/src/test/java/com/yahoo/vespa/service/slobrok/SlobrokMonitorManagerImplTest.java7
-rw-r--r--staging_vespalib/CMakeLists.txt1
-rw-r--r--staging_vespalib/src/tests/assert/.gitignore1
-rw-r--r--staging_vespalib/src/tests/assert/CMakeLists.txt16
-rw-r--r--staging_vespalib/src/tests/assert/assert_test.cpp39
-rw-r--r--staging_vespalib/src/tests/assert/asserter.cpp25
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/CMakeLists.txt1
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/assert.cpp77
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/assert.h38
-rw-r--r--vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java18
-rw-r--r--vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/RestApi.java11
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/OperationHandlerImplTest.java2
-rw-r--r--vespalib/src/vespa/vespalib/component/version.cpp3
-rw-r--r--vespalib/src/vespa/vespalib/component/versionspecification.cpp2
-rw-r--r--vespalib/src/vespa/vespalib/component/versionspecification.h2
-rw-r--r--vespalib/src/vespa/vespalib/component/vtag.cpp2
-rw-r--r--yolean/src/main/java/com/yahoo/yolean/Exceptions.java13
-rw-r--r--yolean/src/test/java/com/yahoo/yolean/ExceptionsTestCase.java14
52 files changed, 668 insertions, 218 deletions
diff --git a/README.md b/README.md
index c07b8d5e45a..1ade07c40ad 100644
--- a/README.md
+++ b/README.md
@@ -7,8 +7,6 @@ Vespa is an engine for low-latency computation over large data sets.
It stores and indexes your data and executes distributed queries including evaluation of
machine-learned models over many data points in real time.
-Code licensed under the Apache 2.0 license. See [LICENSE](LICENSE) for terms.
-
Travis-CI build status: [![Build Status](https://travis-ci.org/vespa-engine/vespa.svg?branch=master)](https://travis-ci.org/vespa-engine/vespa)
## Table of contents
@@ -18,6 +16,7 @@ Travis-CI build status: [![Build Status](https://travis-ci.org/vespa-engine/vesp
- [Usage](#usage)
- [Contribute](#contribute)
- [Building](#building)
+- [License](#license)
## Background
@@ -95,3 +94,7 @@ Replace `<source-dir>` with the directory in which you've cloned/unpacked the so
### Create RPM packages
sh dist.sh VERSION && rpmbuild -ba ~/rpmbuild/SPECS/vespa-VERSION.spec
+
+## License
+
+Code licensed under the Apache 2.0 license. See [LICENSE](LICENSE) for terms.
diff --git a/application/src/main/java/com/yahoo/application/Application.java b/application/src/main/java/com/yahoo/application/Application.java
index 88140873b7b..64ced48edd3 100644
--- a/application/src/main/java/com/yahoo/application/Application.java
+++ b/application/src/main/java/com/yahoo/application/Application.java
@@ -20,6 +20,7 @@ import com.yahoo.search.rendering.Renderer;
import com.yahoo.text.StringUtilities;
import com.yahoo.text.Utf8;
import com.yahoo.vespa.model.VespaModel;
+import com.yahoo.yolean.Exceptions;
import org.xml.sax.SAXException;
import java.io.File;
@@ -315,8 +316,9 @@ public final class Application implements AutoCloseable {
break;
} catch (Error e) { // the container thinks this is really serious, in this case is it not in the cause is a BindException
// catch bind error and reset container
- if (e.getCause() != null && e.getCause().getCause() != null && e.getCause().getCause() instanceof BindException) {
- exception = (Exception) e.getCause().getCause();
+ Optional<BindException> bindException = Exceptions.findCause(e, BindException.class);
+ if (bindException.isPresent()) {
+ exception = bindException.get();
com.yahoo.container.Container.resetInstance(); // this is needed to be able to recreate the container from config again
} else {
throw new Exception(e.getCause());
diff --git a/config-model/src/main/java/com/yahoo/config/model/ApplicationConfigProducerRoot.java b/config-model/src/main/java/com/yahoo/config/model/ApplicationConfigProducerRoot.java
index af9eba92a98..aede68a1dd1 100644
--- a/config-model/src/main/java/com/yahoo/config/model/ApplicationConfigProducerRoot.java
+++ b/config-model/src/main/java/com/yahoo/config/model/ApplicationConfigProducerRoot.java
@@ -19,6 +19,7 @@ import com.yahoo.document.DocumenttypesConfig;
import com.yahoo.document.config.DocumentmanagerConfig;
import com.yahoo.documentapi.messagebus.protocol.DocumentrouteselectorpolicyConfig;
import com.yahoo.messagebus.MessagebusConfig;
+import com.yahoo.vespa.config.content.AllClustersBucketSpacesConfig;
import com.yahoo.vespa.configmodel.producers.DocumentManager;
import com.yahoo.vespa.configmodel.producers.DocumentTypes;
import com.yahoo.vespa.documentmodel.DocumentModel;
@@ -212,6 +213,14 @@ public class ApplicationConfigProducerRoot extends AbstractConfigProducer<Abstra
}
@Override
+ public void getConfig(AllClustersBucketSpacesConfig.Builder builder) {
+ VespaModel model = (VespaModel) getRoot();
+ for (ContentCluster cluster : model.getContentClusters().values()) {
+ builder.cluster(cluster.getName(), cluster.clusterBucketSpaceConfigBuilder());
+ }
+ }
+
+ @Override
public void getConfig(ModelConfig.Builder builder) {
builder.vespaVersion(vespaVersion.toFullString());
for (HostResource modelHost : getHostSystem().getHosts()) {
diff --git a/config-model/src/main/java/com/yahoo/config/model/CommonConfigsProducer.java b/config-model/src/main/java/com/yahoo/config/model/CommonConfigsProducer.java
index 2a30a89dd8a..9dcd94bf455 100644
--- a/config-model/src/main/java/com/yahoo/config/model/CommonConfigsProducer.java
+++ b/config-model/src/main/java/com/yahoo/config/model/CommonConfigsProducer.java
@@ -12,6 +12,7 @@ import com.yahoo.document.DocumenttypesConfig;
import com.yahoo.document.config.DocumentmanagerConfig;
import com.yahoo.documentapi.messagebus.protocol.DocumentrouteselectorpolicyConfig;
import com.yahoo.messagebus.MessagebusConfig;
+import com.yahoo.vespa.config.content.AllClustersBucketSpacesConfig;
/**
@@ -29,6 +30,7 @@ public interface CommonConfigsProducer extends DocumentmanagerConfig.Producer,
ZookeepersConfig.Producer,
LoadTypeConfig.Producer,
ClusterListConfig.Producer,
+ AllClustersBucketSpacesConfig.Producer,
ModelConfig.Producer,
ApplicationIdConfig.Producer {
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
index 27da41c2bfa..1ec56fe529a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
@@ -12,6 +12,7 @@ import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.config.content.MessagetyperouteselectorpolicyConfig;
import com.yahoo.vespa.config.content.FleetcontrollerConfig;
import com.yahoo.vespa.config.content.StorDistributionConfig;
+import com.yahoo.vespa.config.content.AllClustersBucketSpacesConfig;
import com.yahoo.vespa.config.content.core.BucketspacesConfig;
import com.yahoo.vespa.config.content.core.StorDistributormanagerConfig;
import com.yahoo.documentmodel.NewDocumentType;
@@ -736,13 +737,26 @@ public class ContentCluster extends AbstractConfigProducer implements
private static final String DEFAULT_BUCKET_SPACE = "default";
private static final String GLOBAL_BUCKET_SPACE = "global";
+ private String bucketSpaceOfDocumentType(NewDocumentType docType) {
+ return (isGloballyDistributed(docType) ? GLOBAL_BUCKET_SPACE : DEFAULT_BUCKET_SPACE);
+ }
+
+ public AllClustersBucketSpacesConfig.Cluster.Builder clusterBucketSpaceConfigBuilder() {
+ AllClustersBucketSpacesConfig.Cluster.Builder builder = new AllClustersBucketSpacesConfig.Cluster.Builder();
+ for (NewDocumentType docType : getDocumentDefinitions().values()) {
+ AllClustersBucketSpacesConfig.Cluster.DocumentType.Builder typeBuilder = new AllClustersBucketSpacesConfig.Cluster.DocumentType.Builder();
+ typeBuilder.bucketSpace(bucketSpaceOfDocumentType(docType));
+ builder.documentType(docType.getName(), typeBuilder);
+ }
+ return builder;
+ }
+
@Override
public void getConfig(BucketspacesConfig.Builder builder) {
for (NewDocumentType docType : getDocumentDefinitions().values()) {
BucketspacesConfig.Documenttype.Builder docTypeBuilder = new BucketspacesConfig.Documenttype.Builder();
docTypeBuilder.name(docType.getName());
- String bucketSpace = (isGloballyDistributed(docType) ? GLOBAL_BUCKET_SPACE : DEFAULT_BUCKET_SPACE);
- docTypeBuilder.bucketspace(bucketSpace);
+ docTypeBuilder.bucketspace(bucketSpaceOfDocumentType(docType));
builder.documenttype(docTypeBuilder);
}
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java
index 53456c627a4..a003e0122a5 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java
@@ -12,6 +12,7 @@ import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.config.content.core.StorDistributormanagerConfig;
import com.yahoo.vespa.config.content.StorFilestorConfig;
import com.yahoo.vespa.config.content.core.StorServerConfig;
+import com.yahoo.vespa.config.content.AllClustersBucketSpacesConfig;
import com.yahoo.vespa.config.content.FleetcontrollerConfig;
import com.yahoo.vespa.config.content.StorDistributionConfig;
import com.yahoo.metrics.MetricsmanagerConfig;
@@ -29,6 +30,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@@ -823,6 +825,63 @@ public class ContentClusterTest extends ContentBaseTest {
new VespaModelCreatorWithMockPkg(null, xml, sds).create();
}
+ private void assertClusterHasBucketSpaceMappings(AllClustersBucketSpacesConfig config, String clusterId,
+ List<String> defaultSpaceTypes, List<String> globalSpaceTypes) {
+ AllClustersBucketSpacesConfig.Cluster cluster = config.cluster(clusterId);
+ assertNotNull(cluster);
+ assertEquals(defaultSpaceTypes.size() + globalSpaceTypes.size(), cluster.documentType().size());
+ assertClusterHasTypesInBucketSpace(cluster, "default", defaultSpaceTypes);
+ assertClusterHasTypesInBucketSpace(cluster, "global", globalSpaceTypes);
+ }
+
+ private void assertClusterHasTypesInBucketSpace(AllClustersBucketSpacesConfig.Cluster cluster,
+ String bucketSpace, List<String> expectedTypes) {
+ for (String type : expectedTypes) {
+ assertNotNull(cluster.documentType(type));
+ assertEquals(bucketSpace, cluster.documentType(type).bucketSpace());
+ }
+ }
+
+ @Test
+ public void all_clusters_bucket_spaces_config_contains_mappings_across_all_clusters() {
+ String xml =
+ "<services>" +
+ "<admin version=\"2.0\">" +
+ " <adminserver hostalias=\"node0\"/>" +
+ "</admin>" +
+ "<content version=\"1.0\" id=\"foocluster\">" +
+ " <redundancy>1</redundancy>" +
+ " <documents>" +
+ " <document type=\"bunnies\" mode=\"index\"/>" +
+ " <document type=\"hares\" mode=\"index\"/>" +
+ " </documents>" +
+ " <group>" +
+ " <node distribution-key=\"0\" hostalias=\"node0\"/>" +
+ " </group>" +
+ "</content>" +
+ "<content version=\"1.0\" id=\"barcluster\">" +
+ " <redundancy>1</redundancy>" +
+ " <documents>" +
+ " <document type=\"rabbits\" mode=\"index\" global=\"true\"/>" +
+ " </documents>" +
+ " <group>" +
+ " <node distribution-key=\"0\" hostalias=\"node0\"/>" +
+ " </group>" +
+ "</content>" +
+ "</services>";
+ List<String> sds = ApplicationPackageUtils.generateSearchDefinitions("bunnies", "hares", "rabbits");
+ VespaModel model = new VespaModelCreatorWithMockPkg(getHosts(), xml, sds).create();
+
+ AllClustersBucketSpacesConfig.Builder builder = new AllClustersBucketSpacesConfig.Builder();
+ model.getConfig(builder, "client");
+ AllClustersBucketSpacesConfig config = builder.build();
+
+ assertEquals(2, config.cluster().size());
+
+ assertClusterHasBucketSpaceMappings(config, "foocluster", Arrays.asList("bunnies", "hares"), Collections.emptyList());
+ assertClusterHasBucketSpaceMappings(config, "barcluster", Collections.emptyList(), Collections.singletonList("rabbits"));
+ }
+
private ContentCluster createWithZone(String clusterXml, Zone zone) throws Exception {
DeployState.Builder deployStateBuilder = new DeployState.Builder()
.zone(zone)
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchClusterTest.java
index 83b4cfebca5..98fa179b219 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchClusterTest.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.model.content;
import com.yahoo.vespa.config.content.FleetcontrollerConfig;
+import com.yahoo.vespa.config.content.AllClustersBucketSpacesConfig;
import com.yahoo.vespa.config.content.core.BucketspacesConfig;
import com.yahoo.vespa.config.search.core.ProtonConfig;
import com.yahoo.vespa.model.content.cluster.ContentCluster;
@@ -19,6 +20,7 @@ import static com.yahoo.vespa.model.content.utils.ContentClusterUtils.createClus
import static com.yahoo.vespa.model.content.utils.SearchDefinitionBuilder.createSearchDefinitions;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
@@ -168,6 +170,19 @@ public class ContentSearchClusterTest {
}
@Test
+ public void bucket_space_config_builder_returns_correct_mappings() throws Exception {
+ ContentCluster cluster = createClusterWithGlobalType();
+ BucketspacesConfig expected = getBucketspacesConfig(cluster);
+ AllClustersBucketSpacesConfig.Cluster actual = cluster.clusterBucketSpaceConfigBuilder().build();
+ assertEquals(2, expected.documenttype().size());
+ assertEquals(expected.documenttype().size(), actual.documentType().size());
+ assertNotNull(actual.documentType("global"));
+ assertEquals("global", actual.documentType().get("global").bucketSpace());
+ assertNotNull(actual.documentType("regular"));
+ assertEquals("default", actual.documentType().get("regular").bucketSpace());
+ }
+
+ @Test
public void cluster_with_global_document_types_sets_cluster_controller_global_docs_config_option() throws Exception {
ContentCluster cluster = createClusterWithGlobalType();
assertTrue(getFleetcontrollerConfig(cluster).cluster_has_global_document_types());
diff --git a/configdefinitions/src/vespa/CMakeLists.txt b/configdefinitions/src/vespa/CMakeLists.txt
index 0a7d4ef4381..120c403eb74 100644
--- a/configdefinitions/src/vespa/CMakeLists.txt
+++ b/configdefinitions/src/vespa/CMakeLists.txt
@@ -72,5 +72,7 @@ vespa_generate_config(configdefinitions zookeepers.def)
install_config_definition(zookeepers.def cloud.config.zookeepers.def)
vespa_generate_config(configdefinitions bucketspaces.def)
install_config_definition(bucketspaces.def vespa.config.content.core.bucketspaces.def)
+vespa_generate_config(configdefinitions all-clusters-bucket-spaces.def)
+install_config_definition(all-clusters-bucket-spaces.def vespa.config.content.all-clusters-bucket-spaces.def)
vespa_generate_config(configdefinitions stateserver.def)
install_config_definition(stateserver.def vespa.config.core.stateserver.def)
diff --git a/configdefinitions/src/vespa/all-clusters-bucket-spaces.def b/configdefinitions/src/vespa/all-clusters-bucket-spaces.def
new file mode 100644
index 00000000000..7676bc2a03f
--- /dev/null
+++ b/configdefinitions/src/vespa/all-clusters-bucket-spaces.def
@@ -0,0 +1,9 @@
+# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+namespace=vespa.config.content
+
+## This config contains the document types handled by all content clusters
+## and the bucket spaces they belong to.
+
+## The bucket space a document type in a particular cluster belongs to.
+cluster{}.documentType{}.bucketSpace string
+
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerApiImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerApiImpl.java
index 6d97b64c441..1bb0cf3fb10 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerApiImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerApiImpl.java
@@ -127,13 +127,15 @@ public class ConfigServerApiImpl implements ConfigServerApi {
if (!e.isRetryable()) throw e;
lastException = e;
} catch (Exception e) {
+ lastException = e;
+ if (configServers.size() == 1) break;
+
// Failure to communicate with a config server is not abnormal during upgrades
if (e.getMessage().contains("(Connection refused)")) {
NODE_ADMIN_LOGGER.info("Connection refused to " + configServer + " (upgrading?), will try next");
} else {
NODE_ADMIN_LOGGER.warning("Failed to communicate with " + configServer + ", will try next: " + e.getMessage());
}
- lastException = e;
}
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java
index 910c94c54b1..89ab2e60b63 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java
@@ -265,9 +265,6 @@ public class DockerOperationsImpl implements DockerOperations {
return docker.getAllContainersManagedBy(MANAGER_NAME);
}
- /**
- * Returns map of directories to mount and whether they should be writable by everyone
- */
private static void addMounts(NodeAgentContext context, Docker.CreateContainerCommand command) {
final Path varLibSia = Paths.get("/var/lib/sia");
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/IOExceptionUtil.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/IOExceptionUtil.java
index 8a23b1d18b1..9a35188a372 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/IOExceptionUtil.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/IOExceptionUtil.java
@@ -3,7 +3,6 @@ package com.yahoo.vespa.hosted.node.admin.task.util.file;
import com.yahoo.yolean.Exceptions;
-import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.NoSuchFileException;
import java.util.Optional;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/UnixPath.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/UnixPath.java
index 67fcfc69393..e2b19e73fa3 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/UnixPath.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/UnixPath.java
@@ -28,7 +28,6 @@ import java.util.stream.Stream;
import static com.yahoo.vespa.hosted.node.admin.task.util.file.IOExceptionUtil.ifExists;
import static com.yahoo.yolean.Exceptions.uncheck;
-import static com.yahoo.yolean.Exceptions.uncheckAndIgnore;
/**
* Thin wrapper around java.nio.file.Path, especially nice for UNIX-specific features.
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileDeleterTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileDeleterTest.java
index 3a60061344f..09f7d6117f9 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileDeleterTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileDeleterTest.java
@@ -18,7 +18,7 @@ public class FileDeleterTest {
private final TaskContext context = mock(TaskContext.class);
@Test
- public void deleteExisting() throws IOException {
+ public void deleteExisting() {
assertFalse(deleter.converge(context));
path.createParents().writeUtf8File("bar");
assertTrue(deleter.converge(context));
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java
index a3c7759779a..54f5403ca73 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java
@@ -312,7 +312,7 @@ public class ProvisioningTester {
nodes = nodeRepository.setDirty(nodes, Agent.system, getClass().getSimpleName());
nodeRepository.setReady(nodes, Agent.system, getClass().getSimpleName());
- ConfigServerApplication application = ConfigServerApplication.CONFIG_SERVER_APPLICATION;
+ ConfigServerApplication application = new ConfigServerApplication();
List<HostSpec> hosts = prepare(
application.getApplicationId(),
application.getClusterSpecWithVersion(configServersVersion),
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ConfigServerApplication.java b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ConfigServerApplication.java
index f93f25a4eb3..88c2c0d4469 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ConfigServerApplication.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ConfigServerApplication.java
@@ -3,76 +3,30 @@ package com.yahoo.vespa.service.duper;
import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.config.model.api.ApplicationInfo;
-import com.yahoo.config.model.api.HostInfo;
-import com.yahoo.config.model.api.PortInfo;
-import com.yahoo.config.model.api.ServiceInfo;
import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeType;
-import com.yahoo.vespa.applicationmodel.ApplicationInstanceId;
-import com.yahoo.vespa.applicationmodel.ClusterId;
-import com.yahoo.vespa.applicationmodel.ConfigId;
import com.yahoo.vespa.applicationmodel.ServiceType;
-import com.yahoo.vespa.applicationmodel.TenantId;
-import com.yahoo.vespa.service.model.ModelGenerator;
-import com.yahoo.vespa.service.health.ApplicationHealthMonitor;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
+import java.util.stream.Collectors;
/**
* A service/application model of the config server with health status.
*/
-public class ConfigServerApplication extends InfraApplication {
+public class ConfigServerApplication extends ConfigServerLikeApplication {
public static final ConfigServerApplication CONFIG_SERVER_APPLICATION = new ConfigServerApplication();
- public static final TenantId TENANT_ID = new TenantId(CONFIG_SERVER_APPLICATION.getApplicationId().tenant().value());
- public static final ApplicationInstanceId APPLICATION_INSTANCE_ID =
- new ApplicationInstanceId(CONFIG_SERVER_APPLICATION.getApplicationId().application().value());
- public static final ClusterId CLUSTER_ID = new ClusterId(CONFIG_SERVER_APPLICATION.getClusterId().value());
- public static final ServiceType SERVICE_TYPE = new ServiceType("configserver");
- public static final String CONFIG_ID_PREFIX = "configid.";
-
- public static ConfigId configIdFrom(int index) {
- return new ConfigId(CONFIG_ID_PREFIX + index);
- }
public ConfigServerApplication() {
- super("zone-config-servers", NodeType.config,
- ClusterSpec.Type.admin, ClusterSpec.Id.from("zone-config-servers"), ServiceType.CONFIG_SERVER);
+ super("zone-config-servers", NodeType.config, ClusterSpec.Type.admin, ServiceType.CONFIG_SERVER);
}
public ApplicationInfo makeApplicationInfoFromConfig(ConfigserverConfig config) {
- List<HostInfo> hostInfos = new ArrayList<>();
- List<ConfigserverConfig.Zookeeperserver> zooKeeperServers = config.zookeeperserver();
- for (int index = 0; index < zooKeeperServers.size(); ++index) {
- String hostname = zooKeeperServers.get(index).hostname();
- hostInfos.add(makeHostInfo(hostname, config.httpport(), index));
- }
-
- return new ApplicationInfo(
- CONFIG_SERVER_APPLICATION.getApplicationId(),
- 0,
- new HostsModel(hostInfos));
- }
-
- private static HostInfo makeHostInfo(String hostname, int port, int configIndex) {
- PortInfo portInfo = new PortInfo(port, ApplicationHealthMonitor.PORT_TAGS_HEALTH);
-
- Map<String, String> properties = new HashMap<>();
- properties.put(ModelGenerator.CLUSTER_ID_PROPERTY_NAME, CLUSTER_ID.s());
-
- ServiceInfo serviceInfo = new ServiceInfo(
- // service name == service type for the first service of each type on each host
- SERVICE_TYPE.s(),
- SERVICE_TYPE.s(),
- Collections.singletonList(portInfo),
- properties,
- configIdFrom(configIndex).s(),
- hostname);
-
- return new HostInfo(hostname, Collections.singletonList(serviceInfo));
+ List<HostName> hostnames = config.zookeeperserver().stream()
+ .map(ConfigserverConfig.Zookeeperserver::hostname)
+ .map(HostName::from)
+ .collect(Collectors.toList());
+ return makeApplicationInfo(hostnames);
}
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ConfigServerHostApplication.java b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ConfigServerHostApplication.java
index f29cc89e987..4273bccbf45 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ConfigServerHostApplication.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ConfigServerHostApplication.java
@@ -1,14 +1,10 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.service.duper;
-import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.NodeType;
-import com.yahoo.vespa.applicationmodel.ServiceType;
-public class ConfigServerHostApplication extends InfraApplication {
+public class ConfigServerHostApplication extends HostAdminApplication {
public ConfigServerHostApplication() {
- super("configserver-host", NodeType.confighost,
- ClusterSpec.Type.container, ClusterSpec.Id.from("configserver-host"),
- ServiceType.HOST_ADMIN);
+ super("configserver-host", NodeType.confighost);
}
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ConfigServerLikeApplication.java b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ConfigServerLikeApplication.java
new file mode 100644
index 00000000000..ac5f13e2394
--- /dev/null
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ConfigServerLikeApplication.java
@@ -0,0 +1,17 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.service.duper;
+
+import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.NodeType;
+import com.yahoo.vespa.applicationmodel.ServiceType;
+
+/**
+ * Base class for config server and controller infrastructure applications.
+ *
+ * @author hakonhall
+ */
+public abstract class ConfigServerLikeApplication extends InfraApplication {
+ protected ConfigServerLikeApplication(String applicationName, NodeType nodeType, ClusterSpec.Type clusterType, ServiceType serviceType) {
+ super(applicationName, nodeType, clusterType, ClusterSpec.Id.from(applicationName), serviceType, 19071);
+ }
+}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ControllerApplication.java b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ControllerApplication.java
index 1fb01ce0897..3d69b1c9925 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ControllerApplication.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ControllerApplication.java
@@ -8,10 +8,8 @@ import com.yahoo.vespa.applicationmodel.ServiceType;
/**
* @author mpolden
*/
-public class ControllerApplication extends InfraApplication {
+public class ControllerApplication extends ConfigServerLikeApplication {
public ControllerApplication() {
- super("controller", NodeType.controller, ClusterSpec.Type.container,
- ClusterSpec.Id.from("controller"), ServiceType.CONTROLLER);
+ super("controller", NodeType.controller, ClusterSpec.Type.container, ServiceType.CONTROLLER);
}
-
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ControllerHostApplication.java b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ControllerHostApplication.java
index b8e8ead5b7e..7a0c952780d 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ControllerHostApplication.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ControllerHostApplication.java
@@ -1,16 +1,13 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.service.duper;
-import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.NodeType;
-import com.yahoo.vespa.applicationmodel.ServiceType;
/**
* @author mpolden
*/
-public class ControllerHostApplication extends InfraApplication {
+public class ControllerHostApplication extends HostAdminApplication {
public ControllerHostApplication() {
- super("controller-host", NodeType.controllerhost, ClusterSpec.Type.container,
- ClusterSpec.Id.from("controller-host"), ServiceType.HOST_ADMIN);
+ super("controller-host", NodeType.controllerhost);
}
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModelManager.java b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModelManager.java
index ed99d2e3166..e0e2b64bdae 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModelManager.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModelManager.java
@@ -32,13 +32,14 @@ public class DuperModelManager implements DuperModelInfraApi {
private static Logger logger = Logger.getLogger(DuperModelManager.class.getName());
// Infrastructure applications
+ private final ConfigServerHostApplication configServerHostApplication = new ConfigServerHostApplication();
+ private final ProxyHostApplication proxyHostApplication = new ProxyHostApplication();
+ private final ControllerApplication controllerApplication = new ControllerApplication();
+ private final ControllerHostApplication controllerHostApplication = new ControllerHostApplication();
+ // this must be static to be referenced in this(). Remove static once legacy config server from config is gone.
private static final ConfigServerApplication configServerApplication = new ConfigServerApplication();
- private static final ConfigServerHostApplication configServerHostApplication = new ConfigServerHostApplication();
- private static final ProxyHostApplication proxyHostApplication = new ProxyHostApplication();
- private static final ControllerApplication controllerApplication = new ControllerApplication();
- private static final ControllerHostApplication controllerHostApplication = new ControllerHostApplication();
- private static final Map<ApplicationId, InfraApplication> supportedInfraApplications = Stream.of(
+ private final Map<ApplicationId, InfraApplication> supportedInfraApplications = Stream.of(
configServerApplication,
configServerHostApplication,
proxyHostApplication,
@@ -52,19 +53,9 @@ public class DuperModelManager implements DuperModelInfraApi {
private final Object monitor = new Object();
private final DuperModel duperModel;
- // The set of active infrastructure ApplicationInfo. Not all is necessarily in the DuperModel for historical reasons.
+ // The set of active infrastructure ApplicationInfo. Not all are necessarily in the DuperModel for historical reasons.
private final Set<ApplicationId> activeInfraInfos = new HashSet<>(2 * supportedInfraApplications.size());
- /**
- * Returns true if application is considered an infrastructure application by the DuperModel.
- *
- * <p>Note: The tenant host "application" is NOT considered an infrastructure application: It is just a
- * cluster in the {@link ZoneApplication zone application}.
- */
- public static boolean isInfrastructureApplication(ApplicationId applicationId) {
- return supportedInfraApplications.containsKey(applicationId);
- }
-
@Inject
public DuperModelManager(ConfigserverConfig configServerConfig, FlagSource flagSource, SuperModelProvider superModelProvider) {
this(
@@ -148,6 +139,16 @@ public class DuperModelManager implements DuperModelInfraApi {
return new ArrayList<>(supportedInfraApplications.values());
}
+ /**
+ * Returns true if application is considered an infrastructure application by the DuperModel.
+ *
+ * <p>Note: The tenant host "application" is NOT considered an infrastructure application: It is just a
+ * cluster in the {@link ZoneApplication zone application}.
+ */
+ public boolean isSupportedInfraApplication(ApplicationId applicationId) {
+ return supportedInfraApplications.containsKey(applicationId);
+ }
+
@Override
public boolean infraApplicationIsActive(ApplicationId applicationId) {
synchronized (monitor) {
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/HostAdminApplication.java b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/HostAdminApplication.java
new file mode 100644
index 00000000000..5e6cb23e9c1
--- /dev/null
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/HostAdminApplication.java
@@ -0,0 +1,16 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.service.duper;
+
+import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.NodeType;
+import com.yahoo.vespa.applicationmodel.ServiceType;
+
+/**
+ * @author hakonhall
+ */
+public abstract class HostAdminApplication extends InfraApplication {
+ protected HostAdminApplication(String applicationName, NodeType nodeType) {
+ super(applicationName, nodeType, ClusterSpec.Type.container, ClusterSpec.Id.from(applicationName),
+ ServiceType.HOST_ADMIN, 8080);
+ }
+}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/InfraApplication.java b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/InfraApplication.java
index 89aabbfe2b0..8c74fe0396e 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/InfraApplication.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/InfraApplication.java
@@ -1,6 +1,7 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.service.duper;
+import com.yahoo.component.Version;
import com.yahoo.config.model.api.ApplicationInfo;
import com.yahoo.config.model.api.HostInfo;
import com.yahoo.config.model.api.PortInfo;
@@ -11,34 +12,34 @@ import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.TenantName;
-import com.yahoo.component.Version;
+import com.yahoo.vespa.applicationmodel.ApplicationInstanceId;
+import com.yahoo.vespa.applicationmodel.ClusterId;
import com.yahoo.vespa.applicationmodel.ConfigId;
import com.yahoo.vespa.applicationmodel.ServiceType;
-import com.yahoo.vespa.service.monitor.InfraApplicationApi;
-import com.yahoo.vespa.service.model.ModelGenerator;
+import com.yahoo.vespa.applicationmodel.TenantId;
import com.yahoo.vespa.service.health.ApplicationHealthMonitor;
+import com.yahoo.vespa.service.model.ModelGenerator;
+import com.yahoo.vespa.service.monitor.InfraApplicationApi;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.stream.Collectors;
/**
* @author freva
*/
public abstract class InfraApplication implements InfraApplicationApi {
- static final int HEALTH_PORT = 8080;
-
private static final TenantName TENANT_NAME = TenantName.from("hosted-vespa");
- private static final String CONFIG_ID_PREFIX = "configid.";
private final ApplicationId applicationId;
private final Capacity capacity;
- private final ClusterSpec.Type clusterType;
- private final ClusterSpec.Id clusterId;
+ private final ClusterSpec.Type clusterSpecType;
+ private final ClusterSpec.Id clusterSpecId;
private final ServiceType serviceType;
+ private final int healthPort;
public static ApplicationId createHostedVespaApplicationId(String applicationName) {
return new ApplicationId.Builder()
@@ -49,14 +50,16 @@ public abstract class InfraApplication implements InfraApplicationApi {
protected InfraApplication(String applicationName,
NodeType nodeType,
- ClusterSpec.Type clusterType,
- ClusterSpec.Id clusterId,
- ServiceType serviceType) {
+ ClusterSpec.Type clusterSpecType,
+ ClusterSpec.Id clusterSpecId,
+ ServiceType serviceType,
+ int healthPort) {
this.applicationId = createHostedVespaApplicationId(applicationName);
this.capacity = Capacity.fromRequiredNodeType(nodeType);
- this.clusterType = clusterType;
- this.clusterId = clusterId;
+ this.clusterSpecType = clusterSpecType;
+ this.clusterSpecId = clusterSpecId;
this.serviceType = serviceType;
+ this.healthPort = healthPort;
}
@Override
@@ -71,31 +74,43 @@ public abstract class InfraApplication implements InfraApplicationApi {
@Override
public ClusterSpec getClusterSpecWithVersion(Version version) {
- return ClusterSpec.request(clusterType, clusterId, version, true);
+ return ClusterSpec.request(clusterSpecType, clusterSpecId, version, true);
}
- public ClusterSpec.Type getClusterType() {
- return clusterType;
+ public ClusterSpec.Type getClusterSpecType() {
+ return clusterSpecType;
}
- public ClusterSpec.Id getClusterId() {
- return clusterId;
+ public ClusterSpec.Id getClusterSpecId() {
+ return clusterSpecId;
}
- public ApplicationInfo makeApplicationInfo(List<HostName> hostnames) {
- List<HostInfo> hostInfos = new ArrayList<>();
- for (int index = 0; index < hostnames.size(); ++index) {
- hostInfos.add(makeHostInfo(hostnames.get(index), HEALTH_PORT, index, serviceType, clusterId));
- }
+ public ClusterId getClusterId() {
+ return new ClusterId(clusterSpecId.value());
+ }
+ public ServiceType getServiceType() {
+ return serviceType;
+ }
+
+ public ApplicationInstanceId getApplicationInstanceId() {
+ return new ApplicationInstanceId(applicationId.application().value());
+ }
+
+ public TenantId getTenantId() {
+ return new TenantId(applicationId.tenant().value());
+ }
+
+ public ApplicationInfo makeApplicationInfo(List<HostName> hostnames) {
+ List<HostInfo> hostInfos = hostnames.stream().map(this::makeHostInfo).collect(Collectors.toList());
return new ApplicationInfo(applicationId, 0, new HostsModel(hostInfos));
}
- private static HostInfo makeHostInfo(HostName hostname, int port, int configIndex, ServiceType serviceType, ClusterSpec.Id clusterId) {
- PortInfo portInfo = new PortInfo(port, ApplicationHealthMonitor.PORT_TAGS_HEALTH);
+ private HostInfo makeHostInfo(HostName hostname) {
+ PortInfo portInfo = new PortInfo(healthPort, ApplicationHealthMonitor.PORT_TAGS_HEALTH);
Map<String, String> properties = new HashMap<>();
- properties.put(ModelGenerator.CLUSTER_ID_PROPERTY_NAME, clusterId.value());
+ properties.put(ModelGenerator.CLUSTER_ID_PROPERTY_NAME, getClusterId().s());
ServiceInfo serviceInfo = new ServiceInfo(
// service name == service type for the first service of each type on each host
@@ -103,14 +118,24 @@ public abstract class InfraApplication implements InfraApplicationApi {
serviceType.s(),
Collections.singletonList(portInfo),
properties,
- configIdFrom(configIndex).s(),
+ configIdFor(hostname).s(),
hostname.value());
return new HostInfo(hostname.value(), Collections.singletonList(serviceInfo));
}
- private static ConfigId configIdFrom(int index) {
- return new ConfigId(CONFIG_ID_PREFIX + index);
+ public ConfigId configIdFor(HostName hostname) {
+ // Not necessarily unique, but service monitor doesn't require it to be unique.
+ return new ConfigId(String.format("%s/%s", clusterSpecId.value(), prefixTo(hostname.value(), '.')));
+ }
+
+ private static String prefixTo(String string, char sentinel) {
+ int offset = string.indexOf(sentinel);
+ if (offset == -1) {
+ return string;
+ } else {
+ return string.substring(0, offset);
+ }
}
@Override
@@ -118,16 +143,17 @@ public abstract class InfraApplication implements InfraApplicationApi {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InfraApplication that = (InfraApplication) o;
- return Objects.equals(applicationId, that.applicationId) &&
+ return healthPort == that.healthPort &&
+ Objects.equals(applicationId, that.applicationId) &&
Objects.equals(capacity, that.capacity) &&
- clusterType == that.clusterType &&
- Objects.equals(clusterId, that.clusterId) &&
+ clusterSpecType == that.clusterSpecType &&
+ Objects.equals(clusterSpecId, that.clusterSpecId) &&
Objects.equals(serviceType, that.serviceType);
}
@Override
public int hashCode() {
- return Objects.hash(applicationId, capacity, clusterType, clusterId, serviceType);
+ return Objects.hash(applicationId, capacity, clusterSpecType, clusterSpecId, serviceType, healthPort);
}
@Override
@@ -135,9 +161,10 @@ public abstract class InfraApplication implements InfraApplicationApi {
return "InfraApplication{" +
"applicationId=" + applicationId +
", capacity=" + capacity +
- ", clusterType=" + clusterType +
- ", clusterId=" + clusterId +
+ ", clusterSpecType=" + clusterSpecType +
+ ", clusterSpecId=" + clusterSpecId +
", serviceType=" + serviceType +
+ ", healthPort=" + healthPort +
'}';
}
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ProxyHostApplication.java b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ProxyHostApplication.java
index b6b0964a82d..24230609e60 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ProxyHostApplication.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/ProxyHostApplication.java
@@ -1,13 +1,10 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.service.duper;
-import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.NodeType;
-import com.yahoo.vespa.applicationmodel.ServiceType;
-public class ProxyHostApplication extends InfraApplication {
+public class ProxyHostApplication extends HostAdminApplication {
public ProxyHostApplication() {
- super("proxy-host", NodeType.proxyhost, ClusterSpec.Type.container,
- ClusterSpec.Id.from("proxy-host"), ServiceType.HOST_ADMIN);
+ super("proxy-host", NodeType.proxyhost);
}
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/health/ApplicationHealthMonitorFactory.java b/service-monitor/src/main/java/com/yahoo/vespa/service/health/ApplicationHealthMonitorFactory.java
new file mode 100644
index 00000000000..43be236268c
--- /dev/null
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/health/ApplicationHealthMonitorFactory.java
@@ -0,0 +1,12 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.service.health;
+
+import com.yahoo.config.model.api.ApplicationInfo;
+
+/**
+ * @author hakonhall
+ */
+@FunctionalInterface
+interface ApplicationHealthMonitorFactory {
+ ApplicationHealthMonitor create(ApplicationInfo applicationInfo);
+}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/health/HealthMonitorManager.java b/service-monitor/src/main/java/com/yahoo/vespa/service/health/HealthMonitorManager.java
index daf9fd1931b..e9a5ec314f6 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/health/HealthMonitorManager.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/health/HealthMonitorManager.java
@@ -8,7 +8,9 @@ import com.yahoo.vespa.applicationmodel.ClusterId;
import com.yahoo.vespa.applicationmodel.ConfigId;
import com.yahoo.vespa.applicationmodel.ServiceStatus;
import com.yahoo.vespa.applicationmodel.ServiceType;
-import com.yahoo.vespa.service.duper.ConfigServerApplication;
+import com.yahoo.vespa.flags.FeatureFlag;
+import com.yahoo.vespa.flags.FileFlagSource;
+import com.yahoo.vespa.service.duper.DuperModelManager;
import com.yahoo.vespa.service.duper.ZoneApplication;
import com.yahoo.vespa.service.manager.MonitorManager;
@@ -21,14 +23,29 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public class HealthMonitorManager implements MonitorManager {
private final ConcurrentHashMap<ApplicationId, ApplicationHealthMonitor> healthMonitors = new ConcurrentHashMap<>();
+ private final DuperModelManager duperModel;
+ private final ApplicationHealthMonitorFactory applicationHealthMonitorFactory;
+ private final FeatureFlag monitorInfra;
@Inject
- public HealthMonitorManager() { }
+ public HealthMonitorManager(DuperModelManager duperModel, FileFlagSource flagSource) {
+ this(duperModel,
+ new FeatureFlag("healthmonitor-monitorinfra", true, flagSource),
+ ApplicationHealthMonitor::startMonitoring);
+ }
+
+ HealthMonitorManager(DuperModelManager duperModel,
+ FeatureFlag monitorInfra,
+ ApplicationHealthMonitorFactory applicationHealthMonitorFactory) {
+ this.duperModel = duperModel;
+ this.applicationHealthMonitorFactory = applicationHealthMonitorFactory;
+ this.monitorInfra = monitorInfra;
+ }
@Override
public void applicationActivated(ApplicationInfo application) {
if (wouldMonitor(application.getApplicationId())) {
- ApplicationHealthMonitor monitor = ApplicationHealthMonitor.startMonitoring(application);
+ ApplicationHealthMonitor monitor = applicationHealthMonitorFactory.create(application);
healthMonitors.put(application.getApplicationId(), monitor);
}
}
@@ -62,7 +79,11 @@ public class HealthMonitorManager implements MonitorManager {
@Override
public boolean wouldMonitor(ApplicationId id) {
- if (id.equals(ConfigServerApplication.CONFIG_SERVER_APPLICATION.getApplicationId())) {
+ if (duperModel.isSupportedInfraApplication(id) && monitorInfra.value()) {
+ return true;
+ }
+
+ if (id.equals(duperModel.getConfigServerApplication().getApplicationId())) {
return true;
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/model/ApplicationInstanceGenerator.java b/service-monitor/src/main/java/com/yahoo/vespa/service/model/ApplicationInstanceGenerator.java
index 9bcb3f07cf6..f15101d4439 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/model/ApplicationInstanceGenerator.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/model/ApplicationInstanceGenerator.java
@@ -17,6 +17,7 @@ import com.yahoo.vespa.applicationmodel.ServiceInstance;
import com.yahoo.vespa.applicationmodel.ServiceStatus;
import com.yahoo.vespa.applicationmodel.ServiceType;
import com.yahoo.vespa.applicationmodel.TenantId;
+import com.yahoo.vespa.service.duper.ConfigServerApplication;
import com.yahoo.vespa.service.monitor.ServiceStatusProvider;
import java.util.HashMap;
@@ -25,8 +26,6 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
-import static com.yahoo.vespa.service.duper.ConfigServerApplication.CONFIG_SERVER_APPLICATION;
-
/**
* Class to generate an ApplicationInstance given service status for a standard (deployed) application.
*
@@ -37,10 +36,15 @@ public class ApplicationInstanceGenerator {
private final ApplicationInfo applicationInfo;
private final Zone zone;
+ private ApplicationId configServerApplicationId;
public ApplicationInstanceGenerator(ApplicationInfo applicationInfo, Zone zone) {
this.applicationInfo = applicationInfo;
this.zone = zone;
+
+ // This is cheating a bit, but we don't expect DuperModel's config server application ID to be different.
+ // We do this to avoid passing through the ID through multiple levels.
+ this.configServerApplicationId = new ConfigServerApplication().getApplicationId();
}
public ApplicationInstance makeApplicationInstance(ServiceStatusProvider serviceStatusProvider) {
@@ -105,7 +109,7 @@ public class ApplicationInstanceGenerator {
}
private ApplicationInstanceId toApplicationInstanceId(ApplicationInfo applicationInfo, Zone zone) {
- if (applicationInfo.getApplicationId().equals(CONFIG_SERVER_APPLICATION.getApplicationId())) {
+ if (applicationInfo.getApplicationId().equals(configServerApplicationId)) {
// Removing this historical discrepancy would break orchestration during rollout.
// An alternative may be to use a feature flag and flip it between releases,
// once that's available.
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceId.java b/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceId.java
index b695b0b19f8..c0bcff7284c 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceId.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceId.java
@@ -22,9 +22,9 @@ public class ServiceId {
private final ConfigId configId;
public ServiceId(ApplicationId applicationId,
- ClusterId clusterId,
- ServiceType serviceType,
- ConfigId configId) {
+ ClusterId clusterId,
+ ServiceType serviceType,
+ ConfigId configId) {
this.applicationId = applicationId;
this.clusterId = clusterId;
this.serviceType = serviceType;
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/slobrok/SlobrokMonitorManagerImpl.java b/service-monitor/src/main/java/com/yahoo/vespa/service/slobrok/SlobrokMonitorManagerImpl.java
index 39da5040ca0..13f24bc3694 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/slobrok/SlobrokMonitorManagerImpl.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/slobrok/SlobrokMonitorManagerImpl.java
@@ -28,14 +28,16 @@ public class SlobrokMonitorManagerImpl implements SlobrokApi, MonitorManager {
private final Object monitor = new Object();
private final HashMap<ApplicationId, SlobrokMonitor> slobrokMonitors = new HashMap<>();
+ private final DuperModelManager duperModel;
@Inject
- public SlobrokMonitorManagerImpl() {
- this(SlobrokMonitor::new);
+ public SlobrokMonitorManagerImpl(DuperModelManager duperModel) {
+ this(SlobrokMonitor::new, duperModel);
}
- SlobrokMonitorManagerImpl(Supplier<SlobrokMonitor> slobrokMonitorFactory) {
+ SlobrokMonitorManagerImpl(Supplier<SlobrokMonitor> slobrokMonitorFactory, DuperModelManager duperModel) {
this.slobrokMonitorFactory = slobrokMonitorFactory;
+ this.duperModel = duperModel;
}
@Override
@@ -108,7 +110,7 @@ public class SlobrokMonitorManagerImpl implements SlobrokApi, MonitorManager {
@Override
public boolean wouldMonitor(ApplicationId applicationId) {
- if (DuperModelManager.isInfrastructureApplication(applicationId)) {
+ if (duperModel.isSupportedInfraApplication(applicationId)) {
return false;
}
diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/health/ApplicationHealthMonitorTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/health/ApplicationHealthMonitorTest.java
index 078f34b84d1..0dfca12099e 100644
--- a/service-monitor/src/test/java/com/yahoo/vespa/service/health/ApplicationHealthMonitorTest.java
+++ b/service-monitor/src/test/java/com/yahoo/vespa/service/health/ApplicationHealthMonitorTest.java
@@ -1,6 +1,7 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.service.health;
+import com.yahoo.config.provision.HostName;
import com.yahoo.vespa.applicationmodel.ServiceStatus;
import com.yahoo.vespa.service.duper.ConfigServerApplication;
import com.yahoo.vespa.service.monitor.ConfigserverUtil;
@@ -16,6 +17,8 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class ApplicationHealthMonitorTest {
+ private final ConfigServerApplication configServerApplication = new ConfigServerApplication();
+
@Test
public void sanityCheck() {
MonitorFactory monitorFactory = new MonitorFactory();
@@ -36,28 +39,17 @@ public class ApplicationHealthMonitorTest {
ConfigserverUtil.makeExampleConfigServer(),
monitorFactory);
- ConfigServerApplication configServerApplication = new ConfigServerApplication();
-
- ServiceStatus status1 = applicationMonitor.getStatus(
- ConfigServerApplication.CONFIG_SERVER_APPLICATION.getApplicationId(),
- ConfigServerApplication.CLUSTER_ID,
- ConfigServerApplication.SERVICE_TYPE,
- ConfigServerApplication.configIdFrom(0));
- assertEquals(ServiceStatus.UP, status1);
-
- ServiceStatus status2 = applicationMonitor.getStatus(
- ConfigServerApplication.CONFIG_SERVER_APPLICATION.getApplicationId(),
- ConfigServerApplication.CLUSTER_ID,
- ConfigServerApplication.SERVICE_TYPE,
- ConfigServerApplication.configIdFrom(1));
- assertEquals(ServiceStatus.DOWN, status2);
-
- ServiceStatus status3 = applicationMonitor.getStatus(
- ConfigServerApplication.CONFIG_SERVER_APPLICATION.getApplicationId(),
- ConfigServerApplication.CLUSTER_ID,
- ConfigServerApplication.SERVICE_TYPE,
- ConfigServerApplication.configIdFrom(2));
- assertEquals(ServiceStatus.NOT_CHECKED, status3);
+ assertEquals(ServiceStatus.UP, getStatus(applicationMonitor, "cfg1"));
+ assertEquals(ServiceStatus.DOWN, getStatus(applicationMonitor, "cfg2"));
+ assertEquals(ServiceStatus.NOT_CHECKED, getStatus(applicationMonitor, "cfg3"));
+ }
+
+ private ServiceStatus getStatus(ApplicationHealthMonitor monitor, String hostname) {
+ return monitor.getStatus(
+ configServerApplication.getApplicationId(),
+ configServerApplication.getClusterId(),
+ configServerApplication.getServiceType(),
+ configServerApplication.configIdFor(HostName.from(hostname)));
}
private static class MonitorFactory implements Function<HealthEndpoint, HealthMonitor> {
diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/health/HealthMonitorManagerTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/health/HealthMonitorManagerTest.java
index 0b9d7e123b5..f420f5c1284 100644
--- a/service-monitor/src/test/java/com/yahoo/vespa/service/health/HealthMonitorManagerTest.java
+++ b/service-monitor/src/test/java/com/yahoo/vespa/service/health/HealthMonitorManagerTest.java
@@ -2,20 +2,51 @@
package com.yahoo.vespa.service.health;
import com.yahoo.config.model.api.ApplicationInfo;
+import com.yahoo.config.provision.HostName;
import com.yahoo.vespa.applicationmodel.ClusterId;
import com.yahoo.vespa.applicationmodel.ConfigId;
import com.yahoo.vespa.applicationmodel.ServiceStatus;
import com.yahoo.vespa.applicationmodel.ServiceType;
+import com.yahoo.vespa.flags.FeatureFlag;
+import com.yahoo.vespa.service.duper.ConfigServerApplication;
+import com.yahoo.vespa.service.duper.ControllerHostApplication;
+import com.yahoo.vespa.service.duper.DuperModelManager;
+import com.yahoo.vespa.service.duper.InfraApplication;
+import com.yahoo.vespa.service.duper.ProxyHostApplication;
import com.yahoo.vespa.service.duper.ZoneApplication;
import com.yahoo.vespa.service.monitor.ConfigserverUtil;
+import org.junit.Before;
import org.junit.Test;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
public class HealthMonitorManagerTest {
+ private final ConfigServerApplication configServerApplication = new ConfigServerApplication();
+ private final DuperModelManager duperModel = mock(DuperModelManager.class);
+ private final FeatureFlag monitorInfra = mock(FeatureFlag.class);
+ private final ApplicationHealthMonitor monitor = mock(ApplicationHealthMonitor.class);
+ private final ApplicationHealthMonitorFactory monitorFactory = mock(ApplicationHealthMonitorFactory.class);
+ private final HealthMonitorManager manager = new HealthMonitorManager(duperModel, monitorInfra, monitorFactory);
+
+ @Before
+ public void setUp() {
+ when(duperModel.getConfigServerApplication()).thenReturn(configServerApplication);
+ when(monitorFactory.create(any())).thenReturn(monitor);
+ }
+
@Test
public void addRemove() {
- HealthMonitorManager manager = new HealthMonitorManager();
+ when(monitorInfra.value()).thenReturn(false);
ApplicationInfo applicationInfo = ConfigserverUtil.makeExampleConfigServer();
manager.applicationActivated(applicationInfo);
manager.applicationRemoved(applicationInfo.getApplicationId());
@@ -23,7 +54,7 @@ public class HealthMonitorManagerTest {
@Test
public void withHostAdmin() {
- HealthMonitorManager manager = new HealthMonitorManager();
+ when(monitorInfra.value()).thenReturn(false);
ServiceStatus status = manager.getStatus(
ZoneApplication.ZONE_APPLICATION_ID,
ClusterId.NODE_ADMIN,
@@ -31,4 +62,56 @@ public class HealthMonitorManagerTest {
new ConfigId("config-id-1"));
assertEquals(ServiceStatus.UP, status);
}
+
+ @Test
+ public void infrastructureApplication() {
+ when(monitorInfra.value()).thenReturn(false);
+
+ ProxyHostApplication proxyHostApplication = new ProxyHostApplication();
+ when(duperModel.isSupportedInfraApplication(proxyHostApplication.getApplicationId())).thenReturn(true);
+ List<HostName> hostnames = Stream.of("proxyhost1", "proxyhost2").map(HostName::from).collect(Collectors.toList());
+ ApplicationInfo proxyHostApplicationInfo = proxyHostApplication.makeApplicationInfo(hostnames);
+
+ manager.applicationActivated(proxyHostApplicationInfo);
+ verify(monitorFactory, never()).create(proxyHostApplicationInfo);
+
+ assertStatus(ServiceStatus.NOT_CHECKED, 0, proxyHostApplication, "proxyhost1");
+ }
+
+ @Test
+ public void infrastructureApplicationWithMonitoring() {
+ when(monitorInfra.value()).thenReturn(true);
+
+ ProxyHostApplication proxyHostApplication = new ProxyHostApplication();
+ when(duperModel.isSupportedInfraApplication(proxyHostApplication.getApplicationId())).thenReturn(true);
+ List<HostName> hostnames = Stream.of("proxyhost1", "proxyhost2").map(HostName::from).collect(Collectors.toList());
+ ApplicationInfo proxyHostApplicationInfo = proxyHostApplication.makeApplicationInfo(hostnames);
+
+ manager.applicationActivated(proxyHostApplicationInfo);
+ verify(monitorFactory, times(1)).create(proxyHostApplicationInfo);
+
+ when(monitor.getStatus(any(), any(), any(), any())).thenReturn(ServiceStatus.UP);
+ assertStatus(ServiceStatus.UP, 1, proxyHostApplication, "proxyhost1");
+
+ ControllerHostApplication controllerHostApplication = new ControllerHostApplication();
+ when(duperModel.isSupportedInfraApplication(controllerHostApplication.getApplicationId())).thenReturn(true);
+ assertStatus(ServiceStatus.NOT_CHECKED, 0, controllerHostApplication, "controllerhost1");
+ }
+
+ private void assertStatus(ServiceStatus expected, int verifyTimes, InfraApplication infraApplication, String hostname) {
+ ServiceStatus actual = manager.getStatus(
+ infraApplication.getApplicationId(),
+ infraApplication.getClusterId(),
+ infraApplication.getServiceType(),
+ infraApplication.configIdFor(HostName.from(hostname)));
+
+ assertEquals(expected, actual);
+
+ verify(monitor, times(verifyTimes)).getStatus(
+ infraApplication.getApplicationId(),
+ infraApplication.getClusterId(),
+ infraApplication.getServiceType(),
+ infraApplication.configIdFor(HostName.from(hostname)));
+
+ }
} \ No newline at end of file
diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/model/ApplicationInstanceGeneratorTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/model/ApplicationInstanceGeneratorTest.java
index a59206d14e2..bf3f7017b01 100644
--- a/service-monitor/src/test/java/com/yahoo/vespa/service/model/ApplicationInstanceGeneratorTest.java
+++ b/service-monitor/src/test/java/com/yahoo/vespa/service/model/ApplicationInstanceGeneratorTest.java
@@ -6,16 +6,15 @@ import com.yahoo.config.model.api.ApplicationInfo;
import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.applicationmodel.ApplicationInstance;
import com.yahoo.vespa.applicationmodel.ServiceStatus;
-import com.yahoo.vespa.service.monitor.ServiceStatusProvider;
-import com.yahoo.vespa.service.monitor.ConfigserverUtil;
import com.yahoo.vespa.service.duper.ConfigServerApplication;
+import com.yahoo.vespa.service.monitor.ConfigserverUtil;
+import com.yahoo.vespa.service.monitor.ServiceStatusProvider;
import org.junit.Test;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import static com.yahoo.vespa.service.duper.ConfigServerApplication.CONFIG_SERVER_APPLICATION;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
@@ -30,6 +29,7 @@ public class ApplicationInstanceGeneratorTest {
configServer1,
configServer2,
configServer3).collect(Collectors.toList());
+ private static final ConfigServerApplication configServerApplication = new ConfigServerApplication();
private final ServiceStatusProvider statusProvider = mock(ServiceStatusProvider.class);
@@ -43,24 +43,24 @@ public class ApplicationInstanceGeneratorTest {
configServer2,
configServer3);
Zone zone = mock(Zone.class);
- ApplicationInfo configServer = CONFIG_SERVER_APPLICATION.makeApplicationInfoFromConfig(config);
+ ApplicationInfo configServer = configServerApplication.makeApplicationInfoFromConfig(config);
ApplicationInstance applicationInstance = new ApplicationInstanceGenerator(configServer, zone)
.makeApplicationInstance(statusProvider);
assertEquals(
- ConfigServerApplication.APPLICATION_INSTANCE_ID,
+ configServerApplication.getApplicationInstanceId(),
applicationInstance.applicationInstanceId());
assertEquals(
- ConfigServerApplication.TENANT_ID,
+ configServerApplication.getTenantId(),
applicationInstance.tenantId());
assertEquals(
- ConfigServerApplication.TENANT_ID.toString() +
- ":" + ConfigServerApplication.APPLICATION_INSTANCE_ID,
+ configServerApplication.getTenantId().toString() +
+ ":" + configServerApplication.getApplicationInstanceId(),
applicationInstance.reference().toString());
assertEquals(
- ConfigServerApplication.CLUSTER_ID,
+ configServerApplication.getClusterId(),
applicationInstance.serviceClusters().iterator().next().clusterId());
assertEquals(
diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/model/ModelGeneratorTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/model/ModelGeneratorTest.java
index bcc136f80e3..30a49835f03 100644
--- a/service-monitor/src/test/java/com/yahoo/vespa/service/model/ModelGeneratorTest.java
+++ b/service-monitor/src/test/java/com/yahoo/vespa/service/model/ModelGeneratorTest.java
@@ -33,6 +33,7 @@ public class ModelGeneratorTest {
private final String REGION = "us-west-1";
private final String HOSTNAME = "hostname";
private final int PORT = 2;
+ private final ConfigServerApplication configServerApplication = new ConfigServerApplication();
@Test
public void toApplicationModel() throws Exception {
@@ -63,8 +64,7 @@ public class ModelGeneratorTest {
ApplicationInstance applicationInstance1 = iterator.next().getValue();
ApplicationInstance applicationInstance2 = iterator.next().getValue();
- if (applicationInstance1.applicationInstanceId().equals(
- ConfigServerApplication.APPLICATION_INSTANCE_ID)) {
+ if (applicationInstance1.applicationInstanceId().equals(configServerApplication.getApplicationInstanceId())) {
verifyConfigServerApplication(applicationInstance1);
verifyOtherApplication(applicationInstance2);
} else {
@@ -108,7 +108,6 @@ public class ModelGeneratorTest {
private void verifyConfigServerApplication(
ApplicationInstance applicationInstance) {
- assertEquals(ConfigServerApplication.APPLICATION_INSTANCE_ID,
- applicationInstance.applicationInstanceId());
+ assertEquals(configServerApplication.getApplicationInstanceId(), applicationInstance.applicationInstanceId());
}
}
diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/ConfigserverUtil.java b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/ConfigserverUtil.java
index c334fad2334..7f817a0f1e6 100644
--- a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/ConfigserverUtil.java
+++ b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/ConfigserverUtil.java
@@ -33,7 +33,7 @@ public class ConfigserverUtil {
String configServerHostname1,
String configServerHostname2,
String configServerHostname3) {
- return ConfigServerApplication.CONFIG_SERVER_APPLICATION.makeApplicationInfoFromConfig(create(
+ return new ConfigServerApplication().makeApplicationInfoFromConfig(create(
true,
true,
configServerHostname1,
diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/slobrok/SlobrokMonitorManagerImplTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/slobrok/SlobrokMonitorManagerImplTest.java
index c3ee1d078bf..bc6bff6fba8 100644
--- a/service-monitor/src/test/java/com/yahoo/vespa/service/slobrok/SlobrokMonitorManagerImplTest.java
+++ b/service-monitor/src/test/java/com/yahoo/vespa/service/slobrok/SlobrokMonitorManagerImplTest.java
@@ -7,6 +7,8 @@ import com.yahoo.vespa.applicationmodel.ClusterId;
import com.yahoo.vespa.applicationmodel.ConfigId;
import com.yahoo.vespa.applicationmodel.ServiceStatus;
import com.yahoo.vespa.applicationmodel.ServiceType;
+import com.yahoo.vespa.service.duper.ConfigServerApplication;
+import com.yahoo.vespa.service.duper.DuperModelManager;
import org.junit.Before;
import org.junit.Test;
@@ -25,8 +27,10 @@ public class SlobrokMonitorManagerImplTest {
@SuppressWarnings("unchecked")
private final Supplier<SlobrokMonitor> slobrokMonitorFactory = mock(Supplier.class);
+ private final ConfigServerApplication configServerApplication = new ConfigServerApplication();
+ private final DuperModelManager duperModelManager = mock(DuperModelManager.class);
private final SlobrokMonitorManagerImpl slobrokMonitorManager =
- new SlobrokMonitorManagerImpl(slobrokMonitorFactory);
+ new SlobrokMonitorManagerImpl(slobrokMonitorFactory, duperModelManager);
private final SlobrokMonitor slobrokMonitor = mock(SlobrokMonitor.class);
private final ApplicationId applicationId = ApplicationId.from("tenant", "app", "instance");
private final ApplicationInfo application = mock(ApplicationInfo.class);
@@ -34,6 +38,7 @@ public class SlobrokMonitorManagerImplTest {
@Before
public void setup() {
+ when(duperModelManager.getConfigServerApplication()).thenReturn(configServerApplication);
when(slobrokMonitorFactory.get()).thenReturn(slobrokMonitor);
when(application.getApplicationId()).thenReturn(applicationId);
}
diff --git a/staging_vespalib/CMakeLists.txt b/staging_vespalib/CMakeLists.txt
index 095caf17a56..b95b4fda01f 100644
--- a/staging_vespalib/CMakeLists.txt
+++ b/staging_vespalib/CMakeLists.txt
@@ -9,6 +9,7 @@ vespa_define_module(
TESTS
src/tests/array
+ src/tests/assert
src/tests/benchmark
src/tests/bits
src/tests/clock
diff --git a/staging_vespalib/src/tests/assert/.gitignore b/staging_vespalib/src/tests/assert/.gitignore
new file mode 100644
index 00000000000..d0e23fcd846
--- /dev/null
+++ b/staging_vespalib/src/tests/assert/.gitignore
@@ -0,0 +1 @@
+staging_vespalib_asserter_app
diff --git a/staging_vespalib/src/tests/assert/CMakeLists.txt b/staging_vespalib/src/tests/assert/CMakeLists.txt
new file mode 100644
index 00000000000..7403a488645
--- /dev/null
+++ b/staging_vespalib/src/tests/assert/CMakeLists.txt
@@ -0,0 +1,16 @@
+# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+vespa_add_executable(staging_vespalib_assert_test_app TEST
+ SOURCES
+ assert_test.cpp
+ DEPENDS
+ staging_vespalib
+)
+vespa_add_test(NAME staging_vespalib_assert_test_app COMMAND staging_vespalib_assert_test_app)
+
+vespa_add_executable(staging_vespalib_asserter_app TEST
+ SOURCES
+ asserter.cpp
+ DEPENDS
+ staging_vespalib
+)
diff --git a/staging_vespalib/src/tests/assert/assert_test.cpp b/staging_vespalib/src/tests/assert/assert_test.cpp
new file mode 100644
index 00000000000..454c0957974
--- /dev/null
+++ b/staging_vespalib/src/tests/assert/assert_test.cpp
@@ -0,0 +1,39 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/util/slaveproc.h>
+#include <vespa/vespalib/util/stringfmt.h>
+#include <vespa/vespalib/util/assert.h>
+#include <vespa/vespalib/io/fileutil.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <vespa/defaults.h>
+
+using namespace vespalib;
+
+TEST("that it borks the first time.") {
+ std::string assertName;
+ const char * assertDir = "var/db/vespa/tmp";
+ vespalib::rmdir("var", true);
+ ASSERT_TRUE(vespalib::mkdir(assertDir, true));
+ {
+ SlaveProc proc("ulimit -c 0 && exec env VESPA_HOME=./ ./staging_vespalib_asserter_app myassert 10000");
+ proc.wait();
+ ASSERT_EQUAL(proc.getExitCode() & 0x7f, 6);
+ }
+ {
+ SlaveProc proc("ulimit -c 0 && exec env VESPA_HOME=./ ./staging_vespalib_asserter_app myassert 10000");
+ proc.readLine(assertName);
+ proc.wait();
+ ASSERT_EQUAL(proc.getExitCode() & 0x7f, 0);
+ }
+ ASSERT_EQUAL(0, unlink(assertName.c_str()));
+ {
+ SlaveProc proc("ulimit -c 0 && exec env VESPA_HOME=./ ./staging_vespalib_asserter_app myassert 10000");
+ proc.wait();
+ ASSERT_EQUAL(proc.getExitCode() & 0x7f, 6);
+ }
+ ASSERT_EQUAL(0, unlink(assertName.c_str()));
+ ASSERT_TRUE(vespalib::rmdir("var", true));
+}
+
+TEST_MAIN_WITH_PROCESS_PROXY() { TEST_RUN_ALL(); }
diff --git a/staging_vespalib/src/tests/assert/asserter.cpp b/staging_vespalib/src/tests/assert/asserter.cpp
new file mode 100644
index 00000000000..640464889c0
--- /dev/null
+++ b/staging_vespalib/src/tests/assert/asserter.cpp
@@ -0,0 +1,25 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/util/assert.h>
+#include <cassert>
+#include <cstdlib>
+#include <fstream>
+#include <string>
+
+int main(int argc, char *argv[]) {
+ assert(argc == 3);
+ const char * assertKey = argv[1];
+ size_t assertCount = strtoul(argv[2], nullptr, 0);
+ for (size_t i(0); i < assertCount; i++) {
+ ASSERT_ONCE_OR_LOG(true, assertKey, 100);
+ ASSERT_ONCE_OR_LOG(false, assertKey, 100);
+ }
+ std::string filename = vespalib::assert::getAssertLogFileName(assertKey);
+ std::ifstream is(filename.c_str());
+ assert(is);
+ std::string line;
+ std::getline(is, line);
+ printf("%s\n", filename.c_str());
+ assert(line.find(assertKey) != std::string::npos);
+ assert(assertCount == vespalib::assert::getNumAsserts(assertKey));
+ return 0;
+}
diff --git a/staging_vespalib/src/vespa/vespalib/util/CMakeLists.txt b/staging_vespalib/src/vespa/vespalib/util/CMakeLists.txt
index 20d47c90453..c104c047cd0 100644
--- a/staging_vespalib/src/vespa/vespalib/util/CMakeLists.txt
+++ b/staging_vespalib/src/vespa/vespalib/util/CMakeLists.txt
@@ -1,6 +1,7 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
vespa_add_library(staging_vespalib_vespalib_util OBJECT
SOURCES
+ assert.cpp
bits.cpp
clock.cpp
crc.cpp
diff --git a/staging_vespalib/src/vespa/vespalib/util/assert.cpp b/staging_vespalib/src/vespa/vespalib/util/assert.cpp
new file mode 100644
index 00000000000..0482d873f7f
--- /dev/null
+++ b/staging_vespalib/src/vespa/vespalib/util/assert.cpp
@@ -0,0 +1,77 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "assert.h"
+#include <vespa/defaults.h>
+#include <vespa/vespalib/util/backtrace.h>
+#include <vespa/vespalib/util/stringfmt.h>
+#include <vespa/vespalib/component/vtag.h>
+#include <fstream>
+#include <map>
+#include <mutex>
+#include <chrono>
+#include <iomanip>
+
+#include <vespa/log/log.h>
+LOG_SETUP(".vespa.assert");
+
+namespace vespalib::assert {
+
+namespace {
+
+std::mutex _G_lock;
+std::map<std::string, size_t> _G_assertMap;
+
+}
+
+size_t
+getNumAsserts(const char *key)
+{
+ std::lock_guard guard(_G_lock);
+ return _G_assertMap[key];
+}
+
+vespalib::string
+getAssertLogFileName(const char *key)
+{
+ vespalib::string relative = make_string("var/db/vespa/tmp/%s.%s.assert", key, Vtag::currentVersion.toString().c_str());
+ return vespa::Defaults::underVespaHome(relative.c_str());
+}
+
+void
+assertOnceOrLog(const char *expr, const char *key, size_t freq)
+{
+ size_t count(0);
+ {
+ std::lock_guard guard(_G_lock);
+ count = _G_assertMap[key]++;
+ }
+ if (count) {
+ if ((count % freq) == 0) {
+ LOG(error, "assert(%s) named '%s' has failed %zu times. Stacktrace = %s",
+ expr, key, count+1, vespalib::getStackTrace(0).c_str());
+ }
+ } else {
+ std::string rememberAssert = getAssertLogFileName(key);
+ std::ifstream prevAssertFile(rememberAssert.c_str());
+ if (prevAssertFile) {
+ if ((count % freq) == 0) {
+ LOG(error, "assert(%s) named '%s' has failed %zu times. Stacktrace = %s",
+ expr, key, count + 1, vespalib::getStackTrace(0).c_str());
+ }
+ } else {
+ {
+ LOG(error, "assert(%s) named '%s' failed first time. Stacktrace = %s",
+ expr, key, vespalib::getStackTrace(0).c_str());
+ std::ofstream assertStream(rememberAssert.c_str());
+ std::chrono::time_point now = std::chrono::system_clock::now();
+ std::time_t now_c = std::chrono::system_clock::to_time_t(now);
+ assertStream << std::put_time(std::gmtime(&now_c), "%F %T") << " assert(" << expr
+ << ") named " << key << " failed" << std::endl;
+ assertStream.close();
+ }
+ abort();
+ }
+ }
+}
+
+}
diff --git a/staging_vespalib/src/vespa/vespalib/util/assert.h b/staging_vespalib/src/vespa/vespalib/util/assert.h
new file mode 100644
index 00000000000..698fa6774c1
--- /dev/null
+++ b/staging_vespalib/src/vespa/vespalib/util/assert.h
@@ -0,0 +1,38 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/vespalib/stllike/string.h>
+
+namespace vespalib::assert {
+
+/**
+ * How many times has asserts against this key failed.
+ * @param key
+ * @return
+ */
+size_t getNumAsserts(const char *key);
+
+/**
+ * Get the filename that will be used for remembering asserts.
+ * @param key
+ * @return
+ */
+vespalib::string getAssertLogFileName(const char *key);
+
+/**
+ * If there is no record on file that this assert has failed, it will be recorded and aborted.
+ * However if there is a record of it, it will merely be logged the first and then every #freq time.
+ * @param expr that failed the assert
+ * @param key unique name of assert
+ * @param logFreq how often will a failing assert be logged.
+ */
+void assertOnceOrLog(const char *expr, const char *key, size_t logFreq);
+
+}
+
+#define ASSERT_ONCE_OR_LOG(expr, key, freq) { \
+ if ( ! (expr) ) { \
+ vespalib::assert::assertOnceOrLog(#expr, key, freq); \
+ } \
+}
diff --git a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java
index 65e7191e164..2b12c7cd78c 100644
--- a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java
+++ b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java
@@ -50,7 +50,7 @@ public class OperationHandlerImpl implements OperationHandler {
}
public interface BucketSpaceResolver {
- Optional<String> clusterBucketSpaceFromDocumentType(String clusterConfigId, String docType);
+ Optional<String> clusterBucketSpaceFromDocumentType(String clusterId, String docType);
}
public static class BucketSpaceRoute {
@@ -91,20 +91,6 @@ public class OperationHandlerImpl implements OperationHandler {
private final ConcurrentResourcePool<SyncSession> syncSessions;
- private static ClusterEnumerator defaultClusterEnumerator() {
- return () -> new ClusterList("client").getStorageClusters();
- }
-
- private static BucketSpaceResolver defaultBucketResolver() {
- return (clusterConfigId, docType) -> Optional.ofNullable(BucketSpaceEnumerator
- .fromConfig(clusterConfigId).getDoctypeToSpaceMapping()
- .get(docType));
- }
-
- public OperationHandlerImpl(DocumentAccess documentAccess, ClusterEnumerator clusterEnumerator, MetricReceiver metricReceiver) {
- this(documentAccess, clusterEnumerator, defaultBucketResolver(), metricReceiver);
- }
-
public OperationHandlerImpl(DocumentAccess documentAccess, ClusterEnumerator clusterEnumerator,
BucketSpaceResolver bucketSpaceResolver, MetricReceiver metricReceiver) {
this.documentAccess = documentAccess;
@@ -335,7 +321,7 @@ public class OperationHandlerImpl implements OperationHandler {
String targetBucketSpace;
if (!restUri.isRootOnly()) {
String docType = restUri.getDocumentType();
- Optional<String> resolvedSpace = bucketSpaceResolver.clusterBucketSpaceFromDocumentType(clusterDef.getConfigId(), docType);
+ Optional<String> resolvedSpace = bucketSpaceResolver.clusterBucketSpaceFromDocumentType(clusterDef.getName(), docType);
if (!resolvedSpace.isPresent()) {
throw new RestApiException(Response.createErrorResponse(400, String.format(
"Document type '%s' in cluster '%s' is not mapped to a known bucket space", docType, clusterDef.getName()),
diff --git a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/RestApi.java b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/RestApi.java
index f088db31c23..60f9101cdc8 100644
--- a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/RestApi.java
+++ b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/RestApi.java
@@ -30,6 +30,7 @@ import com.yahoo.documentapi.messagebus.loadtypes.LoadTypeSet;
import com.yahoo.metrics.simple.MetricReceiver;
import com.yahoo.text.Text;
import com.yahoo.vespa.config.content.LoadTypeConfig;
+import com.yahoo.vespa.config.content.AllClustersBucketSpacesConfig;
import com.yahoo.vespaclient.ClusterDef;
import com.yahoo.vespaclient.ClusterList;
import com.yahoo.vespaxmlparser.VespaXMLFeedReader;
@@ -75,6 +76,7 @@ public class RestApi extends LoggingRequestHandler {
@Inject
public RestApi(LoggingRequestHandler.Context parentCtx, DocumentmanagerConfig documentManagerConfig,
LoadTypeConfig loadTypeConfig, ThreadpoolConfig threadpoolConfig,
+ AllClustersBucketSpacesConfig bucketSpacesConfig,
ClusterListConfig clusterListConfig, MetricReceiver metricReceiver)
{
super(parentCtx);
@@ -83,6 +85,7 @@ public class RestApi extends LoggingRequestHandler {
this.operationHandler = new OperationHandlerImpl(
new MessageBusDocumentAccess(params),
fixedClusterEnumeratorFromConfig(clusterListConfig),
+ fixedBucketSpaceResolverFromConfig(bucketSpacesConfig),
metricReceiver);
this.singleDocumentParser = new SingleDocumentParser(new DocumentTypeManager(documentManagerConfig));
// 40% of the threads can be blocked before we deny requests.
@@ -120,6 +123,14 @@ public class RestApi extends LoggingRequestHandler {
return () -> clusters;
}
+ private static OperationHandlerImpl.BucketSpaceResolver fixedBucketSpaceResolverFromConfig(
+ AllClustersBucketSpacesConfig bucketSpacesConfig) {
+ return (clusterId, docType) ->
+ Optional.ofNullable(bucketSpacesConfig.cluster(clusterId))
+ .map(cluster -> cluster.documentType(docType))
+ .map(type -> type.bucketSpace());
+ }
+
private static Optional<String> requestProperty(String parameter, HttpRequest request) {
final String property = request.getProperty(parameter);
if (property != null && ! property.isEmpty()) {
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/OperationHandlerImplTest.java b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/OperationHandlerImplTest.java
index 4c04070cec4..30f039c31fb 100644
--- a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/OperationHandlerImplTest.java
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/OperationHandlerImplTest.java
@@ -132,7 +132,7 @@ public class OperationHandlerImplTest {
});
when(documentAccess.createSyncSession(any(SyncParameters.class))).thenReturn(mockSyncSession);
OperationHandlerImpl.ClusterEnumerator clusterEnumerator = () -> Arrays.asList(new ClusterDef("foo", "configId"));
- OperationHandlerImpl.BucketSpaceResolver bucketSpaceResolver = (configId, docType) -> Optional.ofNullable(bucketSpaces.get(docType));
+ OperationHandlerImpl.BucketSpaceResolver bucketSpaceResolver = (clusterId, docType) -> Optional.ofNullable(bucketSpaces.get(docType));
return new OperationHandlerImpl(documentAccess, clusterEnumerator, bucketSpaceResolver, MetricReceiver.nullImplementation);
}
}
diff --git a/vespalib/src/vespa/vespalib/component/version.cpp b/vespalib/src/vespa/vespalib/component/version.cpp
index fc7ee703e7f..b0f14bc18f3 100644
--- a/vespalib/src/vespa/vespalib/component/version.cpp
+++ b/vespalib/src/vespa/vespalib/component/version.cpp
@@ -19,7 +19,7 @@ Version::Version(int major, int minor, int micro, const string & qualifier)
Version::Version(const Version &) = default;
Version & Version::operator = (const Version &) = default;
-Version::~Version() { }
+Version::~Version() = default;
void
Version::initialize()
@@ -151,5 +151,4 @@ Version::compareTo(const Version& other) const
return getQualifier().compare(other.getQualifier());
}
-
} // namespace vespalib
diff --git a/vespalib/src/vespa/vespalib/component/versionspecification.cpp b/vespalib/src/vespa/vespalib/component/versionspecification.cpp
index 3351219804f..84cf446e8c5 100644
--- a/vespalib/src/vespa/vespalib/component/versionspecification.cpp
+++ b/vespalib/src/vespa/vespalib/component/versionspecification.cpp
@@ -22,7 +22,7 @@ VersionSpecification::VersionSpecification(int major, int minor, int micro, cons
VersionSpecification::VersionSpecification(const VersionSpecification &) = default;
-VersionSpecification::~VersionSpecification() { }
+VersionSpecification::~VersionSpecification() = default;
void
VersionSpecification::initialize()
diff --git a/vespalib/src/vespa/vespalib/component/versionspecification.h b/vespalib/src/vespa/vespalib/component/versionspecification.h
index 5ba49106915..399db123a7a 100644
--- a/vespalib/src/vespa/vespalib/component/versionspecification.h
+++ b/vespalib/src/vespa/vespalib/component/versionspecification.h
@@ -1,7 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once
-#include <vespa/vespalib/component/version.h>
+#include "version.h"
namespace vespalib {
diff --git a/vespalib/src/vespa/vespalib/component/vtag.cpp b/vespalib/src/vespa/vespalib/component/vtag.cpp
index b8ab2c5a920..c9cfade6e28 100644
--- a/vespalib/src/vespa/vespalib/component/vtag.cpp
+++ b/vespalib/src/vespa/vespalib/component/vtag.cpp
@@ -1,6 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include <cstdio>
#include "vtag.h"
+#include <cstdio>
#ifndef V_TAG
#define V_TAG "NOTAG"
diff --git a/yolean/src/main/java/com/yahoo/yolean/Exceptions.java b/yolean/src/main/java/com/yahoo/yolean/Exceptions.java
index fa3eb412016..063ba70c75d 100644
--- a/yolean/src/main/java/com/yahoo/yolean/Exceptions.java
+++ b/yolean/src/main/java/com/yahoo/yolean/Exceptions.java
@@ -3,9 +3,7 @@ package com.yahoo.yolean;
import java.io.IOException;
import java.io.UncheckedIOException;
-import java.nio.file.NoSuchFileException;
import java.util.Optional;
-import java.util.function.Supplier;
/**
* Helper methods for handling exceptions
@@ -51,6 +49,17 @@ public class Exceptions {
}
/**
+ * Returns the first cause or the given throwable that is an instance of {@code clazz}
+ */
+ public static <T extends Throwable> Optional<T> findCause(Throwable t, Class<T> clazz) {
+ for (; t != null; t = t.getCause()) {
+ if (clazz.isInstance(t))
+ return Optional.of(clazz.cast(t));
+ }
+ return Optional.empty();
+ }
+
+ /**
* Wraps any IOException thrown from a runnable in an UncheckedIOException.
*/
public static void uncheck(RunnableThrowingIOException runnable) {
diff --git a/yolean/src/test/java/com/yahoo/yolean/ExceptionsTestCase.java b/yolean/src/test/java/com/yahoo/yolean/ExceptionsTestCase.java
index 390018efdf7..cd06d14bb55 100644
--- a/yolean/src/test/java/com/yahoo/yolean/ExceptionsTestCase.java
+++ b/yolean/src/test/java/com/yahoo/yolean/ExceptionsTestCase.java
@@ -6,6 +6,7 @@ import org.junit.Test;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.NoSuchFileException;
+import java.util.Optional;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -17,6 +18,19 @@ import static org.junit.Assert.assertNull;
public class ExceptionsTestCase {
@Test
+ public void testFindCause() {
+ IllegalArgumentException e1 = new IllegalArgumentException();
+ IllegalStateException e2 = new IllegalStateException(e1);
+ RuntimeException e3 = new RuntimeException(e2);
+
+ assertEquals(Optional.of(e3), Exceptions.findCause(e3, RuntimeException.class));
+ assertEquals(Optional.of(e1), Exceptions.findCause(e3, IllegalArgumentException.class));
+ assertEquals(Optional.empty(), Exceptions.findCause(e3, NumberFormatException.class));
+
+ assertEquals(Optional.of(e2), Exceptions.findCause(e2, RuntimeException.class));
+ }
+
+ @Test
public void testToMessageStrings() {
assertEquals("Blah",Exceptions.toMessageString(new Exception("Blah")));
assertEquals("Blah", Exceptions.toMessageString(new Exception(new Exception("Blah"))));