From 76ec2b9c26c7dfbafa84e38459b47bf33623ea3a Mon Sep 17 00:00:00 2001 From: HÃ¥kon Hallingstad Date: Wed, 6 Jun 2018 13:13:54 +0200 Subject: Revert "Revert "Use HTTP and remove Athenz injection"" --- service-monitor/pom.xml | 23 ++++ .../service/monitor/ServiceStatusProvider.java | 8 +- .../application/ApplicationInstanceGenerator.java | 141 ++++++++++++++++++++- .../application/ConfigServerAppGenerator.java | 67 ---------- .../application/ConfigServerApplication.java | 50 ++++++++ .../monitor/application/DeployedAppGenerator.java | 127 ------------------- .../service/monitor/application/HostsModel.java | 75 +++++++++++ .../monitor/application/ZoneApplication.java | 4 +- .../vespa/service/monitor/internal/DuperModel.java | 38 ++++++ .../monitor/internal/DuperModelListener.java | 28 ++++ .../service/monitor/internal/ModelGenerator.java | 38 ++---- .../service/monitor/internal/MonitorManager.java | 5 +- .../vespa/service/monitor/internal/ServiceId.java | 75 +++++++++++ .../monitor/internal/ServiceMonitorImpl.java | 23 +--- .../monitor/internal/SuperModelListenerImpl.java | 16 ++- .../monitor/internal/UnionMonitorManager.java | 41 ++---- .../internal/health/ApplicationHealthMonitor.java | 102 +++++++++++++++ .../monitor/internal/health/HealthClient.java | 139 ++++++++++++++++++++ .../monitor/internal/health/HealthEndpoint.java | 57 +++++++++ .../monitor/internal/health/HealthInfo.java | 75 +++++++++++ .../monitor/internal/health/HealthMonitor.java | 73 +++++++++++ .../internal/health/HealthMonitorManager.java | 42 ++++-- .../monitor/internal/health/HealthResponse.java | 35 +++++ .../slobrok/SlobrokMonitorManagerImpl.java | 29 ++++- .../ApplicationInstanceGeneratorTest.java | 79 ++++++++++++ .../application/ConfigServerAppGeneratorTest.java | 67 ---------- .../service/monitor/internal/ConfigserverUtil.java | 44 +++++++ .../service/monitor/internal/DuperModelTest.java | 40 ++++++ .../monitor/internal/ModelGeneratorTest.java | 45 ++----- .../internal/SuperModelListenerImplTest.java | 13 +- .../monitor/internal/UnionMonitorManagerTest.java | 97 ++++---------- .../health/ApplicationHealthMonitorTest.java | 24 ++++ .../internal/health/HealthMonitorManagerTest.java | 49 +++++++ .../monitor/internal/health/HealthMonitorTest.java | 21 +++ .../slobrok/SlobrokMonitorManagerImplTest.java | 11 +- 35 files changed, 1319 insertions(+), 482 deletions(-) delete mode 100644 service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/ConfigServerAppGenerator.java delete mode 100644 service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/DeployedAppGenerator.java create mode 100644 service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/HostsModel.java create mode 100644 service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/DuperModel.java create mode 100644 service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/DuperModelListener.java create mode 100644 service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/ServiceId.java create mode 100644 service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/ApplicationHealthMonitor.java create mode 100644 service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthClient.java create mode 100644 service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthEndpoint.java create mode 100644 service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthInfo.java create mode 100644 service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitor.java create mode 100644 service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthResponse.java create mode 100644 service-monitor/src/test/java/com/yahoo/vespa/service/monitor/application/ApplicationInstanceGeneratorTest.java delete mode 100644 service-monitor/src/test/java/com/yahoo/vespa/service/monitor/application/ConfigServerAppGeneratorTest.java create mode 100644 service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/ConfigserverUtil.java create mode 100644 service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/DuperModelTest.java create mode 100644 service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/ApplicationHealthMonitorTest.java create mode 100644 service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorManagerTest.java create mode 100644 service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorTest.java (limited to 'service-monitor') diff --git a/service-monitor/pom.xml b/service-monitor/pom.xml index 70f9d4aa655..b8065ed3636 100644 --- a/service-monitor/pom.xml +++ b/service-monitor/pom.xml @@ -63,6 +63,12 @@ annotations ${project.version} + + com.yahoo.vespa + vespa-athenz + ${project.version} + provided + com.google.inject guice @@ -75,6 +81,23 @@ ${jackson2.version} provided + + com.fasterxml.jackson.core + jackson-core + provided + + + com.fasterxml.jackson.core + jackson-databind + provided + + + org.apache.httpcomponents + httpclient + 4.5 + + compile + junit junit diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/ServiceStatusProvider.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/ServiceStatusProvider.java index 35003313775..75e61eef772 100644 --- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/ServiceStatusProvider.java +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/ServiceStatusProvider.java @@ -11,7 +11,13 @@ import com.yahoo.vespa.applicationmodel.ServiceType; * @author hakon */ public interface ServiceStatusProvider { - /** Get the {@link ServiceStatus} of a particular service. */ + /** + * Get the {@link ServiceStatus} of a particular service. + * + *

{@link ServiceStatus#NOT_CHECKED NOT_CHECKED} must be returned if the + * service status provider does does not monitor the service status for + * the particular application, cluster, service type, and config id. + */ ServiceStatus getStatus(ApplicationId applicationId, ClusterId clusterId, ServiceType serviceType, diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/ApplicationInstanceGenerator.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/ApplicationInstanceGenerator.java index ec2702bcfaf..cbdcce125cc 100644 --- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/ApplicationInstanceGenerator.java +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/ApplicationInstanceGenerator.java @@ -1,13 +1,148 @@ // 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.monitor.application; +import com.yahoo.config.model.api.ApplicationInfo; +import com.yahoo.config.model.api.HostInfo; +import com.yahoo.config.model.api.ServiceInfo; +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.Zone; import com.yahoo.vespa.applicationmodel.ApplicationInstance; +import com.yahoo.vespa.applicationmodel.ApplicationInstanceId; +import com.yahoo.vespa.applicationmodel.ClusterId; +import com.yahoo.vespa.applicationmodel.ConfigId; +import com.yahoo.vespa.applicationmodel.HostName; +import com.yahoo.vespa.applicationmodel.ServiceCluster; +import com.yahoo.vespa.applicationmodel.ServiceClusterKey; +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.monitor.ServiceStatusProvider; +import com.yahoo.vespa.service.monitor.internal.ServiceId; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.yahoo.vespa.service.monitor.application.ConfigServerApplication.CONFIG_SERVER_APPLICATION; /** + * Class to generate an ApplicationInstance given service status for a standard (deployed) application. + * * @author hakon */ -public interface ApplicationInstanceGenerator { - /** Make an ApplicationInstance based on current service status. */ - ApplicationInstance makeApplicationInstance(ServiceStatusProvider serviceStatusProvider); +public class ApplicationInstanceGenerator { + public static final String CLUSTER_ID_PROPERTY_NAME = "clustername"; + + private final ApplicationInfo applicationInfo; + private final Zone zone; + + public ApplicationInstanceGenerator(ApplicationInfo applicationInfo, Zone zone) { + this.applicationInfo = applicationInfo; + this.zone = zone; + } + + public ApplicationInstance makeApplicationInstance(ServiceStatusProvider serviceStatusProvider) { + Map> groupedServiceInstances = new HashMap<>(); + + for (HostInfo host : applicationInfo.getModel().getHosts()) { + HostName hostName = new HostName(host.getHostname()); + for (ServiceInfo serviceInfo : host.getServices()) { + ServiceClusterKey serviceClusterKey = toServiceClusterKey(serviceInfo); + ServiceInstance serviceInstance = + toServiceInstance( + applicationInfo.getApplicationId(), + serviceClusterKey.clusterId(), + serviceInfo, + hostName, + serviceStatusProvider); + + if (!groupedServiceInstances.containsKey(serviceClusterKey)) { + groupedServiceInstances.put(serviceClusterKey, new HashSet<>()); + } + groupedServiceInstances.get(serviceClusterKey).add(serviceInstance); + } + } + + Set serviceClusters = groupedServiceInstances.entrySet().stream() + .map(entry -> new ServiceCluster( + entry.getKey().clusterId(), + entry.getKey().serviceType(), + entry.getValue())) + .collect(Collectors.toSet()); + + ApplicationInstance applicationInstance = new ApplicationInstance( + new TenantId(applicationInfo.getApplicationId().tenant().toString()), + toApplicationInstanceId(applicationInfo, zone), + serviceClusters); + + // Fill back-references + for (ServiceCluster serviceCluster : applicationInstance.serviceClusters()) { + serviceCluster.setApplicationInstance(applicationInstance); + for (ServiceInstance serviceInstance : serviceCluster.serviceInstances()) { + serviceInstance.setServiceCluster(serviceCluster); + } + } + + return applicationInstance; + } + + private ServiceInstance toServiceInstance( + ApplicationId applicationId, + ClusterId clusterId, + ServiceInfo serviceInfo, + HostName hostName, + ServiceStatusProvider serviceStatusProvider) { + ConfigId configId = toConfigId(serviceInfo); + + ServiceStatus status = serviceStatusProvider.getStatus( + applicationId, + clusterId, + toServiceType(serviceInfo), configId); + + return new ServiceInstance(configId, hostName, status); + } + + private ApplicationInstanceId toApplicationInstanceId(ApplicationInfo applicationInfo, Zone zone) { + if (applicationInfo.getApplicationId().equals(CONFIG_SERVER_APPLICATION.getApplicationId())) { + // 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. + return new ApplicationInstanceId(applicationInfo.getApplicationId().application().value()); + } else { + return new ApplicationInstanceId(String.format("%s:%s:%s:%s", + applicationInfo.getApplicationId().application().value(), + zone.environment().value(), + zone.region().value(), + applicationInfo.getApplicationId().instance().value())); + } + } + + public static ServiceId getServiceId(ApplicationInfo applicationInfo, ServiceInfo serviceInfo) { + return new ServiceId( + applicationInfo.getApplicationId(), + getClusterId(serviceInfo), + toServiceType(serviceInfo), + toConfigId(serviceInfo)); + } + + private static ClusterId getClusterId(ServiceInfo serviceInfo) { + return new ClusterId(serviceInfo.getProperty(CLUSTER_ID_PROPERTY_NAME).orElse("")); + } + + private static ServiceClusterKey toServiceClusterKey(ServiceInfo serviceInfo) { + ClusterId clusterId = getClusterId(serviceInfo); + ServiceType serviceType = toServiceType(serviceInfo); + return new ServiceClusterKey(clusterId, serviceType); + } + + private static ServiceType toServiceType(ServiceInfo serviceInfo) { + return new ServiceType(serviceInfo.getServiceType()); + } + + private static ConfigId toConfigId(ServiceInfo serviceInfo) { + return new ConfigId(serviceInfo.getConfigId()); + } } diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/ConfigServerAppGenerator.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/ConfigServerAppGenerator.java deleted file mode 100644 index 76ca59cf583..00000000000 --- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/ConfigServerAppGenerator.java +++ /dev/null @@ -1,67 +0,0 @@ -// 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.monitor.application; - -import com.yahoo.vespa.applicationmodel.ApplicationInstance; -import com.yahoo.vespa.applicationmodel.ConfigId; -import com.yahoo.vespa.applicationmodel.HostName; -import com.yahoo.vespa.applicationmodel.ServiceCluster; -import com.yahoo.vespa.applicationmodel.ServiceInstance; -import com.yahoo.vespa.applicationmodel.ServiceStatus; -import com.yahoo.vespa.service.monitor.ServiceStatusProvider; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * Class for generating an ApplicationInstance for the synthesized config server application. - * - * @author hakon - */ -public class ConfigServerAppGenerator implements ApplicationInstanceGenerator { - private final List hostnames; - - public ConfigServerAppGenerator(List hostnames) { - this.hostnames = hostnames; - } - - @Override - public ApplicationInstance makeApplicationInstance(ServiceStatusProvider statusProvider) { - Set serviceInstances = hostnames.stream() - .map(hostname -> makeServiceInstance(hostname, statusProvider)) - .collect(Collectors.toSet()); - - ServiceCluster serviceCluster = new ServiceCluster( - ConfigServerApplication.CLUSTER_ID, - ConfigServerApplication.SERVICE_TYPE, - serviceInstances); - - Set serviceClusters = new HashSet<>(); - serviceClusters.add(serviceCluster); - - ApplicationInstance applicationInstance = new ApplicationInstance( - ConfigServerApplication.TENANT_ID, - ConfigServerApplication.APPLICATION_INSTANCE_ID, - serviceClusters); - - // Fill back-references - serviceCluster.setApplicationInstance(applicationInstance); - for (ServiceInstance serviceInstance : serviceCluster.serviceInstances()) { - serviceInstance.setServiceCluster(serviceCluster); - } - - return applicationInstance; - } - - private ServiceInstance makeServiceInstance(String hostname, ServiceStatusProvider statusProvider) { - ConfigId configId = new ConfigId(ConfigServerApplication.CONFIG_ID_PREFIX + hostname); - ServiceStatus status = statusProvider.getStatus( - ConfigServerApplication.CONFIG_SERVER_APPLICATION.getApplicationId(), - ConfigServerApplication.CLUSTER_ID, - ConfigServerApplication.SERVICE_TYPE, - configId); - - return new ServiceInstance(configId, new HostName(hostname), status); - } -} diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/ConfigServerApplication.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/ConfigServerApplication.java index 132bb0927b8..5ad38cebcfc 100644 --- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/ConfigServerApplication.java +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/ConfigServerApplication.java @@ -1,12 +1,26 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.service.monitor.application; +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.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.monitor.internal.ModelGenerator; +import com.yahoo.vespa.service.monitor.internal.health.ApplicationHealthMonitor; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * A service/application model of the config server with health status. @@ -21,8 +35,44 @@ public class ConfigServerApplication extends HostedVespaApplication { 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); + } + private ConfigServerApplication() { super("zone-config-servers", NodeType.config, ClusterSpec.Type.admin, ClusterSpec.Id.from("zone-config-servers")); } + + public ApplicationInfo makeApplicationInfo(ConfigserverConfig config) { + List hostInfos = new ArrayList<>(); + List 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 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)); + } } diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/DeployedAppGenerator.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/DeployedAppGenerator.java deleted file mode 100644 index 2691a8bf1ee..00000000000 --- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/DeployedAppGenerator.java +++ /dev/null @@ -1,127 +0,0 @@ -// 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.monitor.application; - -import com.yahoo.config.model.api.ApplicationInfo; -import com.yahoo.config.model.api.HostInfo; -import com.yahoo.config.model.api.ServiceInfo; -import com.yahoo.config.provision.ApplicationId; -import com.yahoo.config.provision.Zone; -import com.yahoo.vespa.applicationmodel.ApplicationInstance; -import com.yahoo.vespa.applicationmodel.ApplicationInstanceId; -import com.yahoo.vespa.applicationmodel.ClusterId; -import com.yahoo.vespa.applicationmodel.ConfigId; -import com.yahoo.vespa.applicationmodel.HostName; -import com.yahoo.vespa.applicationmodel.ServiceCluster; -import com.yahoo.vespa.applicationmodel.ServiceClusterKey; -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.monitor.ServiceStatusProvider; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * Class to generate an ApplicationInstance given service status for a standard (deployed) application. - * - * @author hakon - */ -public class DeployedAppGenerator implements ApplicationInstanceGenerator { - public static final String CLUSTER_ID_PROPERTY_NAME = "clustername"; - - private final ApplicationInfo applicationInfo; - private final Zone zone; - - public DeployedAppGenerator(ApplicationInfo applicationInfo, Zone zone) { - this.applicationInfo = applicationInfo; - this.zone = zone; - } - - @Override - public ApplicationInstance makeApplicationInstance(ServiceStatusProvider serviceStatusProvider) { - Map> groupedServiceInstances = new HashMap<>(); - - for (HostInfo host : applicationInfo.getModel().getHosts()) { - HostName hostName = new HostName(host.getHostname()); - for (ServiceInfo serviceInfo : host.getServices()) { - ServiceClusterKey serviceClusterKey = toServiceClusterKey(serviceInfo); - ServiceInstance serviceInstance = - toServiceInstance( - applicationInfo.getApplicationId(), - serviceClusterKey.clusterId(), - serviceInfo, - hostName, - serviceStatusProvider); - - if (!groupedServiceInstances.containsKey(serviceClusterKey)) { - groupedServiceInstances.put(serviceClusterKey, new HashSet<>()); - } - groupedServiceInstances.get(serviceClusterKey).add(serviceInstance); - } - } - - Set serviceClusters = groupedServiceInstances.entrySet().stream() - .map(entry -> new ServiceCluster( - entry.getKey().clusterId(), - entry.getKey().serviceType(), - entry.getValue())) - .collect(Collectors.toSet()); - - ApplicationInstance applicationInstance = new ApplicationInstance( - new TenantId(applicationInfo.getApplicationId().tenant().toString()), - toApplicationInstanceId(applicationInfo, zone), - serviceClusters); - - // Fill back-references - for (ServiceCluster serviceCluster : applicationInstance.serviceClusters()) { - serviceCluster.setApplicationInstance(applicationInstance); - for (ServiceInstance serviceInstance : serviceCluster.serviceInstances()) { - serviceInstance.setServiceCluster(serviceCluster); - } - } - - return applicationInstance; - } - - static ClusterId getClusterId(ServiceInfo serviceInfo) { - return new ClusterId(serviceInfo.getProperty(CLUSTER_ID_PROPERTY_NAME).orElse("")); - } - - private ServiceClusterKey toServiceClusterKey(ServiceInfo serviceInfo) { - ClusterId clusterId = getClusterId(serviceInfo); - ServiceType serviceType = toServiceType(serviceInfo); - return new ServiceClusterKey(clusterId, serviceType); - } - - private ServiceInstance toServiceInstance( - ApplicationId applicationId, - ClusterId clusterId, - ServiceInfo serviceInfo, - HostName hostName, - ServiceStatusProvider serviceStatusProvider) { - ConfigId configId = new ConfigId(serviceInfo.getConfigId()); - - ServiceStatus status = serviceStatusProvider.getStatus( - applicationId, - clusterId, - toServiceType(serviceInfo), configId); - - return new ServiceInstance(configId, hostName, status); - } - - private ApplicationInstanceId toApplicationInstanceId(ApplicationInfo applicationInfo, Zone zone) { - return new ApplicationInstanceId(String.format("%s:%s:%s:%s", - applicationInfo.getApplicationId().application().value(), - zone.environment().value(), - zone.region().value(), - applicationInfo.getApplicationId().instance().value())); - } - - private ServiceType toServiceType(ServiceInfo serviceInfo) { - return new ServiceType(serviceInfo.getServiceType()); - } -} diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/HostsModel.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/HostsModel.java new file mode 100644 index 00000000000..225ffb0adbc --- /dev/null +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/HostsModel.java @@ -0,0 +1,75 @@ +// 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.monitor.application; + +import com.yahoo.config.FileReference; +import com.yahoo.config.model.api.FileDistribution; +import com.yahoo.config.model.api.HostInfo; +import com.yahoo.config.model.api.Model; +import com.yahoo.config.provision.AllocatedHosts; +import com.yahoo.vespa.config.ConfigKey; +import com.yahoo.vespa.config.ConfigPayload; +import com.yahoo.vespa.config.buildergen.ConfigDefinition; + +import java.time.Instant; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * Model that only supports the subset necessary to create an ApplicationInstance. + * + * @author hakon + */ +public class HostsModel implements Model { + private final Collection hosts; + + public HostsModel(List hosts) { + this.hosts = Collections.unmodifiableCollection(hosts); + } + + @Override + public Collection getHosts() { + return hosts; + } + + @Override + public ConfigPayload getConfig(ConfigKey configKey, ConfigDefinition configDefinition) { + throw new UnsupportedOperationException(); + } + + @Override + public Set> allConfigsProduced() { + throw new UnsupportedOperationException(); + } + + @Override + public Set allConfigIds() { + throw new UnsupportedOperationException(); + } + + @Override + public void distributeFiles(FileDistribution fileDistribution) { + throw new UnsupportedOperationException(); + } + + @Override + public Set fileReferences() { + throw new UnsupportedOperationException(); + } + + @Override + public AllocatedHosts allocatedHosts() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean allowModelVersionMismatch(Instant now) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean skipOldConfigModels(Instant now) { + throw new UnsupportedOperationException(); + } +} diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/ZoneApplication.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/ZoneApplication.java index 6bbf0cb6d1d..c10015d3bfa 100644 --- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/ZoneApplication.java +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/application/ZoneApplication.java @@ -21,8 +21,8 @@ public class ZoneApplication { .createHostedVespaApplicationId("routing"); public static boolean isNodeAdminService(ApplicationId applicationId, - ClusterId clusterId, - ServiceType serviceType) { + ClusterId clusterId, + ServiceType serviceType) { return Objects.equals(applicationId, ZONE_APPLICATION_ID) && Objects.equals(serviceType, ServiceType.CONTAINER) && Objects.equals(clusterId, ClusterId.NODE_ADMIN); diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/DuperModel.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/DuperModel.java new file mode 100644 index 00000000000..d3c6f92312c --- /dev/null +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/DuperModel.java @@ -0,0 +1,38 @@ +// 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.monitor.internal; + +import com.yahoo.cloud.config.ConfigserverConfig; +import com.yahoo.config.model.api.ApplicationInfo; +import com.yahoo.config.model.api.SuperModel; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static com.yahoo.vespa.service.monitor.application.ConfigServerApplication.CONFIG_SERVER_APPLICATION; + +/** + * The {@code DuperModel} unites the {@link com.yahoo.config.model.api.SuperModel SuperModel} + * with the synthetically produced applications like the config server application. + * + * @author hakon + */ +public class DuperModel { + private final List staticApplicationInfos = new ArrayList<>(); + + public DuperModel(ConfigserverConfig configServerConfig) { + staticApplicationInfos.add(CONFIG_SERVER_APPLICATION.makeApplicationInfo(configServerConfig)); + } + + /** For testing. */ + DuperModel(ApplicationInfo... staticApplicationInfos) { + this.staticApplicationInfos.addAll(Arrays.asList(staticApplicationInfos)); + } + + public List getApplicationInfos(SuperModel superModelSnapshot) { + List allApplicationInfos = new ArrayList<>(); + allApplicationInfos.addAll(staticApplicationInfos); + allApplicationInfos.addAll(superModelSnapshot.getAllApplicationInfos()); + return allApplicationInfos; + } +} diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/DuperModelListener.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/DuperModelListener.java new file mode 100644 index 00000000000..235c7db5c36 --- /dev/null +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/DuperModelListener.java @@ -0,0 +1,28 @@ +// 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.monitor.internal; + +import com.yahoo.config.model.api.ApplicationInfo; +import com.yahoo.config.model.api.SuperModel; +import com.yahoo.config.provision.ApplicationId; + +/** + * Interface for listening for changes to the {@link DuperModel}. + * + * @author hakon + */ +public interface DuperModelListener { + /** + * An application has been activated: + * + *

+ */ + void applicationActivated(ApplicationInfo application); + + /** Application has been removed. */ + void applicationRemoved(ApplicationId id); +} diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/ModelGenerator.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/ModelGenerator.java index 9da449289a7..ad2f223acf8 100644 --- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/ModelGenerator.java +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/ModelGenerator.java @@ -1,56 +1,40 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.service.monitor.internal; -import com.yahoo.config.model.api.SuperModel; +import com.yahoo.config.model.api.ApplicationInfo; import com.yahoo.config.provision.Zone; import com.yahoo.vespa.applicationmodel.ApplicationInstance; import com.yahoo.vespa.applicationmodel.ApplicationInstanceReference; import com.yahoo.vespa.service.monitor.ServiceModel; import com.yahoo.vespa.service.monitor.ServiceStatusProvider; import com.yahoo.vespa.service.monitor.application.ApplicationInstanceGenerator; -import com.yahoo.vespa.service.monitor.application.ConfigServerAppGenerator; -import com.yahoo.vespa.service.monitor.application.DeployedAppGenerator; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; /** - * Util to convert SuperModel to ServiceModel and application model classes + * Util to make ServiceModel and its related application model classes */ public class ModelGenerator { public static final String CLUSTER_ID_PROPERTY_NAME = "clustername"; - private final List staticGenerators; - - public ModelGenerator(List configServerHosts) { - if (configServerHosts.isEmpty()) { - staticGenerators = Collections.emptyList(); - } else { - staticGenerators = Collections.singletonList(new ConfigServerAppGenerator(configServerHosts)); - } - } - /** * Create service model based primarily on super model. * * If the configServerhosts is non-empty, a config server application is added. */ - ServiceModel toServiceModel( - SuperModel superModel, - Zone zone, - ServiceStatusProvider serviceStatusProvider) { - List generators = new ArrayList<>(staticGenerators); - superModel.getAllApplicationInfos() - .forEach(info -> generators.add(new DeployedAppGenerator(info, zone))); - - Map applicationInstances = generators.stream() - .map(generator -> generator.makeApplicationInstance(serviceStatusProvider)) - .collect(Collectors.toMap(ApplicationInstance::reference, Function.identity())); + public ServiceModel toServiceModel(List allApplicationInfos, + Zone zone, + ServiceStatusProvider serviceStatusProvider) { + Map applicationInstances = + allApplicationInfos.stream() + .map(info -> new ApplicationInstanceGenerator(info, zone) + .makeApplicationInstance(serviceStatusProvider)) + .collect(Collectors.toMap(ApplicationInstance::reference, Function.identity())); return new ServiceModel(applicationInstances); } + } diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/MonitorManager.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/MonitorManager.java index 49863672c43..1edf3a18215 100644 --- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/MonitorManager.java +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/MonitorManager.java @@ -1,11 +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.monitor.internal;// 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.monitor.internal; -import com.yahoo.config.model.api.SuperModelListener; import com.yahoo.vespa.service.monitor.ServiceStatusProvider; /** * @author hakon */ -public interface MonitorManager extends SuperModelListener, ServiceStatusProvider { +public interface MonitorManager extends DuperModelListener, ServiceStatusProvider { } diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/ServiceId.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/ServiceId.java new file mode 100644 index 00000000000..993ea7fed5c --- /dev/null +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/ServiceId.java @@ -0,0 +1,75 @@ +// 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.monitor.internal; + +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.vespa.applicationmodel.ClusterId; +import com.yahoo.vespa.applicationmodel.ConfigId; +import com.yahoo.vespa.applicationmodel.ServiceType; + +import javax.annotation.concurrent.Immutable; +import java.util.Objects; + +/** + * Identifies a service. + * + * @author hakon + */ +@Immutable +public class ServiceId { + private final ApplicationId applicationId; + private final ClusterId clusterId; + private final ServiceType serviceType; + private final ConfigId configId; + + public ServiceId(ApplicationId applicationId, + ClusterId clusterId, + ServiceType serviceType, + ConfigId configId) { + this.applicationId = applicationId; + this.clusterId = clusterId; + this.serviceType = serviceType; + this.configId = configId; + } + + public ApplicationId getApplicationId() { + return applicationId; + } + + public ClusterId getClusterId() { + return clusterId; + } + + public ServiceType getServiceType() { + return serviceType; + } + + public ConfigId getConfigId() { + return configId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ServiceId serviceId = (ServiceId) o; + return Objects.equals(applicationId, serviceId.applicationId) && + Objects.equals(clusterId, serviceId.clusterId) && + Objects.equals(serviceType, serviceId.serviceType) && + Objects.equals(configId, serviceId.configId); + } + + @Override + public int hashCode() { + return Objects.hash(applicationId, clusterId, serviceType, configId); + } + + @Override + public String toString() { + return "ServiceId{" + + "applicationId=" + applicationId + + ", clusterId=" + clusterId + + ", serviceType=" + serviceType + + ", configId=" + configId + + '}'; + } +} diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/ServiceMonitorImpl.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/ServiceMonitorImpl.java index 97c4fdda0f3..bd8fd4a50e0 100644 --- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/ServiceMonitorImpl.java +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/ServiceMonitorImpl.java @@ -14,10 +14,7 @@ import com.yahoo.vespa.service.monitor.ServiceMonitor; import com.yahoo.vespa.service.monitor.internal.health.HealthMonitorManager; import com.yahoo.vespa.service.monitor.internal.slobrok.SlobrokMonitorManagerImpl; -import java.util.Collections; -import java.util.List; import java.util.Map; -import java.util.stream.Collectors; public class ServiceMonitorImpl implements ServiceMonitor { private final ServiceModelCache serviceModelCache; @@ -32,30 +29,20 @@ public class ServiceMonitorImpl implements ServiceMonitor { Zone zone = superModelProvider.getZone(); ServiceMonitorMetrics metrics = new ServiceMonitorMetrics(metric, timer); - UnionMonitorManager monitorManager = new UnionMonitorManager( - slobrokMonitorManager, - healthMonitorManager, - configserverConfig); + DuperModel duperModel = new DuperModel(configserverConfig); + UnionMonitorManager monitorManager = + new UnionMonitorManager(slobrokMonitorManager, healthMonitorManager); SuperModelListenerImpl superModelListener = new SuperModelListenerImpl( monitorManager, metrics, - new ModelGenerator(toConfigServerList(configserverConfig)), + duperModel, + new ModelGenerator(), zone); superModelListener.start(superModelProvider); serviceModelCache = new ServiceModelCache(superModelListener, timer); } - private List toConfigServerList(ConfigserverConfig configserverConfig) { - if (configserverConfig.multitenant()) { - return configserverConfig.zookeeperserver().stream() - .map(ConfigserverConfig.Zookeeperserver::hostname) - .collect(Collectors.toList()); - } - - return Collections.emptyList(); - } - @Override public Map getAllApplicationInstances() { return serviceModelCache.get().getAllApplicationInstances(); diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/SuperModelListenerImpl.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/SuperModelListenerImpl.java index b2f3617131b..f509809c33d 100644 --- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/SuperModelListenerImpl.java +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/SuperModelListenerImpl.java @@ -8,7 +8,9 @@ import com.yahoo.config.model.api.SuperModelProvider; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Zone; import com.yahoo.vespa.service.monitor.ServiceModel; +import com.yahoo.vespa.service.monitor.ServiceStatusProvider; +import java.util.List; import java.util.function.Supplier; import java.util.logging.Logger; @@ -16,6 +18,7 @@ public class SuperModelListenerImpl implements SuperModelListener, Supplier - monitorManager.applicationActivated(superModel, application)); + duperModel.getApplicationInfos(superModel).forEach(monitorManager::applicationActivated); } } @@ -50,7 +54,7 @@ public class SuperModelListenerImpl implements SuperModelListener, Supplier applicationInfos = duperModel.getApplicationInfos(superModel); + + return modelGenerator.toServiceModel(applicationInfos, zone, (ServiceStatusProvider) monitorManager); } } diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/UnionMonitorManager.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/UnionMonitorManager.java index 82d2043bd17..81cf6f2af5e 100644 --- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/UnionMonitorManager.java +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/UnionMonitorManager.java @@ -1,16 +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.monitor.internal; -import com.yahoo.cloud.config.ConfigserverConfig; import com.yahoo.config.model.api.ApplicationInfo; -import com.yahoo.config.model.api.SuperModel; import com.yahoo.config.provision.ApplicationId; 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.monitor.application.ConfigServerApplication; -import com.yahoo.vespa.service.monitor.application.ZoneApplication; import com.yahoo.vespa.service.monitor.internal.health.HealthMonitorManager; import com.yahoo.vespa.service.monitor.internal.slobrok.SlobrokMonitorManagerImpl; @@ -20,14 +16,11 @@ import com.yahoo.vespa.service.monitor.internal.slobrok.SlobrokMonitorManagerImp public class UnionMonitorManager implements MonitorManager { private final SlobrokMonitorManagerImpl slobrokMonitorManager; private final HealthMonitorManager healthMonitorManager; - private final ConfigserverConfig configserverConfig; UnionMonitorManager(SlobrokMonitorManagerImpl slobrokMonitorManager, - HealthMonitorManager healthMonitorManager, - ConfigserverConfig configserverConfig) { + HealthMonitorManager healthMonitorManager) { this.slobrokMonitorManager = slobrokMonitorManager; this.healthMonitorManager = healthMonitorManager; - this.configserverConfig = configserverConfig; } @Override @@ -35,33 +28,25 @@ public class UnionMonitorManager implements MonitorManager { ClusterId clusterId, ServiceType serviceType, ConfigId configId) { - - if (applicationId.equals(ConfigServerApplication.CONFIG_SERVER_APPLICATION.getApplicationId())) { - // todo: use health - return ServiceStatus.NOT_CHECKED; + // Trust the new health monitoring status if it actually monitors the particular service. + ServiceStatus status = healthMonitorManager.getStatus(applicationId, clusterId, serviceType, configId); + if (status != ServiceStatus.NOT_CHECKED) { + return status; } - MonitorManager monitorManager = useHealth(applicationId, clusterId, serviceType) ? - healthMonitorManager : - slobrokMonitorManager; - - return monitorManager.getStatus(applicationId, clusterId, serviceType, configId); + // fallback is the older slobrok + return slobrokMonitorManager.getStatus(applicationId, clusterId, serviceType, configId); } @Override - public void applicationActivated(SuperModel superModel, ApplicationInfo application) { - slobrokMonitorManager.applicationActivated(superModel, application); - healthMonitorManager.applicationActivated(superModel, application); + public void applicationActivated(ApplicationInfo application) { + slobrokMonitorManager.applicationActivated(application); + healthMonitorManager.applicationActivated(application); } @Override - public void applicationRemoved(SuperModel superModel, ApplicationId id) { - slobrokMonitorManager.applicationRemoved(superModel, id); - healthMonitorManager.applicationRemoved(superModel, id); - } - - private boolean useHealth(ApplicationId applicationId, ClusterId clusterId, ServiceType serviceType) { - return !configserverConfig.nodeAdminInContainer() && - ZoneApplication.isNodeAdminService(applicationId, clusterId, serviceType); + public void applicationRemoved(ApplicationId id) { + slobrokMonitorManager.applicationRemoved(id); + healthMonitorManager.applicationRemoved(id); } } diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/ApplicationHealthMonitor.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/ApplicationHealthMonitor.java new file mode 100644 index 00000000000..bd2658db8aa --- /dev/null +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/ApplicationHealthMonitor.java @@ -0,0 +1,102 @@ +// 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.monitor.internal.health; + +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.ApplicationId; +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.service.monitor.ServiceStatusProvider; +import com.yahoo.vespa.service.monitor.application.ApplicationInstanceGenerator; +import com.yahoo.vespa.service.monitor.internal.ServiceId; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Responsible for monitoring a whole application using /state/v1/health. + * + * @author hakon + */ +public class ApplicationHealthMonitor implements ServiceStatusProvider, AutoCloseable { + public static final String PORT_TAG_STATE = "STATE"; + public static final String PORT_TAG_HTTP = "HTTP"; + /** Port tags implying /state/v1/health is served */ + public static final List PORT_TAGS_HEALTH = + Collections.unmodifiableList(Arrays.asList(PORT_TAG_HTTP, PORT_TAG_STATE)); + + private final Map healthMonitors; + + public static ApplicationHealthMonitor startMonitoring(ApplicationInfo application) { + return new ApplicationHealthMonitor(makeHealthMonitors(application)); + } + + private ApplicationHealthMonitor(Map healthMonitors) { + this.healthMonitors = healthMonitors; + } + + @Override + public ServiceStatus getStatus(ApplicationId applicationId, + ClusterId clusterId, + ServiceType serviceType, + ConfigId configId) { + ServiceId serviceId = new ServiceId(applicationId, clusterId, serviceType, configId); + HealthMonitor monitor = healthMonitors.get(serviceId); + if (monitor == null) { + return ServiceStatus.NOT_CHECKED; + } + + return monitor.getStatus(); + } + + @Override + public void close() { + healthMonitors.values().forEach(HealthMonitor::close); + healthMonitors.clear(); + } + + private static Map makeHealthMonitors(ApplicationInfo application) { + Map healthMonitors = new HashMap<>(); + for (HostInfo hostInfo : application.getModel().getHosts()) { + for (ServiceInfo serviceInfo : hostInfo.getServices()) { + for (PortInfo portInfo : serviceInfo.getPorts()) { + maybeCreateHealthMonitor( + application, + hostInfo, + serviceInfo, + portInfo) + .ifPresent(healthMonitor -> healthMonitors.put( + ApplicationInstanceGenerator.getServiceId(application, serviceInfo), + healthMonitor)); + } + } + } + return healthMonitors; + } + + private static Optional maybeCreateHealthMonitor( + ApplicationInfo applicationInfo, + HostInfo hostInfo, + ServiceInfo serviceInfo, + PortInfo portInfo) { + if (portInfo.getTags().containsAll(PORT_TAGS_HEALTH)) { + HostName hostname = HostName.from(hostInfo.getHostname()); + HealthEndpoint endpoint = HealthEndpoint.forHttp(hostname, portInfo.getPort()); + // todo: make HealthMonitor + // HealthMonitor healthMonitor = new HealthMonitor(endpoint); + // healthMonitor.startMonitoring(); + return Optional.empty(); + } + + return Optional.empty(); + } +} diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthClient.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthClient.java new file mode 100644 index 00000000000..43a02a385be --- /dev/null +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthClient.java @@ -0,0 +1,139 @@ +// 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.monitor.internal.health; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.yahoo.vespa.athenz.api.AthenzService; +import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.ConnectionKeepAliveStrategy; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.BasicHttpClientConnectionManager; +import org.apache.http.protocol.HttpContext; +import org.apache.http.util.EntityUtils; + +import javax.net.ssl.SSLContext; + +/** + * @author hakon + */ +public class HealthClient implements AutoCloseable, ServiceIdentityProvider.Listener { + private static final ObjectMapper mapper = new ObjectMapper(); + private static final long MAX_CONTENT_LENGTH = 1L << 20; // 1 MB + private static final int DEFAULT_TIMEOUT_MILLIS = 1_000; + + private static final ConnectionKeepAliveStrategy KEEP_ALIVE_STRATEGY = + new DefaultConnectionKeepAliveStrategy() { + @Override + public long getKeepAliveDuration(HttpResponse response, HttpContext context) { + long keepAlive = super.getKeepAliveDuration(response, context); + if (keepAlive == -1) { + // Keep connections alive 60 seconds if a keep-alive value + // has not be explicitly set by the server + keepAlive = 60000; + } + return keepAlive; + } + }; + + private final HealthEndpoint endpoint; + + private volatile CloseableHttpClient httpClient; + + public HealthClient(HealthEndpoint endpoint) { + this.endpoint = endpoint; + } + + public void start() { + endpoint.getServiceIdentityProvider().ifPresent(provider -> { + onCredentialsUpdate(provider.getIdentitySslContext(), null); + provider.addIdentityListener(this); + }); + } + + @Override + public void onCredentialsUpdate(SSLContext sslContext, AthenzService ignored) { + SSLConnectionSocketFactory socketFactory = + new SSLConnectionSocketFactory(sslContext, endpoint.getHostnameVerifier().orElse(null)); + + Registry registry = RegistryBuilder.create() + .register("https", socketFactory) + .build(); + + HttpClientConnectionManager connectionManager = new BasicHttpClientConnectionManager(registry); + + RequestConfig requestConfig = RequestConfig.custom() + .setConnectTimeout(DEFAULT_TIMEOUT_MILLIS) // establishment of connection + .setConnectionRequestTimeout(DEFAULT_TIMEOUT_MILLIS) // connection from connection manager + .setSocketTimeout(DEFAULT_TIMEOUT_MILLIS) // waiting for data + .build(); + + this.httpClient = HttpClients.custom() + .setKeepAliveStrategy(KEEP_ALIVE_STRATEGY) + .setConnectionManager(connectionManager) + .disableAutomaticRetries() + .setDefaultRequestConfig(requestConfig) + .build(); + } + + public HealthInfo getHealthInfo() { + try { + return probeHealth(); + } catch (Exception e) { + return HealthInfo.fromException(e); + } + } + + @Override + public void close() { + endpoint.getServiceIdentityProvider().ifPresent(provider -> provider.removeIdentityListener(this)); + + try { + httpClient.close(); + } catch (Exception e) { + // ignore + } + httpClient = null; + } + + private HealthInfo probeHealth() throws Exception { + HttpGet httpget = new HttpGet(endpoint.getStateV1HealthUrl().toString()); + CloseableHttpResponse httpResponse; + + CloseableHttpClient httpClient = this.httpClient; + if (httpClient == null) { + throw new IllegalStateException("HTTP client has closed"); + } + + httpResponse = httpClient.execute(httpget); + + int httpStatusCode = httpResponse.getStatusLine().getStatusCode(); + if (httpStatusCode < 200 || httpStatusCode >= 300) { + return HealthInfo.fromBadHttpStatusCode(httpStatusCode); + } + + HttpEntity bodyEntity = httpResponse.getEntity(); + long contentLength = bodyEntity.getContentLength(); + if (contentLength > MAX_CONTENT_LENGTH) { + throw new IllegalArgumentException("Content too long: " + contentLength + " bytes"); + } + String body = EntityUtils.toString(bodyEntity); + HealthResponse healthResponse = mapper.readValue(body, HealthResponse.class); + + if (healthResponse.status == null || healthResponse.status.code == null) { + return HealthInfo.fromHealthStatusCode(HealthResponse.Status.DEFAULT_STATUS); + } else { + return HealthInfo.fromHealthStatusCode(healthResponse.status.code); + } + } +} diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthEndpoint.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthEndpoint.java new file mode 100644 index 00000000000..e9d17a9ab70 --- /dev/null +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthEndpoint.java @@ -0,0 +1,57 @@ +// 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.monitor.internal.health; + +import com.yahoo.config.provision.HostName; +import com.yahoo.vespa.athenz.api.AthenzIdentity; +import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider; +import com.yahoo.vespa.athenz.tls.AthenzIdentityVerifier; + +import javax.net.ssl.HostnameVerifier; +import java.net.URL; +import java.util.Collections; +import java.util.Optional; + +import static com.yahoo.yolean.Exceptions.uncheck; + +/** + * @author hakon + */ +class HealthEndpoint { + private final URL url; + private final Optional hostnameVerifier; + private final Optional serviceIdentityProvider; + + static HealthEndpoint forHttp(HostName hostname, int port) { + URL url = uncheck(() -> new URL("http", hostname.value(), port, "/state/v1/health")); + return new HealthEndpoint(url, Optional.empty(), Optional.empty()); + } + + static HealthEndpoint forHttps(HostName hostname, + int port, + ServiceIdentityProvider serviceIdentityProvider, + AthenzIdentity remoteIdentity) { + URL url = uncheck(() -> new URL("https", hostname.value(), port, "/state/v1/health")); + HostnameVerifier peerVerifier = new AthenzIdentityVerifier(Collections.singleton(remoteIdentity)); + return new HealthEndpoint(url, Optional.of(serviceIdentityProvider), Optional.of(peerVerifier)); + } + + private HealthEndpoint(URL url, + Optional serviceIdentityProvider, + Optional hostnameVerifier) { + this.url = url; + this.serviceIdentityProvider = serviceIdentityProvider; + this.hostnameVerifier = hostnameVerifier; + } + + public URL getStateV1HealthUrl() { + return url; + } + + public Optional getServiceIdentityProvider() { + return serviceIdentityProvider; + } + + public Optional getHostnameVerifier() { + return hostnameVerifier; + } +} diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthInfo.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthInfo.java new file mode 100644 index 00000000000..a3fe3cb3106 --- /dev/null +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthInfo.java @@ -0,0 +1,75 @@ +// 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.monitor.internal.health; + +import com.yahoo.vespa.applicationmodel.ServiceStatus; +import com.yahoo.yolean.Exceptions; + +import java.time.Instant; +import java.util.Optional; +import java.util.OptionalInt; + +/** + * The result of a health lookup. + * + * @author hakon + */ +public class HealthInfo { + public static final String UP_STATUS_CODE = "up"; + + private final Optional exception; + private final OptionalInt httpStatusCode; + private final Optional healthStatusCode; + private final Instant time; + + static HealthInfo fromException(Exception exception) { + return new HealthInfo(Optional.of(exception), OptionalInt.empty(), Optional.empty()); + } + + static HealthInfo fromBadHttpStatusCode(int httpStatusCode) { + return new HealthInfo(Optional.empty(), OptionalInt.of(httpStatusCode), Optional.empty()); + } + + static HealthInfo fromHealthStatusCode(String healthStatusCode) { + return new HealthInfo(Optional.empty(), OptionalInt.empty(), Optional.of(healthStatusCode)); + } + + static HealthInfo empty() { + return new HealthInfo(Optional.empty(), OptionalInt.empty(), Optional.empty()); + } + + private HealthInfo(Optional exception, + OptionalInt httpStatusCode, + Optional healthStatusCode) { + this.exception = exception; + this.httpStatusCode = httpStatusCode; + this.healthStatusCode = healthStatusCode; + this.time = Instant.now(); + } + + public boolean isHealthy() { + return healthStatusCode.map(UP_STATUS_CODE::equals).orElse(false); + } + + public ServiceStatus toSerivceStatus() { + return isHealthy() ? ServiceStatus.UP : ServiceStatus.DOWN; + } + + public Instant time() { + return time; + } + + @Override + public String toString() { + if (isHealthy()) { + return UP_STATUS_CODE; + } else if (healthStatusCode.isPresent()) { + return "Bad health status code '" + healthStatusCode.get() + "'"; + } else if (exception.isPresent()) { + return Exceptions.toMessageString(exception.get()); + } else if (httpStatusCode.isPresent()) { + return "Bad HTTP response status code " + httpStatusCode.getAsInt(); + } else { + return "No health info available"; + } + } +} diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitor.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitor.java new file mode 100644 index 00000000000..fd809b32918 --- /dev/null +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitor.java @@ -0,0 +1,73 @@ +// 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.monitor.internal.health; + +import com.yahoo.log.LogLevel; +import com.yahoo.vespa.applicationmodel.ServiceStatus; + +import java.time.Duration; +import java.util.Random; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; + +/** + * Used to monitor the health of a single URL endpoint. + * + * @author hakon + */ +public class HealthMonitor implements AutoCloseable { + private static final Logger logger = Logger.getLogger(HealthMonitor.class.getName()); + private static final Duration DELAY = Duration.ofSeconds(20); + // About 'static': Javadoc says "Instances of java.util.Random are threadsafe." + private static final Random random = new Random(); + + private final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1); + private final HealthClient healthClient; + + private volatile HealthInfo lastHealthInfo = HealthInfo.empty(); + + public HealthMonitor(HealthEndpoint stateV1HealthEndpoint) { + this.healthClient = new HealthClient(stateV1HealthEndpoint); + } + + /** For testing. */ + HealthMonitor(HealthClient healthClient) { + this.healthClient = healthClient; + } + + public void startMonitoring() { + healthClient.start(); + executor.scheduleWithFixedDelay( + this::updateSynchronously, + initialDelayInSeconds(DELAY.getSeconds()), + DELAY.getSeconds(), + TimeUnit.SECONDS); + } + + public ServiceStatus getStatus() { + // todo: return lastHealthInfo.toServiceStatus(); + return ServiceStatus.NOT_CHECKED; + } + + @Override + public void close() { + executor.shutdown(); + + try { + executor.awaitTermination(2, TimeUnit.SECONDS); + } catch (InterruptedException e) { + logger.log(LogLevel.INFO, "Interrupted while waiting for health monitor termination: " + + e.getMessage()); + } + + healthClient.close(); + } + + private long initialDelayInSeconds(long maxInitialDelayInSeconds) { + return random.nextLong() % maxInitialDelayInSeconds; + } + + private void updateSynchronously() { + lastHealthInfo = healthClient.getHealthInfo(); + } +} diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorManager.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorManager.java index 5a4b7251ae2..473ef5e3a94 100644 --- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorManager.java +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorManager.java @@ -2,8 +2,8 @@ package com.yahoo.vespa.service.monitor.internal.health; import com.google.inject.Inject; +import com.yahoo.cloud.config.ConfigserverConfig; import com.yahoo.config.model.api.ApplicationInfo; -import com.yahoo.config.model.api.SuperModel; import com.yahoo.config.provision.ApplicationId; import com.yahoo.vespa.applicationmodel.ClusterId; import com.yahoo.vespa.applicationmodel.ConfigId; @@ -12,19 +12,38 @@ import com.yahoo.vespa.applicationmodel.ServiceType; import com.yahoo.vespa.service.monitor.application.ZoneApplication; import com.yahoo.vespa.service.monitor.internal.MonitorManager; +import java.util.HashMap; +import java.util.Map; + /** * @author hakon */ public class HealthMonitorManager implements MonitorManager { + private final Map healthMonitors = new HashMap<>(); + private final ConfigserverConfig configserverConfig; + @Inject - public HealthMonitorManager() {} + public HealthMonitorManager(ConfigserverConfig configserverConfig) { + this.configserverConfig = configserverConfig; + } @Override - public void applicationActivated(SuperModel superModel, ApplicationInfo application) { + public void applicationActivated(ApplicationInfo application) { + if (applicationMonitored(application.getApplicationId())) { + ApplicationHealthMonitor monitor = + ApplicationHealthMonitor.startMonitoring(application); + healthMonitors.put(application.getApplicationId(), monitor); + } } @Override - public void applicationRemoved(SuperModel superModel, ApplicationId id) { + public void applicationRemoved(ApplicationId id) { + if (applicationMonitored(id)) { + ApplicationHealthMonitor monitor = healthMonitors.remove(id); + if (monitor != null) { + monitor.close(); + } + } } @Override @@ -32,13 +51,18 @@ public class HealthMonitorManager implements MonitorManager { ClusterId clusterId, ServiceType serviceType, ConfigId configId) { - // TODO: Do proper health check - if (ZoneApplication.isNodeAdminService(applicationId, clusterId, serviceType)) { + if (!configserverConfig.nodeAdminInContainer() && + ZoneApplication.isNodeAdminService(applicationId, clusterId, serviceType)) { + // If node admin doesn't run in a JDisc container, it must be monitored with health. + // TODO: Do proper health check return ServiceStatus.UP; } - throw new IllegalArgumentException("Health monitoring not implemented for application " + - applicationId.toShortString() + ", cluster " + clusterId.s() + ", serviceType " + - serviceType); + return ServiceStatus.NOT_CHECKED; + } + + private boolean applicationMonitored(ApplicationId id) { + // todo: health-check config server + return false; } } diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthResponse.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthResponse.java new file mode 100644 index 00000000000..574523ad564 --- /dev/null +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/health/HealthResponse.java @@ -0,0 +1,35 @@ +// 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.monitor.internal.health; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.yahoo.text.JSON; + +/** + * Response entity from /state/v1/health + * + * @author hakon + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class HealthResponse { + @JsonProperty("status") + public Status status = new Status(); + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Status { + public static final String DEFAULT_STATUS = "down"; + + @JsonProperty("code") + public String code = DEFAULT_STATUS; + + @Override + public String toString() { + return "{ \"code\": \"" + JSON.escape(code) + "\" }"; + } + } + + @Override + public String toString() { + return "{ \"status\": " + status.toString() + " }"; + } +} diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/slobrok/SlobrokMonitorManagerImpl.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/slobrok/SlobrokMonitorManagerImpl.java index aaaab22e742..68958c94dfd 100644 --- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/slobrok/SlobrokMonitorManagerImpl.java +++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/internal/slobrok/SlobrokMonitorManagerImpl.java @@ -3,8 +3,6 @@ package com.yahoo.vespa.service.monitor.internal.slobrok; import com.google.inject.Inject; import com.yahoo.config.model.api.ApplicationInfo; -import com.yahoo.config.model.api.SuperModel; -import com.yahoo.config.model.api.SuperModelListener; import com.yahoo.config.provision.ApplicationId; import com.yahoo.jrt.slobrok.api.Mirror; import com.yahoo.log.LogLevel; @@ -13,6 +11,7 @@ import com.yahoo.vespa.applicationmodel.ConfigId; import com.yahoo.vespa.applicationmodel.ServiceStatus; import com.yahoo.vespa.applicationmodel.ServiceType; import com.yahoo.vespa.service.monitor.SlobrokApi; +import com.yahoo.vespa.service.monitor.application.ConfigServerApplication; import com.yahoo.vespa.service.monitor.internal.MonitorManager; import java.util.HashMap; @@ -21,7 +20,7 @@ import java.util.Optional; import java.util.function.Supplier; import java.util.logging.Logger; -public class SlobrokMonitorManagerImpl implements SuperModelListener, SlobrokApi, MonitorManager { +public class SlobrokMonitorManagerImpl implements SlobrokApi, MonitorManager { private static final Logger logger = Logger.getLogger(SlobrokMonitorManagerImpl.class.getName()); @@ -40,7 +39,11 @@ public class SlobrokMonitorManagerImpl implements SuperModelListener, SlobrokApi } @Override - public void applicationActivated(SuperModel superModel, ApplicationInfo application) { + public void applicationActivated(ApplicationInfo application) { + if (!applicationMonitoredWithSlobrok(application.getApplicationId())) { + return; + } + synchronized (monitor) { SlobrokMonitor slobrokMonitor = slobrokMonitors.computeIfAbsent( application.getApplicationId(), @@ -50,7 +53,11 @@ public class SlobrokMonitorManagerImpl implements SuperModelListener, SlobrokApi } @Override - public void applicationRemoved(SuperModel superModel, ApplicationId id) { + public void applicationRemoved(ApplicationId id) { + if (!applicationMonitoredWithSlobrok(id)) { + return; + } + synchronized (monitor) { SlobrokMonitor slobrokMonitor = slobrokMonitors.remove(id); if (slobrokMonitor == null) { @@ -79,6 +86,10 @@ public class SlobrokMonitorManagerImpl implements SuperModelListener, SlobrokApi ClusterId clusterId, ServiceType serviceType, ConfigId configId) { + if (!applicationMonitoredWithSlobrok(applicationId)) { + return ServiceStatus.NOT_CHECKED; + } + Optional slobrokServiceName = findSlobrokServiceName(serviceType, configId); if (slobrokServiceName.isPresent()) { synchronized (monitor) { @@ -95,6 +106,14 @@ public class SlobrokMonitorManagerImpl implements SuperModelListener, SlobrokApi } } + private boolean applicationMonitoredWithSlobrok(ApplicationId applicationId) { + if (applicationId.equals(ConfigServerApplication.CONFIG_SERVER_APPLICATION.getApplicationId())) { + return false; + } + + return true; + } + /** * Get the Slobrok service name of the service, or empty if the service * is not registered with Slobrok. diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/application/ApplicationInstanceGeneratorTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/application/ApplicationInstanceGeneratorTest.java new file mode 100644 index 00000000000..e1d9b728bf8 --- /dev/null +++ b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/application/ApplicationInstanceGeneratorTest.java @@ -0,0 +1,79 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.service.monitor.application; + +import com.yahoo.cloud.config.ConfigserverConfig; +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.internal.ConfigserverUtil; +import org.junit.Test; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.yahoo.vespa.service.monitor.application.ConfigServerApplication.CONFIG_SERVER_APPLICATION; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ApplicationInstanceGeneratorTest { + private static final String configServer1 = "cfg1.yahoo.com"; + private static final String configServer2 = "cfg2.yahoo.com"; + private static final String configServer3 = "cfg3.yahoo.com"; + private static final List configServerList = Stream.of( + configServer1, + configServer2, + configServer3).collect(Collectors.toList()); + + private final ServiceStatusProvider statusProvider = mock(ServiceStatusProvider.class); + + @Test + public void toApplicationInstance() { + when(statusProvider.getStatus(any(), any(), any(), any())).thenReturn(ServiceStatus.NOT_CHECKED); + ConfigserverConfig config = ConfigserverUtil.create( + true, + configServer1, + configServer2, + configServer3); + Zone zone = mock(Zone.class); + ApplicationInfo configServer = CONFIG_SERVER_APPLICATION.makeApplicationInfo(config); + ApplicationInstance applicationInstance = new ApplicationInstanceGenerator(configServer, zone) + .makeApplicationInstance(statusProvider); + + assertEquals( + ConfigServerApplication.APPLICATION_INSTANCE_ID, + applicationInstance.applicationInstanceId()); + assertEquals( + ConfigServerApplication.TENANT_ID, + applicationInstance.tenantId()); + + assertEquals( + ConfigServerApplication.TENANT_ID.toString() + + ":" + ConfigServerApplication.APPLICATION_INSTANCE_ID, + applicationInstance.reference().toString()); + + assertEquals( + ConfigServerApplication.CLUSTER_ID, + applicationInstance.serviceClusters().iterator().next().clusterId()); + + assertEquals( + ServiceStatus.NOT_CHECKED, + applicationInstance + .serviceClusters().iterator().next() + .serviceInstances().iterator().next() + .serviceStatus()); + + assertTrue(configServerList.contains( + applicationInstance + .serviceClusters().iterator().next() + .serviceInstances().iterator().next() + .hostName() + .toString())); + } + +} \ No newline at end of file diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/application/ConfigServerAppGeneratorTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/application/ConfigServerAppGeneratorTest.java deleted file mode 100644 index 58f99786017..00000000000 --- a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/application/ConfigServerAppGeneratorTest.java +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.service.monitor.application; - -import com.yahoo.vespa.applicationmodel.ApplicationInstance; -import com.yahoo.vespa.applicationmodel.ServiceStatus; -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 org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class ConfigServerAppGeneratorTest { - private static final String configServer1 = "cfg1.yahoo.com"; - private static final String configServer2 = "cfg2.yahoo.com"; - private static final String configServer3 = "cfg3.yahoo.com"; - private static final List configServerList = Stream.of( - configServer1, - configServer2, - configServer3).collect(Collectors.toList()); - - private final ServiceStatusProvider statusProvider = mock(ServiceStatusProvider.class); - - @Test - public void toApplicationInstance() throws Exception { - when(statusProvider.getStatus(any(), any(), any(), any())).thenReturn(ServiceStatus.NOT_CHECKED); - ApplicationInstance applicationInstance = new ConfigServerAppGenerator(configServerList) - .makeApplicationInstance(statusProvider); - - assertEquals( - ConfigServerApplication.APPLICATION_INSTANCE_ID, - applicationInstance.applicationInstanceId()); - assertEquals( - ConfigServerApplication.TENANT_ID, - applicationInstance.tenantId()); - - assertEquals( - ConfigServerApplication.TENANT_ID.toString() + - ":" + ConfigServerApplication.APPLICATION_INSTANCE_ID, - applicationInstance.reference().toString()); - - assertEquals( - ConfigServerApplication.CLUSTER_ID, - applicationInstance.serviceClusters().iterator().next().clusterId()); - - assertEquals( - ServiceStatus.NOT_CHECKED, - applicationInstance - .serviceClusters().iterator().next() - .serviceInstances().iterator().next() - .serviceStatus()); - - assertTrue(configServerList.contains( - applicationInstance - .serviceClusters().iterator().next() - .serviceInstances().iterator().next() - .hostName() - .toString())); - } - -} \ No newline at end of file diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/ConfigserverUtil.java b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/ConfigserverUtil.java new file mode 100644 index 00000000000..68a55d41b19 --- /dev/null +++ b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/ConfigserverUtil.java @@ -0,0 +1,44 @@ +// 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.monitor.internal; + +import com.yahoo.cloud.config.ConfigserverConfig; +import com.yahoo.config.model.api.ApplicationInfo; +import com.yahoo.vespa.service.monitor.application.ConfigServerApplication; + +/** + * @author hakon + */ +public class ConfigserverUtil { + /** Create a ConfigserverConfig with the given settings. */ + public static ConfigserverConfig create( + boolean nodeAdminInContainer, + String configServerHostname1, + String configServerHostname2, + String configServerHostname3) { + return new ConfigserverConfig( + new ConfigserverConfig.Builder() + .nodeAdminInContainer(nodeAdminInContainer) + .zookeeperserver(new ConfigserverConfig.Zookeeperserver.Builder().hostname(configServerHostname1).port(1)) + .zookeeperserver(new ConfigserverConfig.Zookeeperserver.Builder().hostname(configServerHostname2).port(2)) + .zookeeperserver(new ConfigserverConfig.Zookeeperserver.Builder().hostname(configServerHostname3).port(3))); + } + + public static ConfigserverConfig createExampleConfigserverConfig(boolean nodeAdminInContainer) { + return create(nodeAdminInContainer, "cfg1", "cfg2", "cfg3"); + } + + public static ApplicationInfo makeConfigServerApplicationInfo( + String configServerHostname1, + String configServerHostname2, + String configServerHostname3) { + return ConfigServerApplication.CONFIG_SERVER_APPLICATION.makeApplicationInfo(create( + true, + configServerHostname1, + configServerHostname2, + configServerHostname3)); + } + + public static ApplicationInfo makeExampleConfigServer() { + return makeConfigServerApplicationInfo("cfg1", "cfg2", "cfg3"); + } +} diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/DuperModelTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/DuperModelTest.java new file mode 100644 index 00000000000..0a68b4b0ff7 --- /dev/null +++ b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/DuperModelTest.java @@ -0,0 +1,40 @@ +// 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.monitor.internal; + +import com.yahoo.cloud.config.ConfigserverConfig; +import com.yahoo.config.model.api.ApplicationInfo; +import com.yahoo.config.model.api.SuperModel; +import com.yahoo.vespa.applicationmodel.ServiceStatus; +import com.yahoo.vespa.service.monitor.ServiceStatusProvider; +import com.yahoo.vespa.service.monitor.application.ConfigServerApplication; +import org.junit.Test; + +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * @author hakon + */ +public class DuperModelTest { + private final ServiceStatusProvider statusProvider = mock(ServiceStatusProvider.class); + + @Test + public void toApplicationInstance() { + when(statusProvider.getStatus(any(), any(), any(), any())).thenReturn(ServiceStatus.NOT_CHECKED); + ConfigserverConfig config = ConfigserverUtil.createExampleConfigserverConfig(true); + DuperModel duperModel = new DuperModel(config); + SuperModel superModel = mock(SuperModel.class); + ApplicationInfo superModelApplicationInfo = mock(ApplicationInfo.class); + when(superModel.getAllApplicationInfos()).thenReturn(Collections.singletonList(superModelApplicationInfo)); + List applicationInfos = duperModel.getApplicationInfos(superModel); + assertEquals(2, applicationInfos.size()); + assertEquals(ConfigServerApplication.CONFIG_SERVER_APPLICATION.getApplicationId(), applicationInfos.get(0).getApplicationId()); + assertSame(superModelApplicationInfo, applicationInfos.get(1)); + } +} diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/ModelGeneratorTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/ModelGeneratorTest.java index a21691ee4d0..07cfa124434 100644 --- a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/ModelGeneratorTest.java +++ b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/ModelGeneratorTest.java @@ -1,6 +1,7 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.service.monitor.internal; +import com.yahoo.cloud.config.ConfigserverConfig; import com.yahoo.config.model.api.SuperModel; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.RegionName; @@ -15,13 +16,9 @@ import com.yahoo.vespa.service.monitor.application.ConfigServerApplication; import com.yahoo.vespa.service.monitor.internal.slobrok.SlobrokMonitorManagerImpl; import org.junit.Test; -import java.util.Collections; import java.util.Iterator; -import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; @@ -35,13 +32,13 @@ public class ModelGeneratorTest { private final int PORT = 2; @Test - public void toApplicationModelWithConfigServerApplication() throws Exception { - SuperModel superModel = - ExampleModel.createExampleSuperModelWithOneRpcPort(HOSTNAME, PORT); + public void toApplicationModel() throws Exception { + SuperModel superModel = ExampleModel.createExampleSuperModelWithOneRpcPort(HOSTNAME, PORT); - List configServerHosts = Stream.of("cfg1", "cfg2", "cfg3") - .collect(Collectors.toList()); - ModelGenerator modelGenerator = new ModelGenerator(configServerHosts); + ConfigserverConfig config = ConfigserverUtil.create( + true, "cfg1", "cfg2", "cfg3"); + DuperModel duperModel = new DuperModel(config); + ModelGenerator modelGenerator = new ModelGenerator(); Zone zone = new Zone(Environment.from(ENVIRONMENT), RegionName.from(REGION)); @@ -51,7 +48,7 @@ public class ModelGeneratorTest { ServiceModel serviceModel = modelGenerator.toServiceModel( - superModel, + duperModel.getApplicationInfos(superModel), zone, slobrokMonitorManager); @@ -78,32 +75,6 @@ public class ModelGeneratorTest { } } - @Test - public void toApplicationModel() throws Exception { - SuperModel superModel = - ExampleModel.createExampleSuperModelWithOneRpcPort(HOSTNAME, PORT); - ModelGenerator modelGenerator = new ModelGenerator(Collections.emptyList()); - - Zone zone = new Zone(Environment.from(ENVIRONMENT), RegionName.from(REGION)); - - SlobrokMonitorManagerImpl slobrokMonitorManager = mock(SlobrokMonitorManagerImpl.class); - when(slobrokMonitorManager.getStatus(any(), any(), any(), any())) - .thenReturn(ServiceStatus.UP); - - ServiceModel serviceModel = - modelGenerator.toServiceModel( - superModel, - zone, - slobrokMonitorManager); - - Map applicationInstances = - serviceModel.getAllApplicationInstances(); - - assertEquals(1, applicationInstances.size()); - verifyOtherApplication(applicationInstances.values().iterator().next()); - } - private void verifyOtherApplication(ApplicationInstance applicationInstance) { assertEquals(String.format("%s:%s:%s:%s:%s", ExampleModel.TENANT, diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/SuperModelListenerImplTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/SuperModelListenerImplTest.java index 83bad0ddb2a..eb6d6d583f7 100644 --- a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/SuperModelListenerImplTest.java +++ b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/SuperModelListenerImplTest.java @@ -14,6 +14,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -22,11 +23,13 @@ public class SuperModelListenerImplTest { public void sanityCheck() { SlobrokMonitorManagerImpl slobrokMonitorManager = mock(SlobrokMonitorManagerImpl.class); ServiceMonitorMetrics metrics = mock(ServiceMonitorMetrics.class); + DuperModel duperModel = mock(DuperModel.class); ModelGenerator modelGenerator = mock(ModelGenerator.class); Zone zone = mock(Zone.class); SuperModelListenerImpl listener = new SuperModelListenerImpl( slobrokMonitorManager, metrics, + duperModel, modelGenerator, zone); @@ -38,13 +41,15 @@ public class SuperModelListenerImplTest { ApplicationInfo application2 = mock(ApplicationInfo.class); List applications = Stream.of(application1, application2) .collect(Collectors.toList()); - when(superModel.getAllApplicationInfos()).thenReturn(applications); + when(duperModel.getApplicationInfos(superModel)).thenReturn(applications); listener.start(superModelProvider); - verify(slobrokMonitorManager).applicationActivated(superModel, application1); - verify(slobrokMonitorManager).applicationActivated(superModel, application2); + verify(duperModel, times(1)).getApplicationInfos(superModel); + verify(slobrokMonitorManager).applicationActivated(application1); + verify(slobrokMonitorManager).applicationActivated(application2); ServiceModel serviceModel = listener.get(); - verify(modelGenerator).toServiceModel(superModel, zone, slobrokMonitorManager); + verify(duperModel, times(2)).getApplicationInfos(superModel); + verify(modelGenerator).toServiceModel(applications, zone, slobrokMonitorManager); } } \ No newline at end of file diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/UnionMonitorManagerTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/UnionMonitorManagerTest.java index b7c3ed8e1e1..79916e43712 100644 --- a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/UnionMonitorManagerTest.java +++ b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/UnionMonitorManagerTest.java @@ -1,95 +1,44 @@ // 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.monitor.internal; -import com.yahoo.cloud.config.ConfigserverConfig; -import com.yahoo.config.provision.ApplicationId; -import com.yahoo.vespa.applicationmodel.ClusterId; import com.yahoo.vespa.applicationmodel.ConfigId; -import com.yahoo.vespa.applicationmodel.ServiceType; +import com.yahoo.vespa.applicationmodel.ServiceStatus; import com.yahoo.vespa.service.monitor.internal.health.HealthMonitorManager; import com.yahoo.vespa.service.monitor.internal.slobrok.SlobrokMonitorManagerImpl; import org.junit.Test; import static com.yahoo.vespa.applicationmodel.ClusterId.NODE_ADMIN; +import static com.yahoo.vespa.applicationmodel.ServiceStatus.*; +import static com.yahoo.vespa.applicationmodel.ServiceStatus.NOT_CHECKED; +import static com.yahoo.vespa.applicationmodel.ServiceStatus.UP; import static com.yahoo.vespa.applicationmodel.ServiceType.CONTAINER; import static com.yahoo.vespa.service.monitor.application.ZoneApplication.ZONE_APPLICATION_ID; +import static org.junit.Assert.assertSame; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class UnionMonitorManagerTest { - @Test - public void nodeAdminInContainer() { - testWith( - true, - ZONE_APPLICATION_ID, - NODE_ADMIN, - CONTAINER, - 1, - 0); - } - - @Test - public void nodeAdminOutsideContainer() { - boolean inContainer = false; - - // When nodeAdminInContainer is set, then only the node admin cluster should use health - testWith( - inContainer, - ZONE_APPLICATION_ID, - NODE_ADMIN, - CONTAINER, - 0, - 1); - - testWith( - inContainer, - ApplicationId.fromSerializedForm("a:b:default"), - NODE_ADMIN, - CONTAINER, - 1, - 0); + private final SlobrokMonitorManagerImpl slobrokMonitorManager = mock(SlobrokMonitorManagerImpl.class); + private final HealthMonitorManager healthMonitorManager = mock(HealthMonitorManager.class); - testWith( - inContainer, - ZONE_APPLICATION_ID, - new ClusterId("foo"), - CONTAINER, - 1, - 0); + private final UnionMonitorManager manager = new UnionMonitorManager( + slobrokMonitorManager, + healthMonitorManager); - testWith( - inContainer, - ZONE_APPLICATION_ID, - NODE_ADMIN, - new ServiceType("foo"), - 1, - 0); + @Test + public void verifyHealthTakesPriority() { + testWith(UP, DOWN, UP); + testWith(NOT_CHECKED, DOWN, DOWN); + testWith(NOT_CHECKED, NOT_CHECKED, NOT_CHECKED); } - private void testWith(boolean nodeAdminInContainer, - ApplicationId applicationId, - ClusterId clusterId, - ServiceType serviceType, - int expectedSlobrokCalls, - int expectedHealthCalls) { - SlobrokMonitorManagerImpl slobrokMonitorManager = mock(SlobrokMonitorManagerImpl.class); - HealthMonitorManager healthMonitorManager = mock(HealthMonitorManager.class); - - ConfigserverConfig.Builder builder = new ConfigserverConfig.Builder(); - builder.nodeAdminInContainer(nodeAdminInContainer); - ConfigserverConfig config = new ConfigserverConfig(builder); - - - UnionMonitorManager manager = new UnionMonitorManager( - slobrokMonitorManager, - healthMonitorManager, - config); - - manager.getStatus(applicationId, clusterId, serviceType, new ConfigId("config-id")); - - verify(slobrokMonitorManager, times(expectedSlobrokCalls)).getStatus(any(), any(), any(), any()); - verify(healthMonitorManager, times(expectedHealthCalls)).getStatus(any(), any(), any(), any()); + private void testWith(ServiceStatus healthStatus, + ServiceStatus slobrokStatus, + ServiceStatus expectedStatus) { + when(healthMonitorManager.getStatus(any(), any(), any(), any())).thenReturn(healthStatus); + when(slobrokMonitorManager.getStatus(any(), any(), any(), any())).thenReturn(slobrokStatus); + ServiceStatus status = manager.getStatus(ZONE_APPLICATION_ID, NODE_ADMIN, CONTAINER, new ConfigId("config-id")); + assertSame(expectedStatus, status); } } \ No newline at end of file diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/ApplicationHealthMonitorTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/ApplicationHealthMonitorTest.java new file mode 100644 index 00000000000..51b0503565f --- /dev/null +++ b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/ApplicationHealthMonitorTest.java @@ -0,0 +1,24 @@ +// 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.monitor.internal.health; + +import com.yahoo.vespa.applicationmodel.ServiceStatus; +import com.yahoo.vespa.service.monitor.application.ConfigServerApplication; +import com.yahoo.vespa.service.monitor.internal.ConfigserverUtil; +import org.junit.Test; + +import static com.yahoo.vespa.applicationmodel.ServiceStatus.NOT_CHECKED; +import static org.junit.Assert.assertEquals; + +public class ApplicationHealthMonitorTest { + @Test + public void sanityCheck() { + ApplicationHealthMonitor monitor = ApplicationHealthMonitor.startMonitoring( + ConfigserverUtil.makeExampleConfigServer()); + ServiceStatus status = monitor.getStatus( + ConfigServerApplication.CONFIG_SERVER_APPLICATION.getApplicationId(), + ConfigServerApplication.CLUSTER_ID, + ConfigServerApplication.SERVICE_TYPE, + ConfigServerApplication.configIdFrom(0)); + assertEquals(NOT_CHECKED, status); + } +} \ No newline at end of file diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorManagerTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorManagerTest.java new file mode 100644 index 00000000000..97963393268 --- /dev/null +++ b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorManagerTest.java @@ -0,0 +1,49 @@ +// 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.monitor.internal.health; + +import com.yahoo.cloud.config.ConfigserverConfig; +import com.yahoo.config.model.api.ApplicationInfo; +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.monitor.application.ZoneApplication; +import com.yahoo.vespa.service.monitor.internal.ConfigserverUtil; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class HealthMonitorManagerTest { + @Test + public void addRemove() { + ConfigserverConfig config = ConfigserverUtil.createExampleConfigserverConfig(true); + HealthMonitorManager manager = new HealthMonitorManager(config); + ApplicationInfo applicationInfo = ConfigserverUtil.makeExampleConfigServer(); + manager.applicationActivated(applicationInfo); + manager.applicationRemoved(applicationInfo.getApplicationId()); + } + + @Test + public void withNodeAdmin() { + ConfigserverConfig config = ConfigserverUtil.createExampleConfigserverConfig(true); + HealthMonitorManager manager = new HealthMonitorManager(config); + ServiceStatus status = manager.getStatus( + ZoneApplication.ZONE_APPLICATION_ID, + ClusterId.NODE_ADMIN, + ServiceType.CONTAINER, + new ConfigId("config-id-1")); + assertEquals(ServiceStatus.NOT_CHECKED, status); + } + + @Test + public void withHostAdmin() { + ConfigserverConfig config = ConfigserverUtil.createExampleConfigserverConfig(false); + HealthMonitorManager manager = new HealthMonitorManager(config); + ServiceStatus status = manager.getStatus( + ZoneApplication.ZONE_APPLICATION_ID, + ClusterId.NODE_ADMIN, + ServiceType.CONTAINER, + new ConfigId("config-id-1")); + assertEquals(ServiceStatus.UP, status); + } +} \ No newline at end of file diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorTest.java new file mode 100644 index 00000000000..cca1530ad97 --- /dev/null +++ b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/health/HealthMonitorTest.java @@ -0,0 +1,21 @@ +// 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.monitor.internal.health; + +import com.yahoo.vespa.applicationmodel.ServiceStatus; +import org.junit.Test; + +import java.net.MalformedURLException; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + +public class HealthMonitorTest { + @Test + public void basicTests() throws MalformedURLException { + HealthClient healthClient = mock(HealthClient.class); + try (HealthMonitor monitor = new HealthMonitor(healthClient)) { + monitor.startMonitoring(); + assertEquals(ServiceStatus.NOT_CHECKED, monitor.getStatus()); + } + } +} \ No newline at end of file diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/slobrok/SlobrokMonitorManagerImplTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/slobrok/SlobrokMonitorManagerImplTest.java index 8e4443df83b..a567559980b 100644 --- a/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/slobrok/SlobrokMonitorManagerImplTest.java +++ b/service-monitor/src/test/java/com/yahoo/vespa/service/monitor/internal/slobrok/SlobrokMonitorManagerImplTest.java @@ -2,7 +2,7 @@ package com.yahoo.vespa.service.monitor.internal.slobrok; import com.yahoo.config.model.api.ApplicationInfo; -import com.yahoo.config.model.api.SuperModel; +import com.yahoo.config.provision.ApplicationId; import com.yahoo.vespa.applicationmodel.ClusterId; import com.yahoo.vespa.applicationmodel.ConfigId; import com.yahoo.vespa.applicationmodel.ServiceStatus; @@ -28,18 +28,19 @@ public class SlobrokMonitorManagerImplTest { private final SlobrokMonitorManagerImpl slobrokMonitorManager = new SlobrokMonitorManagerImpl(slobrokMonitorFactory); private final SlobrokMonitor slobrokMonitor = mock(SlobrokMonitor.class); - private final SuperModel superModel = mock(SuperModel.class); + private final ApplicationId applicationId = ApplicationId.from("tenant", "app", "instance"); private final ApplicationInfo application = mock(ApplicationInfo.class); private final ClusterId clusterId = new ClusterId("cluster-id"); @Before public void setup() { when(slobrokMonitorFactory.get()).thenReturn(slobrokMonitor); + when(application.getApplicationId()).thenReturn(applicationId); } @Test public void testActivationOfApplication() { - slobrokMonitorManager.applicationActivated(superModel, application); + slobrokMonitorManager.applicationActivated(application); verify(slobrokMonitorFactory, times(1)).get(); } @@ -51,14 +52,14 @@ public class SlobrokMonitorManagerImplTest { @Test public void testGetStatus_ApplicationInSlobrok() { - slobrokMonitorManager.applicationActivated(superModel, application); + slobrokMonitorManager.applicationActivated(application); when(slobrokMonitor.registeredInSlobrok("config.id")).thenReturn(true); assertEquals(ServiceStatus.UP, getStatus("topleveldispatch")); } @Test public void testGetStatus_ServiceNotInSlobrok() { - slobrokMonitorManager.applicationActivated(superModel, application); + slobrokMonitorManager.applicationActivated(application); when(slobrokMonitor.registeredInSlobrok("config.id")).thenReturn(false); assertEquals(ServiceStatus.DOWN, getStatus("topleveldispatch")); } -- cgit v1.2.3