diff options
author | Håkon Hallingstad <hakon@yahoo-inc.com> | 2017-04-28 17:29:57 +0200 |
---|---|---|
committer | Håkon Hallingstad <hakon@yahoo-inc.com> | 2017-04-28 17:29:57 +0200 |
commit | 019a33d591366a025fa012951e9dbac21f83c1de (patch) | |
tree | fa65aa8ecf5a139eff855eabc22b8ee86eab9565 /orchestrator/src/test | |
parent | c00a3b8a6318e7dd6c7c09b8d87042734dfdc382 (diff) |
Adds classes to give the Orchestrator policy classes a simplified view of Vespa.
This should be a no-op. The only changes that actually could have an impact
are the changes to getting the cluster controllers, but it should be
functionally equivalent.
This PR will make it easier to change the Orchestrator policy to allow
suspending several nodes (NodeGroup) in an application on a single Docker host.
Diffstat (limited to 'orchestrator/src/test')
9 files changed, 605 insertions, 31 deletions
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/DummyInstanceLookupService.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/DummyInstanceLookupService.java index d48bc1f302b..dede55f402a 100644 --- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/DummyInstanceLookupService.java +++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/DummyInstanceLookupService.java @@ -12,6 +12,7 @@ import com.yahoo.vespa.applicationmodel.ServiceCluster; import com.yahoo.vespa.applicationmodel.ServiceInstance; import com.yahoo.vespa.applicationmodel.ServiceType; import com.yahoo.vespa.applicationmodel.TenantId; +import com.yahoo.vespa.orchestrator.model.VespaModelUtil; import com.yahoo.vespa.service.monitor.ServiceMonitorStatus; import java.util.HashSet; diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientFactoryMock.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientFactoryMock.java index 4fc9ad2d56d..93dcb5815c6 100644 --- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientFactoryMock.java +++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientFactoryMock.java @@ -4,15 +4,14 @@ package com.yahoo.vespa.orchestrator.controller; import com.yahoo.vespa.applicationmodel.ApplicationInstance; import com.yahoo.vespa.applicationmodel.ClusterId; import com.yahoo.vespa.applicationmodel.HostName; -import com.yahoo.vespa.applicationmodel.ServiceInstance; import com.yahoo.vespa.orchestrator.DummyInstanceLookupService; -import com.yahoo.vespa.orchestrator.VespaModelUtil; +import com.yahoo.vespa.orchestrator.model.VespaModelUtil; import com.yahoo.vespa.service.monitor.ServiceMonitorStatus; import java.io.IOException; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -50,7 +49,7 @@ public class ClusterControllerClientFactoryMock implements ClusterControllerClie } @Override - public ClusterControllerClient createClient(Collection<? extends ServiceInstance<?>> clusterControllers, String clusterName) { + public ClusterControllerClient createClient(List<HostName> clusterControllers, String clusterName) { return new ClusterControllerClient() { @Override diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/SingleInstanceClusterControllerClientFactoryTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/SingleInstanceClusterControllerClientFactoryTest.java index cc217885047..cde644e7e8b 100644 --- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/SingleInstanceClusterControllerClientFactoryTest.java +++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/SingleInstanceClusterControllerClientFactoryTest.java @@ -1,17 +1,14 @@ // Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.orchestrator.controller; -import com.yahoo.vespa.jaxrs.client.JaxRsClientFactory; -import com.yahoo.vespa.orchestrator.TestUtil; import com.yahoo.vespa.applicationmodel.ConfigId; import com.yahoo.vespa.applicationmodel.HostName; -import com.yahoo.vespa.applicationmodel.ServiceInstance; -import com.yahoo.vespa.service.monitor.ServiceMonitorStatus; +import com.yahoo.vespa.jaxrs.client.JaxRsClientFactory; import org.junit.Before; import org.junit.Test; -import java.util.Collection; -import java.util.Collections; +import java.util.Arrays; +import java.util.List; import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.equalTo; @@ -52,7 +49,7 @@ public class SingleInstanceClusterControllerClientFactoryTest { @Test public void testCreateClientWithNoClusterControllerInstances() throws Exception { - final Collection<ServiceInstance<ServiceMonitorStatus>> clusterControllers = Collections.emptySet(); + final List<HostName> clusterControllers = Arrays.asList(); try { clientFactory.createClient(clusterControllers, "clusterName"); @@ -64,8 +61,7 @@ public class SingleInstanceClusterControllerClientFactoryTest { @Test public void testCreateClientWithSingleClusterControllerInstance() throws Exception { - final Collection<ServiceInstance<ServiceMonitorStatus>> clusterControllers = Collections.singleton( - new ServiceInstance<>(clusterControllerConfigId(1), HOST_NAME_1, ServiceMonitorStatus.UP)); + final List<HostName> clusterControllers = Arrays.asList(HOST_NAME_1); clientFactory.createClient(clusterControllers, "clusterName") .setNodeState(0, ClusterControllerState.MAINTENANCE); @@ -78,10 +74,8 @@ public class SingleInstanceClusterControllerClientFactoryTest { } @Test - public void testCreateClientWithTwoNonClusterControllerInstances() throws Exception { - final Collection<ServiceInstance<ServiceMonitorStatus>> clusterControllers = TestUtil.makeServiceInstanceSet( - new ServiceInstance<>(new ConfigId("not-a-cluster-controller-1"), HOST_NAME_1, ServiceMonitorStatus.UP), - new ServiceInstance<>(new ConfigId("not-a-cluster-controller-2"), HOST_NAME_2, ServiceMonitorStatus.UP)); + public void testCreateClientWithoutClusterControllerInstances() throws Exception { + final List<HostName> clusterControllers = Arrays.asList(); try { clientFactory.createClient(clusterControllers, "clusterName"); @@ -93,10 +87,7 @@ public class SingleInstanceClusterControllerClientFactoryTest { @Test public void testCreateClientWithThreeClusterControllerInstances() throws Exception { - final Collection<ServiceInstance<ServiceMonitorStatus>> clusterControllers = TestUtil.makeServiceInstanceSet( - new ServiceInstance<>(clusterControllerConfigId(1), HOST_NAME_1, ServiceMonitorStatus.UP), - new ServiceInstance<>(clusterControllerConfigId(2), HOST_NAME_2, ServiceMonitorStatus.UP), - new ServiceInstance<>(clusterControllerConfigId(3), HOST_NAME_3, ServiceMonitorStatus.UP)); + final List<HostName> clusterControllers = Arrays.asList(HOST_NAME_1, HOST_NAME_2, HOST_NAME_3); clientFactory.createClient(clusterControllers, "clusterName") .setNodeState(0, ClusterControllerState.MAINTENANCE); diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/ApplicationApiImplTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/ApplicationApiImplTest.java new file mode 100644 index 00000000000..684f7e76d94 --- /dev/null +++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/ApplicationApiImplTest.java @@ -0,0 +1,321 @@ +package com.yahoo.vespa.orchestrator.model; + +import com.yahoo.vespa.applicationmodel.ApplicationInstance; +import com.yahoo.vespa.applicationmodel.HostName; +import com.yahoo.vespa.applicationmodel.ServiceType; +import com.yahoo.vespa.orchestrator.status.HostStatus; +import com.yahoo.vespa.service.monitor.ServiceMonitorStatus; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; + +public class ApplicationApiImplTest { + final ModelTestUtils modelUtils = new ModelTestUtils(); + + @Test + public void testApplicationInfo() { + ApplicationApiImpl applicationApi = + modelUtils.createApplicationApiImpl(modelUtils.createApplicationInstance(new ArrayList<>())); + assertEquals("tenant:application-name:foo:bar:default", applicationApi.applicationInfo()); + } + + @Test + public void testGetClustersThatAreOnAtLeastOneNodeInGroup() { + HostName hostName1 = new HostName("host1"); + HostName hostName2 = new HostName("host2"); + HostName hostName3 = new HostName("host3"); + HostName hostName4 = new HostName("host4"); + + ApplicationInstance<ServiceMonitorStatus> applicationInstance = + modelUtils.createApplicationInstance(Arrays.asList( + modelUtils.createServiceCluster( + "cluster-3", + new ServiceType("service-type-3"), + Arrays.asList( + modelUtils.createServiceInstance("config-id-1", hostName1, ServiceMonitorStatus.UP), + modelUtils.createServiceInstance("config-id-2", hostName2, ServiceMonitorStatus.UP) + ) + ), + modelUtils.createServiceCluster( + "cluster-1", + new ServiceType("service-type-1"), + Arrays.asList( + modelUtils.createServiceInstance("config-id-3", hostName1, ServiceMonitorStatus.UP), + modelUtils.createServiceInstance("config-id-4", hostName3, ServiceMonitorStatus.UP) + ) + ), + modelUtils.createServiceCluster( + "cluster-2", + new ServiceType("service-type-2"), + Arrays.asList( + modelUtils.createServiceInstance("config-id-5", hostName1, ServiceMonitorStatus.UP), + modelUtils.createServiceInstance("config-id-6", hostName2, ServiceMonitorStatus.UP) + ) + ) + )); + + verifyClustersInOrder(modelUtils.createApplicationApiImpl(applicationInstance, hostName1), 1, 2, 3); + verifyClustersInOrder(modelUtils.createApplicationApiImpl(applicationInstance, hostName2), 2, 3); + verifyClustersInOrder(modelUtils.createApplicationApiImpl(applicationInstance, hostName3), 1); + verifyClustersInOrder(modelUtils.createApplicationApiImpl(applicationInstance, hostName4)); + } + + private void verifyClustersInOrder(ApplicationApi applicationApi, + Integer... expectedClusterNumbers) { + // Note: we require the clusters to be in order. + List<ClusterApi> clusterApis = applicationApi.getClusters(); + String clusterInfos = clusterApis.stream().map(clusterApi -> clusterApi.clusterInfo()).collect(Collectors.joining(",")); + + String expectedClusterInfos = Arrays.stream(expectedClusterNumbers) + .map(number -> "{ clusterId=cluster-" + number + ", serviceType=service-type-" + number + " }") + .collect(Collectors.joining(",")); + + assertEquals(expectedClusterInfos, clusterInfos); + } + + @Test + public void testGetUpStorageNodesInGroupInClusterOrder() { + HostName hostName1 = new HostName("host1"); + HostName hostName2 = new HostName("host2"); + HostName hostName3 = new HostName("host3"); + HostName hostName4 = new HostName("host4"); + HostName hostName5 = new HostName("host5"); + HostName hostName6 = new HostName("host6"); + HostName hostName7 = new HostName("host7"); + + ApplicationInstance<ServiceMonitorStatus> applicationInstance = + modelUtils.createApplicationInstance(Arrays.asList( + modelUtils.createServiceCluster( + "cluster-3", + VespaModelUtil.STORAGENODE_SERVICE_TYPE, + Arrays.asList( + modelUtils.createServiceInstance("config-id-30", hostName1, ServiceMonitorStatus.UP), + modelUtils.createServiceInstance("config-id-31", hostName2, ServiceMonitorStatus.UP) + ) + ), + modelUtils.createServiceCluster( + "cluster-1", + VespaModelUtil.STORAGENODE_SERVICE_TYPE, + Arrays.asList( + modelUtils.createServiceInstance("config-id-10", hostName3, ServiceMonitorStatus.DOWN), + modelUtils.createServiceInstance("config-id-11", hostName4, ServiceMonitorStatus.UP) + ) + ), + modelUtils.createServiceCluster( + "cluster-4", + new ServiceType("service-type-4"), + Arrays.asList( + modelUtils.createServiceInstance("config-id-40", hostName1, ServiceMonitorStatus.UP), + modelUtils.createServiceInstance("config-id-41", hostName2, ServiceMonitorStatus.UP), + modelUtils.createServiceInstance("config-id-42", hostName3, ServiceMonitorStatus.UP), + modelUtils.createServiceInstance("config-id-43", hostName5, ServiceMonitorStatus.UP) + ) + ), + modelUtils.createServiceCluster( + "cluster-2", + VespaModelUtil.STORAGENODE_SERVICE_TYPE, + Arrays.asList( + modelUtils.createServiceInstance("config-id-20", hostName6, ServiceMonitorStatus.DOWN), + modelUtils.createServiceInstance("config-id-21", hostName7, ServiceMonitorStatus.UP) + ) + ) + )); + + verifyUpStorageNodesInOrder(modelUtils.createApplicationApiImpl(applicationInstance, hostName1), hostName1); + verifyUpStorageNodesInOrder(modelUtils.createApplicationApiImpl(applicationInstance, hostName2), hostName2); + verifyUpStorageNodesInOrder(modelUtils.createApplicationApiImpl(applicationInstance, hostName3)); // host3 is DOWN + verifyUpStorageNodesInOrder(modelUtils.createApplicationApiImpl(applicationInstance, hostName4), hostName4); + verifyUpStorageNodesInOrder(modelUtils.createApplicationApiImpl(applicationInstance, hostName5)); // not a storage cluster + + verifyUpStorageNodesInOrder(modelUtils.createApplicationApiImpl(applicationInstance, hostName1, hostName3), hostName1); + + // For the node group (host1, host4), they both have an up storage node (service instance) + // with clusters (cluster-3, cluster-1) respectively, and so the order of the hosts are reversed + // (host4, host1) when sorted by the clusters. + verifyUpStorageNodesInOrder(modelUtils.createApplicationApiImpl(applicationInstance, hostName1, hostName4), hostName4, hostName1); + + verifyUpStorageNodesInOrder(modelUtils.createApplicationApiImpl( + applicationInstance, hostName1, hostName4, hostName5), hostName4, hostName1); + verifyUpStorageNodesInOrder(modelUtils.createApplicationApiImpl( + applicationInstance, hostName1, hostName4, hostName5, hostName6), hostName4, hostName1); + verifyUpStorageNodesInOrder(modelUtils.createApplicationApiImpl( + applicationInstance, hostName1, hostName4, hostName5, hostName7), hostName4, hostName7, hostName1); + } + + private void verifyUpStorageNodesInOrder(ApplicationApi applicationApi, + HostName... expectedHostNames) { + List<HostName> upStorageNodes = applicationApi.getUpStorageNodesInGroupInClusterOrder().stream() + .map(storageNode -> storageNode.hostName()) + .collect(Collectors.toList()); + assertEquals(Arrays.asList(expectedHostNames), upStorageNodes); + } + + @Test + public void testUpConditionOfStorageNode() { + verifyUpConditionWith(HostStatus.NO_REMARKS, ServiceMonitorStatus.UP, true); + verifyUpConditionWith(HostStatus.NO_REMARKS, ServiceMonitorStatus.NOT_CHECKED, true); + verifyUpConditionWith(HostStatus.NO_REMARKS, ServiceMonitorStatus.DOWN, false); + verifyUpConditionWith(HostStatus.ALLOWED_TO_BE_DOWN, ServiceMonitorStatus.UP, false); + verifyUpConditionWith(HostStatus.ALLOWED_TO_BE_DOWN, ServiceMonitorStatus.NOT_CHECKED, false); + verifyUpConditionWith(HostStatus.ALLOWED_TO_BE_DOWN, ServiceMonitorStatus.DOWN, false); + } + + private void verifyUpConditionWith(HostStatus hostStatus, ServiceMonitorStatus serviceStatus, boolean expectUp) { + HostName hostName1 = modelUtils.createNode("host1", hostStatus); + + ApplicationInstance<ServiceMonitorStatus> applicationInstance = + modelUtils.createApplicationInstance(Arrays.asList( + modelUtils.createServiceCluster( + "cluster-1", + VespaModelUtil.STORAGENODE_SERVICE_TYPE, + Arrays.asList(modelUtils.createServiceInstance("config-id-1", hostName1, serviceStatus)) + ) + )); + + ApplicationApiImpl applicationApi = modelUtils.createApplicationApiImpl(applicationInstance, hostName1); + List<HostName> upStorageNodes = expectUp ? Arrays.asList(hostName1) : new ArrayList<>(); + + List<HostName> actualStorageNodes = applicationApi.getUpStorageNodesInGroupInClusterOrder().stream() + .map(storageNode -> storageNode.hostName()) + .collect(Collectors.toList()); + assertEquals(upStorageNodes, actualStorageNodes); + } + + @Test + public void testGetNodesInGroupWithStatus() { + HostName hostName1 = modelUtils.createNode("host1", HostStatus.NO_REMARKS); + HostName hostName2 = modelUtils.createNode("host2", HostStatus.NO_REMARKS); + HostName hostName3 = modelUtils.createNode("host3", HostStatus.ALLOWED_TO_BE_DOWN); + + ApplicationInstance<ServiceMonitorStatus> applicationInstance = + modelUtils.createApplicationInstance(Arrays.asList( + modelUtils.createServiceCluster( + "cluster-1", + new ServiceType("service-type-1"), + Arrays.asList( + modelUtils.createServiceInstance("config-id-10", hostName1, ServiceMonitorStatus.UP), + modelUtils.createServiceInstance("config-id-11", hostName2, ServiceMonitorStatus.UP) + ) + ), + modelUtils.createServiceCluster( + "cluster-2", + new ServiceType("service-type-2"), + Arrays.asList( + modelUtils.createServiceInstance("config-id-20", hostName1, ServiceMonitorStatus.UP), + modelUtils.createServiceInstance("config-id-21", hostName3, ServiceMonitorStatus.UP) + ) + ) + )); + + verifyNodesInGroupWithoutRemarks( + modelUtils.createApplicationApiImpl(applicationInstance, hostName1), + Arrays.asList(hostName1), + Arrays.asList()); + verifyNodesInGroupWithoutRemarks( + modelUtils.createApplicationApiImpl(applicationInstance, hostName1, hostName2), + Arrays.asList(hostName1, hostName2), + Arrays.asList()); + verifyNodesInGroupWithoutRemarks( + modelUtils.createApplicationApiImpl(applicationInstance, hostName1, hostName2, hostName3), + Arrays.asList(hostName1, hostName2), + Arrays.asList(hostName3)); + verifyNodesInGroupWithoutRemarks( + modelUtils.createApplicationApiImpl(applicationInstance, hostName3), + Arrays.asList(), + Arrays.asList(hostName3)); + } + + private void verifyNodesInGroupWithoutRemarks(ApplicationApi applicationApi, + List<HostName> noRemarksHostNames, + List<HostName> allowedToBeDownHostNames) { + List<HostName> actualNoRemarksHosts = applicationApi.getNodesInGroupWithStatus(HostStatus.NO_REMARKS); + assertEquals(noRemarksHostNames, actualNoRemarksHosts); + List<HostName> actualAllowedToBeDownHosts = applicationApi.getNodesInGroupWithStatus(HostStatus.ALLOWED_TO_BE_DOWN); + assertEquals(allowedToBeDownHostNames, actualAllowedToBeDownHosts); + } + + @Test + public void testGetStorageNodesAllowedToBeDownInGroupInReverseClusterOrder() { + HostName allowedToBeDownHost1 = modelUtils.createNode("host1", HostStatus.ALLOWED_TO_BE_DOWN); + HostName noRemarksHost2 = modelUtils.createNode("host2", HostStatus.NO_REMARKS); + HostName allowedToBeDownHost3 = modelUtils.createNode("host3", HostStatus.ALLOWED_TO_BE_DOWN); + HostName allowedToBeDownHost4 = modelUtils.createNode("host4", HostStatus.ALLOWED_TO_BE_DOWN); + HostName noRemarksHost5 = modelUtils.createNode("host5", HostStatus.ALLOWED_TO_BE_DOWN); + HostName noRemarksHost6 = modelUtils.createNode("host6", HostStatus.NO_REMARKS); + HostName allowedToBeDownHost7 = modelUtils.createNode("host7", HostStatus.ALLOWED_TO_BE_DOWN); + + ApplicationInstance<ServiceMonitorStatus> applicationInstance = + modelUtils.createApplicationInstance(Arrays.asList( + modelUtils.createServiceCluster( + "cluster-4", + VespaModelUtil.STORAGENODE_SERVICE_TYPE, + Arrays.asList( + modelUtils.createServiceInstance("config-id-40", allowedToBeDownHost1, ServiceMonitorStatus.UP), + modelUtils.createServiceInstance("config-id-41", noRemarksHost2, ServiceMonitorStatus.DOWN) + ) + ), + modelUtils.createServiceCluster( + "cluster-1", + new ServiceType("service-type-1"), + Arrays.asList( + modelUtils.createServiceInstance("config-id-10", allowedToBeDownHost1, ServiceMonitorStatus.UP), + modelUtils.createServiceInstance("config-id-11", allowedToBeDownHost3, ServiceMonitorStatus.UP) + ) + ), + modelUtils.createServiceCluster( + "cluster-3", + VespaModelUtil.STORAGENODE_SERVICE_TYPE, + Arrays.asList( + modelUtils.createServiceInstance("config-id-30", allowedToBeDownHost4, ServiceMonitorStatus.UP), + modelUtils.createServiceInstance("config-id-31", noRemarksHost5, ServiceMonitorStatus.UP) + ) + ), + modelUtils.createServiceCluster( + "cluster-2", + VespaModelUtil.STORAGENODE_SERVICE_TYPE, + Arrays.asList( + modelUtils.createServiceInstance("config-id-20", noRemarksHost6, ServiceMonitorStatus.UP), + modelUtils.createServiceInstance("config-id-21", allowedToBeDownHost7, ServiceMonitorStatus.UP) + ) + ) + )); + + verifyStorageNodesAllowedToBeDown( + modelUtils.createApplicationApiImpl(applicationInstance, allowedToBeDownHost1), allowedToBeDownHost1); + verifyStorageNodesAllowedToBeDown( + modelUtils.createApplicationApiImpl(applicationInstance, noRemarksHost2)); + verifyStorageNodesAllowedToBeDown( + modelUtils.createApplicationApiImpl(applicationInstance, allowedToBeDownHost3)); + + verifyStorageNodesAllowedToBeDown( + modelUtils.createApplicationApiImpl(applicationInstance, allowedToBeDownHost1, noRemarksHost6), allowedToBeDownHost1); + + // allowedToBeDownHost4 is in cluster-3, while allowedToBeDownHost1 is in cluster-4, so allowedToBeDownHost4 should be ordered + // before allowedToBeDownHost1. + verifyStorageNodesAllowedToBeDown( + modelUtils.createApplicationApiImpl(applicationInstance, allowedToBeDownHost1, noRemarksHost6, allowedToBeDownHost4), + allowedToBeDownHost4, allowedToBeDownHost1); + + verifyStorageNodesAllowedToBeDown( + modelUtils.createApplicationApiImpl(applicationInstance, allowedToBeDownHost1, allowedToBeDownHost4, allowedToBeDownHost7), + allowedToBeDownHost7, allowedToBeDownHost4, allowedToBeDownHost1); + + verifyStorageNodesAllowedToBeDown( + modelUtils.createApplicationApiImpl(applicationInstance, allowedToBeDownHost4, allowedToBeDownHost1, allowedToBeDownHost7), + allowedToBeDownHost7, allowedToBeDownHost4, allowedToBeDownHost1); + } + + private void verifyStorageNodesAllowedToBeDown( + ApplicationApi applicationApi, HostName... hostNames) { + List<HostName> actualStorageNodes = + applicationApi.getStorageNodesAllowedToBeDownInGroupInReverseClusterOrder().stream() + .map(storageNode -> storageNode.hostName()) + .collect(Collectors.toList()); + assertEquals(Arrays.asList(hostNames), actualStorageNodes); + } +} diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/ClusterApiImplTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/ClusterApiImplTest.java new file mode 100644 index 00000000000..082002b7cf2 --- /dev/null +++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/ClusterApiImplTest.java @@ -0,0 +1,133 @@ +package com.yahoo.vespa.orchestrator.model; + +import com.yahoo.vespa.applicationmodel.HostName; +import com.yahoo.vespa.applicationmodel.ServiceCluster; +import com.yahoo.vespa.applicationmodel.ServiceType; +import com.yahoo.vespa.orchestrator.status.HostStatus; +import com.yahoo.vespa.service.monitor.ServiceMonitorStatus; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class ClusterApiImplTest { + final ModelTestUtils modelUtils = new ModelTestUtils(); + + @Test + public void testServicesDownAndNotInGroup() { + HostName hostName1 = modelUtils.createNode("host1", HostStatus.NO_REMARKS); + HostName hostName2 = modelUtils.createNode("host2", HostStatus.NO_REMARKS); + HostName hostName3 = modelUtils.createNode("host3", HostStatus.ALLOWED_TO_BE_DOWN); + HostName hostName4 = modelUtils.createNode("host4", HostStatus.ALLOWED_TO_BE_DOWN); + HostName hostName5 = modelUtils.createNode("host5", HostStatus.NO_REMARKS); + + + ServiceCluster<ServiceMonitorStatus> serviceCluster = modelUtils.createServiceCluster( + "cluster", + new ServiceType("service-type"), + Arrays.asList( + modelUtils.createServiceInstance("service-1", hostName1, ServiceMonitorStatus.UP), + modelUtils.createServiceInstance("service-2", hostName2, ServiceMonitorStatus.DOWN), + modelUtils.createServiceInstance("service-3", hostName3, ServiceMonitorStatus.UP), + modelUtils.createServiceInstance("service-4", hostName4, ServiceMonitorStatus.DOWN), + modelUtils.createServiceInstance("service-5", hostName5, ServiceMonitorStatus.UP) + ) + ); + + ClusterApiImpl clusterApi = new ClusterApiImpl( + serviceCluster, + new NodeGroup(modelUtils.createApplicationInstance(new ArrayList<>()), hostName5), + modelUtils.getHostStatusMap(), + modelUtils.getClusterControllerClientFactory()); + + assertEquals("{ clusterId=cluster, serviceType=service-type }", clusterApi.clusterInfo()); + assertFalse(clusterApi.isStorageCluster()); + assertEquals("[ServiceInstance{configId=service-2, hostName=host2, serviceStatus=DOWN}, " + + "ServiceInstance{configId=service-3, hostName=host3, serviceStatus=UP}, " + + "ServiceInstance{configId=service-4, hostName=host4, serviceStatus=DOWN}]", + clusterApi.servicesDownAndNotInGroupDescription()); + assertEquals("[host3, host4]", + clusterApi.nodesAllowedToBeDownNotInGroupDescription()); + assertEquals(60, clusterApi.percentageOfServicesDown()); + assertEquals(80, clusterApi.percentageOfServicesDownIfGroupIsAllowedToBeDown()); + } + + @Test + public void testNoServices() { + HostName hostName1 = modelUtils.createNode("host1", HostStatus.NO_REMARKS); + HostName hostName2 = modelUtils.createNode("host2", HostStatus.NO_REMARKS); + HostName hostName3 = modelUtils.createNode("host3", HostStatus.ALLOWED_TO_BE_DOWN); + HostName hostName4 = modelUtils.createNode("host4", HostStatus.ALLOWED_TO_BE_DOWN); + HostName hostName5 = modelUtils.createNode("host5", HostStatus.NO_REMARKS); + + + ServiceCluster<ServiceMonitorStatus> serviceCluster = modelUtils.createServiceCluster( + "cluster", + new ServiceType("service-type"), + Arrays.asList( + modelUtils.createServiceInstance("service-1", hostName1, ServiceMonitorStatus.UP), + modelUtils.createServiceInstance("service-2", hostName2, ServiceMonitorStatus.DOWN), + modelUtils.createServiceInstance("service-3", hostName3, ServiceMonitorStatus.UP), + modelUtils.createServiceInstance("service-4", hostName4, ServiceMonitorStatus.DOWN), + modelUtils.createServiceInstance("service-5", hostName5, ServiceMonitorStatus.UP) + ) + ); + + verifyNoServices(serviceCluster, false, false, hostName1); + verifyNoServices(serviceCluster, true, false, hostName2); + verifyNoServices(serviceCluster, true, false, hostName3); + verifyNoServices(serviceCluster, true, false, hostName4); + verifyNoServices(serviceCluster, false, false, hostName5); + + verifyNoServices(serviceCluster, false, false, hostName1, hostName2); + verifyNoServices(serviceCluster, true, false, hostName2, hostName3); + verifyNoServices(serviceCluster, true, true, hostName2, hostName3, hostName4); + verifyNoServices(serviceCluster, false, true, hostName1, hostName2, hostName3, hostName4); + } + + private void verifyNoServices(ServiceCluster<ServiceMonitorStatus> serviceCluster, + boolean expectedNoServicesInGroupIsUp, + boolean expectedNoServicesOutsideGroupIsDown, + HostName... groupNodes) { + ClusterApiImpl clusterApi = new ClusterApiImpl( + serviceCluster, + new NodeGroup(modelUtils.createApplicationInstance(new ArrayList<>()), groupNodes), + modelUtils.getHostStatusMap(), + modelUtils.getClusterControllerClientFactory()); + + assertEquals(expectedNoServicesInGroupIsUp, clusterApi.noServicesInGroupIsUp()); + assertEquals(expectedNoServicesOutsideGroupIsDown, clusterApi.noServicesOutsideGroupIsDown()); + } + + @Test + public void testStorageCluster() { + HostName hostName1 = new HostName("host1"); + HostName hostName2 = new HostName("host2"); + HostName hostName3 = new HostName("host3"); + + ServiceCluster<ServiceMonitorStatus> serviceCluster = modelUtils.createServiceCluster( + "cluster", + VespaModelUtil.STORAGENODE_SERVICE_TYPE, + Arrays.asList( + modelUtils.createServiceInstance("storage-1", hostName1, ServiceMonitorStatus.UP), + modelUtils.createServiceInstance("storage-2", hostName2, ServiceMonitorStatus.DOWN) + ) + ); + + ClusterApiImpl clusterApi = new ClusterApiImpl( + serviceCluster, + new NodeGroup(modelUtils.createApplicationInstance(new ArrayList<>()), hostName1, hostName3), + new HashMap<>(), + modelUtils.getClusterControllerClientFactory()); + + assertTrue(clusterApi.isStorageCluster()); + assertEquals(Optional.of(hostName1), clusterApi.storageNodeInGroup().map(storageNode -> storageNode.hostName())); + assertEquals(Optional.of(hostName1), clusterApi.upStorageNodeInGroup().map(storageNode -> storageNode.hostName())); + } +}
\ No newline at end of file diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/ModelTestUtils.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/ModelTestUtils.java new file mode 100644 index 00000000000..03eb0184a88 --- /dev/null +++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/ModelTestUtils.java @@ -0,0 +1,92 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.orchestrator.model; + +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.ServiceInstance; +import com.yahoo.vespa.applicationmodel.ServiceType; +import com.yahoo.vespa.applicationmodel.TenantId; +import com.yahoo.vespa.orchestrator.controller.ClusterControllerClientFactory; +import com.yahoo.vespa.orchestrator.status.HostStatus; +import com.yahoo.vespa.orchestrator.status.MutableStatusRegistry; +import com.yahoo.vespa.service.monitor.ServiceMonitorStatus; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ModelTestUtils { + private final MutableStatusRegistry statusRegistry = mock(MutableStatusRegistry.class); + private final ClusterControllerClientFactory clusterControllerClientFactory = mock(ClusterControllerClientFactory.class); + private final Map<HostName, HostStatus> hostStatusMap = new HashMap<>(); + + ModelTestUtils() { + when(statusRegistry.getHostStatus(any())).thenReturn(HostStatus.NO_REMARKS); + } + + Map<HostName, HostStatus> getHostStatusMap() { + return hostStatusMap; + } + + HostName createNode(String name, HostStatus hostStatus) { + HostName hostName = new HostName(name); + hostStatusMap.put(hostName, hostStatus); + when(statusRegistry.getHostStatus(hostName)).thenReturn(hostStatus); + return hostName; + } + + ApplicationApiImpl createApplicationApiImpl( + ApplicationInstance<ServiceMonitorStatus> applicationInstance, + HostName... hostnames) { + NodeGroup nodeGroup = new NodeGroup(applicationInstance, hostnames); + return new ApplicationApiImpl(nodeGroup, statusRegistry, clusterControllerClientFactory); + } + + ApplicationInstance<ServiceMonitorStatus> createApplicationInstance( + List<ServiceCluster<ServiceMonitorStatus>> serviceClusters) { + Set<ServiceCluster<ServiceMonitorStatus>> serviceClusterSet = serviceClusters.stream() + .collect(Collectors.toSet()); + + return new ApplicationInstance<>( + new TenantId("tenant"), + new ApplicationInstanceId("application-name:foo:bar:default"), + serviceClusterSet); + } + + ServiceCluster<ServiceMonitorStatus> createServiceCluster( + String clusterId, + ServiceType serviceType, + List<ServiceInstance<ServiceMonitorStatus>> serviceInstances) { + Set<ServiceInstance<ServiceMonitorStatus>> serviceInstanceSet = serviceInstances.stream() + .collect(Collectors.toSet()); + + return new ServiceCluster<>( + new ClusterId(clusterId), + serviceType, + serviceInstanceSet); + } + + ServiceInstance<ServiceMonitorStatus> createServiceInstance( + String configId, + HostName hostName, + ServiceMonitorStatus status) { + return new ServiceInstance<>( + new ConfigId(configId), + hostName, + status); + } + + public ClusterControllerClientFactory getClusterControllerClientFactory() { + return clusterControllerClientFactory; + } +} diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/NodeGroupTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/NodeGroupTest.java new file mode 100644 index 00000000000..56a9e6bf147 --- /dev/null +++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/NodeGroupTest.java @@ -0,0 +1,33 @@ +package com.yahoo.vespa.orchestrator.model; + +import com.yahoo.vespa.applicationmodel.ApplicationInstance; +import com.yahoo.vespa.applicationmodel.ApplicationInstanceId; +import com.yahoo.vespa.applicationmodel.HostName; +import com.yahoo.vespa.applicationmodel.TenantId; +import com.yahoo.vespa.service.monitor.ServiceMonitorStatus; +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashSet; + +import static org.junit.Assert.assertEquals; + +public class NodeGroupTest { + @Test + public void testBasics() { + ApplicationInstance<ServiceMonitorStatus> applicationInstance = new ApplicationInstance<>( + new TenantId("tenant"), + new ApplicationInstanceId("application-instance"), + new HashSet<>()); + + HostName hostName1 = new HostName("host1"); + HostName hostName2 = new HostName("host2"); + HostName hostName3 = new HostName("host3"); + NodeGroup nodeGroup = new NodeGroup(applicationInstance, hostName1, hostName3); + nodeGroup.addNode(hostName2); + + // hostnames are sorted (for no good reason other than testability due to stability, readability) + assertEquals(Arrays.asList(hostName1, hostName2, hostName3), nodeGroup.getHostNames()); + assertEquals("host1,host2,host3", nodeGroup.toCommaSeparatedString()); + } +}
\ No newline at end of file diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/VespaModelUtilTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/VespaModelUtilTest.java index c39410e9ca6..f2c17299d39 100644 --- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/VespaModelUtilTest.java +++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/VespaModelUtilTest.java @@ -1,5 +1,5 @@ // Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.orchestrator; +package com.yahoo.vespa.orchestrator.model; import com.yahoo.vespa.applicationmodel.ApplicationInstance; import com.yahoo.vespa.applicationmodel.ApplicationInstanceId; @@ -10,14 +10,15 @@ import com.yahoo.vespa.applicationmodel.ServiceCluster; import com.yahoo.vespa.applicationmodel.ServiceInstance; import com.yahoo.vespa.applicationmodel.ServiceType; import com.yahoo.vespa.applicationmodel.TenantId; +import com.yahoo.vespa.orchestrator.TestUtil; import com.yahoo.vespa.service.monitor.ServiceMonitorStatus; import org.junit.Test; +import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Optional; -import java.util.Set; -import com.google.common.collect.ImmutableSet; import static com.yahoo.vespa.orchestrator.TestUtil.makeServiceClusterSet; import static com.yahoo.vespa.orchestrator.TestUtil.makeServiceInstanceSet; import static junit.framework.TestCase.assertFalse; @@ -169,9 +170,8 @@ public class VespaModelUtilTest { @Test public void testGettingClusterControllerInstances() { - Set<ServiceInstance<?>> controllers = - new HashSet<>(VespaModelUtil.getClusterControllerInstances(application, CONTENT_CLUSTER_ID)); - Set<ServiceInstance<ServiceMonitorStatus>> expectedControllers = ImmutableSet.of(controller0, controller1); + List<HostName> controllers = VespaModelUtil.getClusterControllerInstancesInOrder(application, CONTENT_CLUSTER_ID); + List<HostName> expectedControllers = Arrays.asList(controller0.hostName(), controller1.hostName()); assertThat(controllers).isEqualTo(expectedControllers); } diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicyTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicyTest.java index 45d605b6d8a..09471f9d6ae 100644 --- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicyTest.java +++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicyTest.java @@ -12,19 +12,21 @@ import com.yahoo.vespa.applicationmodel.ServiceInstance; import com.yahoo.vespa.applicationmodel.ServiceType; import com.yahoo.vespa.applicationmodel.TenantId; import com.yahoo.vespa.orchestrator.TestUtil; -import com.yahoo.vespa.orchestrator.VespaModelUtil; import com.yahoo.vespa.orchestrator.controller.ClusterControllerClient; import com.yahoo.vespa.orchestrator.controller.ClusterControllerClientFactory; import com.yahoo.vespa.orchestrator.controller.ClusterControllerState; import com.yahoo.vespa.orchestrator.controller.ClusterControllerStateResponse; +import com.yahoo.vespa.orchestrator.model.VespaModelUtil; import com.yahoo.vespa.orchestrator.status.HostStatus; import com.yahoo.vespa.orchestrator.status.MutableStatusRegistry; import com.yahoo.vespa.service.monitor.ServiceMonitorStatus; import org.junit.Test; import java.util.HashSet; +import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import static com.yahoo.vespa.orchestrator.TestUtil.makeServiceClusterSet; import static com.yahoo.vespa.orchestrator.TestUtil.makeServiceInstanceSet; @@ -417,10 +419,12 @@ public class HostedVespaPolicyTest { // Verification phase. if (expectedNodeStateSentToClusterController.isPresent()) { + List<HostName> clusterControllers = CLUSTER_CONTROLLER_SERVICE_CLUSTER.serviceInstances().stream() + .map(service -> service.hostName()) + .collect(Collectors.toList()); + verify(clusterControllerClientFactory, times(1)) - .createClient( - CLUSTER_CONTROLLER_SERVICE_CLUSTER.serviceInstances(), - CONTENT_CLUSTER_NAME); + .createClient(clusterControllers, CONTENT_CLUSTER_NAME); verify(client, times(1)) .setNodeState( STORAGE_NODE_INDEX, |