diff options
Diffstat (limited to 'controller-server/src/test/java')
24 files changed, 592 insertions, 174 deletions
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ConfigServerClientMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ConfigServerClientMock.java index 9db852374b8..9228e83bbc6 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ConfigServerClientMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ConfigServerClientMock.java @@ -7,16 +7,16 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.yahoo.component.AbstractComponent; import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; -import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerClient; -import com.yahoo.vespa.hosted.controller.api.integration.configserver.Log; -import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeList; -import com.yahoo.vespa.hosted.controller.api.integration.configserver.PrepareResponse; import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions; import com.yahoo.vespa.hosted.controller.api.application.v4.model.EndpointStatus; import com.yahoo.vespa.hosted.controller.api.application.v4.model.configserverbindings.ConfigChangeActions; import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; import com.yahoo.vespa.hosted.controller.api.identifiers.Hostname; import com.yahoo.vespa.hosted.controller.api.identifiers.TenantId; +import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerClient; +import com.yahoo.vespa.hosted.controller.api.integration.configserver.Log; +import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeList; +import com.yahoo.vespa.hosted.controller.api.integration.configserver.PrepareResponse; import com.yahoo.vespa.hosted.controller.api.rotation.Rotation; import com.yahoo.vespa.serviceview.bindings.ApplicationView; import com.yahoo.vespa.serviceview.bindings.ClusterView; @@ -28,7 +28,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -40,15 +39,11 @@ import java.util.UUID; */ public class ConfigServerClientMock extends AbstractComponent implements ConfigServerClient { - private Map<ApplicationId, byte[]> applicationContent = new HashMap<>(); - private Map<ApplicationId, String> applicationInstances = new HashMap<>(); - private Map<ApplicationId, Boolean> applicationActivated = new HashMap<>(); - private Set<ApplicationId> applicationRestarted = new HashSet<>(); - private Set<String> hostsExplicitlyRestarted = new HashSet<>(); - private Map<String, EndpointStatus> endpoints = new HashMap<>(); - - private Map<URI, Version> configServerVersions = new HashMap<>(); - private Version defaultConfigServerVersion = new Version(6, 1, 0); + private final Map<ApplicationId, String> applicationInstances = new HashMap<>(); + private final Map<ApplicationId, Boolean> applicationActivated = new HashMap<>(); + private final Map<String, EndpointStatus> endpoints = new HashMap<>(); + private final Map<URI, Version> versions = new HashMap<>(); + private Version defaultVersion = new Version(6, 1, 0); /** The exception to throw on the next prepare run, or null to continue normally */ private RuntimeException prepareException = null; @@ -64,18 +59,27 @@ public class ConfigServerClientMock extends AbstractComponent implements ConfigS public Map<ApplicationId, Boolean> activated() { return Collections.unmodifiableMap(applicationActivated); } + + public void throwOnNextPrepare(RuntimeException prepareException) { + this.prepareException = prepareException; + } + + /** + * Returns the (initially empty) mutable map of config server urls to versions. + * This API will return defaultVersion as response to any version(url) call for versions not added to the map. + */ + public Map<URI, Version> versions() { + return versions; + } @Override public PreparedApplication prepare(DeploymentId deployment, DeployOptions deployOptions, Set<String> rotationCnames, Set<Rotation> rotations, byte[] content) { lastPrepareVersion = deployOptions.vespaVersion.map(Version::new); - if (prepareException != null) { RuntimeException prepareException = this.prepareException; this.prepareException = null; throw prepareException; } - - applicationContent.put(deployment.applicationId(), content); applicationActivated.put(deployment.applicationId(), false); applicationInstances.put(deployment.applicationId(), UUID.randomUUID() + ":4080"); @@ -111,20 +115,8 @@ public class ConfigServerClientMock extends AbstractComponent implements ConfigS }; } - public void throwOnNextPrepare(RuntimeException prepareException) { - this.prepareException = prepareException; - } - - /** - * Returns the (initially empty) mutable map of config server urls to versions. - * This API will return defaultConfigserverVersion as response to any version(url) call for versions not added to the map. - */ - public Map<URI, Version> configServerVersions() { - return configServerVersions; - } - - public Version getDefaultConfigServerVersion() { return defaultConfigServerVersion; } - public void setDefaultConfigServerVersion(Version version) { defaultConfigServerVersion = version; } + /** Set the default config server version */ + public void setDefaultVersion(Version version) { this.defaultVersion = version; } @Override public List<String> getNodeQueryHost(DeploymentId deployment, String type) { @@ -137,16 +129,11 @@ public class ConfigServerClientMock extends AbstractComponent implements ConfigS @Override public void restart(DeploymentId deployment, Optional<Hostname> hostname) { - applicationRestarted.add(deployment.applicationId()); - if (hostname.isPresent()) { - hostsExplicitlyRestarted.add(hostname.get().id()); - } } @Override public void deactivate(DeploymentId deployment) { applicationActivated.remove(deployment.applicationId()); - applicationContent.remove(deployment.applicationId()); applicationInstances.remove(deployment.applicationId()); } @@ -197,7 +184,7 @@ public class ConfigServerClientMock extends AbstractComponent implements ConfigS @Override public Version version(URI configServerURI) { - return configServerVersions.getOrDefault(configServerURI, defaultConfigServerVersion); + return versions.getOrDefault(configServerURI, defaultVersion); } @Override @@ -207,10 +194,8 @@ public class ConfigServerClientMock extends AbstractComponent implements ConfigS @Override public EndpointStatus getGlobalRotationStatus(DeploymentId deployment, String endpoint) { - EndpointStatus result = new EndpointStatus(EndpointStatus.Status.in, "", "", 1497618757l); - return endpoints.containsKey(endpoint) - ? endpoints.get(endpoint) - : result; + EndpointStatus result = new EndpointStatus(EndpointStatus.Status.in, "", "", 1497618757L); + return endpoints.getOrDefault(endpoint, result); } @Override diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java index 6fc787d940e..408be6a49c2 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java @@ -24,9 +24,6 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId; import com.yahoo.vespa.hosted.controller.api.identifiers.TenantId; import com.yahoo.vespa.hosted.controller.api.identifiers.UserGroup; import com.yahoo.vespa.hosted.controller.api.integration.BuildService.BuildJob; -import com.yahoo.vespa.hosted.controller.api.integration.athens.NToken; -import com.yahoo.vespa.hosted.controller.api.integration.athens.mock.AthensDbMock; -import com.yahoo.vespa.hosted.controller.api.integration.athens.mock.NTokenMock; import com.yahoo.vespa.hosted.controller.api.integration.dns.Record; import com.yahoo.vespa.hosted.controller.application.ApplicationPackage; import com.yahoo.vespa.hosted.controller.application.ApplicationRevision; @@ -36,6 +33,8 @@ import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobError; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobReport; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType; import com.yahoo.vespa.hosted.controller.application.JobStatus; +import com.yahoo.vespa.hosted.controller.athenz.NToken; +import com.yahoo.vespa.hosted.controller.athenz.mock.AthensDbMock; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.deployment.BuildSystem; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; @@ -260,16 +259,19 @@ public class ControllerTest { Version systemVersion = controller.versionStatus().systemVersion().get().versionNumber(); Version newSystemVersion = new Version(systemVersion.getMajor(), systemVersion.getMinor()+1, 0); VespaVersion newSystemVespaVersion = new VespaVersion(DeploymentStatistics.empty(newSystemVersion), - "commit1", + "commit1", Instant.now(), true, Collections.emptyList(), - controller); + VespaVersion.Confidence.low + ); List<VespaVersion> versions = new ArrayList<>(controller.versionStatus().versions()); for (int i = 0; i < versions.size(); i++) { VespaVersion c = versions.get(i); if (c.isCurrentSystemVersion()) - versions.set(i, new VespaVersion(c.statistics(), c.releaseCommit(), c.releasedAt(), false, c.configServerHostnames(), controller)); + versions.set(i, new VespaVersion(c.statistics(), c.releaseCommit(), c.releasedAt(), + false, c.configServerHostnames(), + c.confidence())); } versions.add(newSystemVespaVersion); controller.updateVersionStatus(new VersionStatus(versions)); @@ -378,7 +380,7 @@ public class ControllerTest { assertFalse(mockDomain.isVespaTenant); // Migrate tenant to Athens - NToken nToken = new NTokenMock("token"); + NToken nToken = TestIdentities.userNToken; tester.controller().tenants().migrateTenantToAthens( tenantId, athensDomain, new PropertyId("1567"), new Property("vespa_dev.no"), nToken); @@ -542,7 +544,7 @@ public class ControllerTest { // Current system version, matches version in test data Version version = Version.fromString("6.141.117"); - tester.configServer().setDefaultConfigServerVersion(version); + tester.configServer().setDefaultVersion(version); tester.updateVersionStatus(version); assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber()); @@ -572,7 +574,7 @@ public class ControllerTest { // New version is released version = Version.fromString("6.142.1"); - tester.configServer().setDefaultConfigServerVersion(version); + tester.configServer().setDefaultVersion(version); tester.updateVersionStatus(version); assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber()); tester.upgrader().maintain(); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java index 5184cde79c2..8e1234b7e96 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java @@ -21,8 +21,6 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.Property; import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId; import com.yahoo.vespa.hosted.controller.api.identifiers.ScrewdriverId; import com.yahoo.vespa.hosted.controller.api.identifiers.TenantId; -import com.yahoo.vespa.hosted.controller.api.integration.athens.mock.AthensDbMock; -import com.yahoo.vespa.hosted.controller.api.integration.athens.mock.AthensMock; import com.yahoo.vespa.hosted.controller.api.integration.chef.ChefMock; import com.yahoo.vespa.hosted.controller.api.integration.dns.MemoryNameService; import com.yahoo.vespa.hosted.controller.api.integration.entity.MemoryEntityService; @@ -30,6 +28,8 @@ import com.yahoo.vespa.hosted.controller.api.integration.github.GitHubMock; import com.yahoo.vespa.hosted.controller.api.integration.jira.JiraMock; import com.yahoo.vespa.hosted.controller.api.integration.routing.MemoryGlobalRoutingService; import com.yahoo.vespa.hosted.controller.application.ApplicationPackage; +import com.yahoo.vespa.hosted.controller.athenz.mock.AthensDbMock; +import com.yahoo.vespa.hosted.controller.athenz.mock.AthenzClientFactoryMock; import com.yahoo.vespa.hosted.controller.integration.MockMetricsService; import com.yahoo.vespa.hosted.controller.persistence.ControllerDb; import com.yahoo.vespa.hosted.controller.persistence.CuratorDb; @@ -214,7 +214,7 @@ public final class ControllerTester { new MockRoutingGenerator(), new ChefMock(), clock, - new AthensMock(athensDb)); + new AthenzClientFactoryMock(athensDb)); controller.updateVersionStatus(VersionStatus.compute(controller)); return controller; } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/TestIdentities.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/TestIdentities.java index 1f52ebcadb7..355b63335c0 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/TestIdentities.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/TestIdentities.java @@ -1,7 +1,6 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller; -import com.yahoo.vespa.hosted.controller.api.integration.athens.mock.NTokenMock; import com.yahoo.vespa.hosted.controller.api.Tenant; import com.yahoo.vespa.hosted.controller.api.identifiers.EnvironmentId; import com.yahoo.vespa.hosted.controller.api.identifiers.InstanceId; @@ -10,7 +9,9 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.RegionId; import com.yahoo.vespa.hosted.controller.api.identifiers.TenantId; import com.yahoo.vespa.hosted.controller.api.identifiers.UserGroup; import com.yahoo.vespa.hosted.controller.api.identifiers.UserId; -import com.yahoo.vespa.hosted.controller.api.integration.athens.NToken; +import com.yahoo.vespa.hosted.controller.athenz.AthenzUtils; +import com.yahoo.vespa.hosted.controller.athenz.NToken; +import com.yahoo.vespa.hosted.controller.athenz.filter.AthenzTestUtils; /** * @author Tony Vaagenes @@ -33,6 +34,8 @@ public class TestIdentities { public static Tenant tenant = Tenant.createOpsDbTenant(tenantId, userGroup1, property); - public static NToken userNToken = new NTokenMock("token"); + public static NToken userNToken = new NToken.Builder( + "U1", AthenzUtils.createPrincipal(userId), AthenzTestUtils.generateRsaKeypair().getPrivate(), "0") + .build(); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/ClusterCostTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/ClusterCostTest.java new file mode 100644 index 00000000000..313f565f546 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/ClusterCostTest.java @@ -0,0 +1,35 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.application; + +import com.yahoo.config.provision.ClusterSpec; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author smorgrav + */ +public class ClusterCostTest { + + @Test + public void clusterCost() throws Exception { + List<String> hostnames = new ArrayList<>(); + hostnames.add("host1"); + hostnames.add("host2"); + ClusterInfo info = new ClusterInfo("test", 100, 10, 10, 10, ClusterSpec.Type.container, hostnames); + ClusterUtilization util = new ClusterUtilization(0.3, 0.2, 0.5, 0.1); + ClusterCost cost = new ClusterCost(info, util); + + // CPU is fully utilized + Assert.assertEquals(200, cost.getTco(), Double.MIN_VALUE); + Assert.assertEquals(0, cost.getWaste(), Double.MIN_VALUE); + + // Set Disk as the most utilized resource + util = new ClusterUtilization(0.3, 0.1, 0.5, 0.1); + cost = new ClusterCost(info, util); + Assert.assertEquals(200, cost.getTco(), Double.MIN_NORMAL); // TCO is independent of utilization + Assert.assertEquals(57.1428571429, cost.getWaste(), 0.001); // Waste is not independent + } +}
\ No newline at end of file diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/DeploymentCostTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/DeploymentCostTest.java new file mode 100644 index 00000000000..2e58253d768 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/DeploymentCostTest.java @@ -0,0 +1,38 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.application; + +import com.yahoo.config.provision.ClusterSpec; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author smorgrav + */ +public class DeploymentCostTest { + + @Test + public void deploymentCost() { + Map<String, ClusterCost> clusters = new HashMap<>(); + clusters.put("cluster1", createClusterCost(100, 0.2)); + clusters.put("cluster2", createClusterCost(50, 0.1)); + + DeploymentCost cost = new DeploymentCost(clusters); + Assert.assertEquals(300, cost.getTco(), Double.MIN_VALUE); // 2*100 + 2*50 + Assert.assertEquals(28.5714285714, cost.getWaste(), 0.001); // from cluster2 + Assert.assertEquals(0.7142857142857143, cost.getUtilization(), Double.MIN_VALUE); // from cluster2 + } + + private ClusterCost createClusterCost(int flavorCost, double cpuUtil) { + List<String> hostnames = new ArrayList<>(); + hostnames.add("host1"); + hostnames.add("host2"); + ClusterInfo info = new ClusterInfo("test", flavorCost, 10, 10, 10, ClusterSpec.Type.container, hostnames); + ClusterUtilization util = new ClusterUtilization(0.3, cpuUtil, 0.5, 0.1); + return new ClusterCost(info, util); + } +}
\ No newline at end of file diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzPrincipalFilterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzPrincipalFilterTest.java new file mode 100644 index 00000000000..20db038485d --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzPrincipalFilterTest.java @@ -0,0 +1,123 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.athenz.filter; + +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.ReadableContentChannel; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.http.filter.DiscFilterRequest; +import com.yahoo.vespa.hosted.controller.api.identifiers.UserId; +import com.yahoo.vespa.hosted.controller.athenz.AthenzPrincipal; +import com.yahoo.vespa.hosted.controller.athenz.AthenzUtils; +import com.yahoo.vespa.hosted.controller.athenz.InvalidTokenException; +import com.yahoo.vespa.hosted.controller.athenz.NToken; +import org.junit.Before; +import org.junit.Test; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UncheckedIOException; +import java.util.Objects; + +import static com.yahoo.jdisc.Response.Status.UNAUTHORIZED; +import static java.util.stream.Collectors.joining; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * @author bjorncs + */ +public class AthenzPrincipalFilterTest { + + private static final NToken NTOKEN = createDummyToken(); + private static final String ATHENZ_PRINCIPAL_HEADER = "Athenz-Principal-Auth"; + + private NTokenValidator validator; + private AthenzPrincipal principal; + + @Before + public void before() { + validator = mock(NTokenValidator.class); + principal = AthenzUtils.createPrincipal(new UserId("bob")); + } + + @Test + public void valid_ntoken_is_accepted() throws Exception { + DiscFilterRequest request = mock(DiscFilterRequest.class); + when(request.getHeader(ATHENZ_PRINCIPAL_HEADER)).thenReturn(NTOKEN.getToken()); + + when(validator.validate(NTOKEN)).thenReturn(principal); + + AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, Runnable::run, ATHENZ_PRINCIPAL_HEADER); + filter.filter(request, new ResponseHandlerMock()); + + verify(request).setUserPrincipal(principal); + } + + @Test + public void missing_token_is_unauthorized() throws Exception { + DiscFilterRequest request = mock(DiscFilterRequest.class); + when(request.getHeader(ATHENZ_PRINCIPAL_HEADER)).thenReturn(null); + + ResponseHandlerMock responseHandler = new ResponseHandlerMock(); + + AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, Runnable::run, ATHENZ_PRINCIPAL_HEADER); + filter.filter(request, responseHandler); + + assertThat(responseHandler.response, notNullValue()); + assertThat(responseHandler.response.getStatus(), equalTo(UNAUTHORIZED)); + assertThat(responseHandler.getResponseContent(), containsString("NToken is missing")); + } + + @Test + public void invalid_token_is_unauthorized() throws Exception { + DiscFilterRequest request = mock(DiscFilterRequest.class); + when(request.getHeader(ATHENZ_PRINCIPAL_HEADER)).thenReturn(NTOKEN.getToken()); + + when(validator.validate(NTOKEN)).thenThrow(new InvalidTokenException("Invalid token")); + + ResponseHandlerMock responseHandler = new ResponseHandlerMock(); + + AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, Runnable::run, ATHENZ_PRINCIPAL_HEADER); + filter.filter(request, responseHandler); + + assertThat(responseHandler.response, notNullValue()); + assertThat(responseHandler.response.getStatus(), equalTo(UNAUTHORIZED)); + assertThat(responseHandler.getResponseContent(), containsString("Invalid token")); + } + + private static NToken createDummyToken() { + return new NToken.Builder( + "U1", AthenzUtils.createPrincipal(new UserId("bob")), AthenzTestUtils.generateRsaKeypair().getPrivate(), "0") + .build(); + } + + private static class ResponseHandlerMock implements ResponseHandler { + + public Response response; + public ReadableContentChannel contentChannel; + + @Override + public ContentChannel handleResponse(Response r) { + response = Objects.requireNonNull(r); + contentChannel = new ReadableContentChannel(); + return contentChannel; + } + + public String getResponseContent() { + try (BufferedReader br = new BufferedReader(new InputStreamReader(contentChannel.toStream()))) { + return br.lines().collect(joining(System.lineSeparator())); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + } + +} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzTestUtils.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzTestUtils.java new file mode 100644 index 00000000000..40b38254dda --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzTestUtils.java @@ -0,0 +1,22 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.athenz.filter; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; + +/** + * @author bjorncs + */ +public class AthenzTestUtils { + public static KeyPair generateRsaKeypair() { + try { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(512); + return keyGen.genKeyPair(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/NTokenValidatorTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/NTokenValidatorTest.java new file mode 100644 index 00000000000..e269f2842e2 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/NTokenValidatorTest.java @@ -0,0 +1,95 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.athenz.filter; + +import com.yahoo.vespa.hosted.controller.api.identifiers.AthensDomain; +import com.yahoo.vespa.hosted.controller.api.identifiers.UserId; +import com.yahoo.vespa.hosted.controller.athenz.AthenzPrincipal; +import com.yahoo.vespa.hosted.controller.athenz.InvalidTokenException; +import com.yahoo.vespa.hosted.controller.athenz.NToken; +import com.yahoo.vespa.hosted.controller.athenz.ZmsKeystore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.util.Optional; + +import static com.yahoo.vespa.hosted.controller.athenz.AthenzUtils.ZMS_ATHENZ_SERVICE; +import static org.junit.Assert.assertEquals; + +/** + * @author bjorncs + */ +public class NTokenValidatorTest { + + private static final KeyPair TRUSTED_KEY = AthenzTestUtils.generateRsaKeypair(); + private static final KeyPair UNKNOWN_KEY = AthenzTestUtils.generateRsaKeypair(); + private static final AthenzPrincipal PRINCIPAL = new AthenzPrincipal(new AthensDomain("yby"), new UserId("user")); + + @Rule + public ExpectedException exceptionRule = ExpectedException.none(); + + @Test + public void valid_token_is_accepted() throws NoSuchAlgorithmException, InvalidTokenException { + NTokenValidator validator = new NTokenValidator(createKeystore()); + NToken token = createNToken(PRINCIPAL, System.currentTimeMillis(), TRUSTED_KEY, "0"); + AthenzPrincipal principal = validator.validate(token); + assertEquals("yby.user", principal.toYRN()); + } + + @Test + public void invalid_signature_is_not_accepted() throws InvalidTokenException { + NTokenValidator validator = new NTokenValidator(createKeystore()); + NToken token = createNToken(PRINCIPAL, System.currentTimeMillis(), UNKNOWN_KEY, "0"); + exceptionRule.expect(InvalidTokenException.class); + exceptionRule.expectMessage("NToken is expired or has invalid signature"); + validator.validate(token); + } + + @Test + public void expired_token_is_not_accepted() throws InvalidTokenException { + NTokenValidator validator = new NTokenValidator(createKeystore()); + NToken token = createNToken(PRINCIPAL, 1234 /*long time ago*/, TRUSTED_KEY, "0"); + exceptionRule.expect(InvalidTokenException.class); + exceptionRule.expectMessage("NToken is expired or has invalid signature"); + validator.validate(token); + } + + @Test + public void unknown_keyId_is_not_accepted() throws InvalidTokenException { + NTokenValidator validator = new NTokenValidator(createKeystore()); + NToken token = createNToken(PRINCIPAL, System.currentTimeMillis(), TRUSTED_KEY, "unknown-key-id"); + exceptionRule.expect(InvalidTokenException.class); + exceptionRule.expectMessage("NToken has an unknown keyId"); + validator.validate(token); + } + + @Test + public void failing_to_find_key_should_throw_exception() throws InvalidTokenException { + ZmsKeystore keystore = (athensService, keyId) -> { throw new RuntimeException(); }; + NTokenValidator validator = new NTokenValidator(keystore); + NToken token = createNToken(PRINCIPAL, System.currentTimeMillis(), TRUSTED_KEY, "0"); + exceptionRule.expect(InvalidTokenException.class); + exceptionRule.expectMessage("Failed to retrieve public key"); + validator.validate(token); + } + + private static ZmsKeystore createKeystore() { + return (athensService, keyId) -> + athensService.equals(ZMS_ATHENZ_SERVICE) && keyId.equals("0") + ? Optional.of(TRUSTED_KEY.getPublic()) + : Optional.empty(); + } + + private static NToken createNToken(AthenzPrincipal principal, long issueTime, KeyPair keyPair, String keyId) { + return new NToken.Builder("U1", principal, keyPair.getPrivate(), keyId) + .salt("1234") + .hostname("host") + .ip("1.2.3.4") + .issueTime(issueTime / 1000) + .expirationWindow(1000) + .build(); + } + +} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java index be14947de2b..e45166b1c16 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java @@ -90,12 +90,17 @@ public class DeploymentTester { .filter(c -> c instanceof Change.VersionChange) .map(Change.VersionChange.class::cast); } + + public void updateVersionStatus() { + controller().updateVersionStatus(VersionStatus.compute(controller(), tester.controller().systemVersion())); + } public void updateVersionStatus(Version currentVersion) { controller().updateVersionStatus(VersionStatus.compute(controller(), currentVersion)); } public void upgradeSystem(Version version) { + controllerTester().configServer().setDefaultVersion(version); updateVersionStatus(version); upgrader().maintain(); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainerTest.java index 7ae89082660..13919cefd3b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainerTest.java @@ -31,7 +31,7 @@ public class ClusterInfoMaintainerTest { deployment = tester.controller().applications().get(app).get().deployments().values().stream().findAny().get(); Assert.assertEquals(2, deployment.clusterInfo().size()); - Assert.assertEquals(10, deployment.clusterInfo().get(ClusterSpec.Id.from("clusterA")).getCost()); + Assert.assertEquals(10, deployment.clusterInfo().get(ClusterSpec.Id.from("clusterA")).getFlavorCost()); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainerTest.java new file mode 100644 index 00000000000..cb503fc1fb7 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainerTest.java @@ -0,0 +1,41 @@ +package com.yahoo.vespa.hosted.controller.maintenance; + +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.Environment; +import com.yahoo.vespa.hosted.controller.ControllerTester; +import com.yahoo.vespa.hosted.controller.application.Deployment; +import com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb; +import org.junit.Assert; +import org.junit.Test; + +import java.time.Duration; + +/** + * @author smorgrav + */ +public class DeploymentMetricsMaintainerTest { + + @Test + public void maintain() { + ControllerTester tester = new ControllerTester(); + ApplicationId app = tester.createAndDeploy("tenant1", "domain1", "app1", Environment.dev, 123).id(); + + // Pre condition: no metric info on the deployment + Deployment deployment = tester.controller().applications().get(app).get().deployments().values().stream().findAny().get(); + Assert.assertEquals(0, deployment.metrics().documentCount(), Double.MIN_VALUE); + + DeploymentMetricsMaintainer maintainer = new DeploymentMetricsMaintainer(tester.controller(), Duration.ofMinutes(10), new JobControl(new MockCuratorDb())); + maintainer.maintain(); + + // Post condition: + deployment = tester.controller().applications().get(app).get().deployments().values().stream().findAny().get(); + Assert.assertEquals(1, deployment.metrics().queriesPerSecond(), Double.MIN_VALUE); + Assert.assertEquals(2, deployment.metrics().writesPerSecond(), Double.MIN_VALUE); + Assert.assertEquals(3, deployment.metrics().documentCount(), Double.MIN_VALUE); + Assert.assertEquals(4, deployment.metrics().queryLatencyMillis(), Double.MIN_VALUE); + Assert.assertEquals(5, deployment.metrics().writeLatencyMillis(), Double.MIN_VALUE); + } + +}
\ No newline at end of file diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployerTest.java index 286db864c22..4c935747ac6 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployerTest.java @@ -181,7 +181,7 @@ public class FailureRedeployerTest { // Current system version, matches version in test data Version version = Version.fromString("6.141.117"); - tester.configServer().setDefaultConfigServerVersion(version); + tester.configServer().setDefaultVersion(version); tester.updateVersionStatus(version); assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber()); @@ -200,7 +200,7 @@ public class FailureRedeployerTest { // New version is released version = Version.fromString("6.142.1"); - tester.configServer().setDefaultConfigServerVersion(version); + tester.configServer().setDefaultVersion(version); tester.updateVersionStatus(version); assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber()); tester.upgrader().maintain(); @@ -237,7 +237,7 @@ public class FailureRedeployerTest { // Current system version, matches version in test data Version version = Version.fromString("6.42.1"); - tester.configServer().setDefaultConfigServerVersion(version); + tester.configServer().setDefaultVersion(version); tester.updateVersionStatus(version); assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber()); @@ -262,7 +262,7 @@ public class FailureRedeployerTest { // Current system version, matches version in test data Version version = Version.fromString("6.42.1"); - tester.configServer().setDefaultConfigServerVersion(version); + tester.configServer().setDefaultVersion(version); tester.updateVersionStatus(version); assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java index 3e73bf4445b..f7fd7cb6fb1 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java @@ -20,6 +20,7 @@ import com.yahoo.vespa.hosted.controller.application.ClusterUtilization; import com.yahoo.vespa.hosted.controller.application.Deployment; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobError; +import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics; import com.yahoo.vespa.hosted.controller.application.JobStatus; import com.yahoo.vespa.hosted.controller.application.SourceRevision; import org.junit.Test; @@ -61,7 +62,7 @@ public class ApplicationSerializerTest { ApplicationRevision revision2 = ApplicationRevision.from("appHash2", new SourceRevision("repo1", "branch1", "commit1")); deployments.add(new Deployment(zone1, revision1, Version.fromString("1.2.3"), Instant.ofEpochMilli(3))); // One deployment without cluster info and utils deployments.add(new Deployment(zone2, revision2, Version.fromString("1.2.3"), Instant.ofEpochMilli(5), - createClusterUtils(3, 0.2), createClusterInfo(3, 4))); + createClusterUtils(3, 0.2), createClusterInfo(3, 4),new DeploymentMetrics(2,3,4,5,6))); Optional<Long> projectId = Optional.of(123L); List<JobStatus> statusList = new ArrayList<>(); @@ -118,10 +119,20 @@ public class ApplicationSerializerTest { // Test cluster info assertEquals(3, serialized.deployments().get(zone2).clusterInfo().size()); - assertEquals(10, serialized.deployments().get(zone2).clusterInfo().get(ClusterSpec.Id.from("id2")).getCost()); + assertEquals(10, serialized.deployments().get(zone2).clusterInfo().get(ClusterSpec.Id.from("id2")).getFlavorCost()); assertEquals(ClusterSpec.Type.content, serialized.deployments().get(zone2).clusterInfo().get(ClusterSpec.Id.from("id2")).getClusterType()); assertEquals("flavor2", serialized.deployments().get(zone2).clusterInfo().get(ClusterSpec.Id.from("id2")).getFlavor()); assertEquals(4, serialized.deployments().get(zone2).clusterInfo().get(ClusterSpec.Id.from("id2")).getHostnames().size()); + assertEquals(2, serialized.deployments().get(zone2).clusterInfo().get(ClusterSpec.Id.from("id2")).getFlavorCPU(), Double.MIN_VALUE); + assertEquals(4, serialized.deployments().get(zone2).clusterInfo().get(ClusterSpec.Id.from("id2")).getFlavorMem(), Double.MIN_VALUE); + assertEquals(50, serialized.deployments().get(zone2).clusterInfo().get(ClusterSpec.Id.from("id2")).getFlavorDisk(), Double.MIN_VALUE); + + // Test metrics + assertEquals(2, serialized.deployments().get(zone2).metrics().queriesPerSecond(), Double.MIN_VALUE); + assertEquals(3, serialized.deployments().get(zone2).metrics().writesPerSecond(), Double.MIN_VALUE); + assertEquals(4, serialized.deployments().get(zone2).metrics().documentCount(), Double.MIN_VALUE); + assertEquals(5, serialized.deployments().get(zone2).metrics().queryLatencyMillis(), Double.MIN_VALUE); + assertEquals(6, serialized.deployments().get(zone2).metrics().writeLatencyMillis(), Double.MIN_VALUE); { // test more deployment serialization cases Application original2 = original.withDeploying(Optional.of(Change.ApplicationChange.of(ApplicationRevision.from("hash1")))); @@ -153,7 +164,7 @@ public class ApplicationSerializerTest { } result.put(ClusterSpec.Id.from("id" + cluster), new ClusterInfo("flavor" + cluster, 10, - ClusterSpec.Type.content, hostnames)); + 2, 4, 50, ClusterSpec.Type.content, hostnames)); } return result; } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/VersionStatusSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/VersionStatusSerializerTest.java new file mode 100644 index 00000000000..feaf1289853 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/VersionStatusSerializerTest.java @@ -0,0 +1,53 @@ +package com.yahoo.vespa.hosted.controller.persistence; + +import com.yahoo.component.Version; +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.vespa.hosted.controller.versions.DeploymentStatistics; +import com.yahoo.vespa.hosted.controller.versions.VersionStatus; +import com.yahoo.vespa.hosted.controller.versions.VespaVersion; +import org.junit.Test; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * @author mpolden + */ +public class VersionStatusSerializerTest { + + @Test + public void testSerialization() throws Exception { + List<VespaVersion> vespaVersions = new ArrayList<>(); + DeploymentStatistics statistics = new DeploymentStatistics( + Version.fromString("5.0"), + Arrays.asList(ApplicationId.from("tenant1", "failing1", "default")), + Arrays.asList(ApplicationId.from("tenant2", "success1", "default"), + ApplicationId.from("tenant2", "success2", "default")) + ); + vespaVersions.add(new VespaVersion(statistics, "dead", Instant.now(), false, + Arrays.asList("cfg1", "cfg2", "cfg3"), VespaVersion.Confidence.normal)); + vespaVersions.add(new VespaVersion(statistics, "cafe", Instant.now(), true, + Arrays.asList("cfg1", "cfg2", "cfg3"), VespaVersion.Confidence.normal)); + VersionStatus status = new VersionStatus(vespaVersions); + VersionStatusSerializer serializer = new VersionStatusSerializer(); + VersionStatus deserialized = serializer.fromSlime(serializer.toSlime(status)); + + assertEquals(status.versions().size(), deserialized.versions().size()); + for (int i = 0; i < status.versions().size(); i++) { + VespaVersion a = status.versions().get(i); + VespaVersion b = deserialized.versions().get(i); + assertEquals(a.releaseCommit(), b.releaseCommit()); + assertEquals(a.releasedAt(), b.releasedAt()); + assertEquals(a.isCurrentSystemVersion(), b.isCurrentSystemVersion()); + assertEquals(a.statistics(), b.statistics()); + assertEquals(a.configServerHostnames(), b.configServerHostnames()); + assertEquals(a.confidence(), b.confidence()); + } + + } + +} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java index a792626d691..99381d538d5 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java @@ -21,13 +21,11 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId; import com.yahoo.vespa.hosted.controller.api.identifiers.ScrewdriverId; import com.yahoo.vespa.hosted.controller.api.identifiers.TenantId; import com.yahoo.vespa.hosted.controller.api.identifiers.UserId; -import com.yahoo.vespa.hosted.controller.api.integration.athens.Athens; -import com.yahoo.vespa.hosted.controller.api.integration.athens.AthensPrincipal; -import com.yahoo.vespa.hosted.controller.api.integration.athens.mock.AthensDbMock; -import com.yahoo.vespa.hosted.controller.api.integration.athens.mock.AthensMock; -import com.yahoo.vespa.hosted.controller.api.integration.athens.mock.ZmsClientFactoryMock; import com.yahoo.vespa.hosted.controller.application.ApplicationPackage; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs; +import com.yahoo.vespa.hosted.controller.athenz.AthenzPrincipal; +import com.yahoo.vespa.hosted.controller.athenz.mock.AthensDbMock; +import com.yahoo.vespa.hosted.controller.athenz.mock.AthenzClientFactoryMock; import com.yahoo.vespa.hosted.controller.maintenance.JobControl; import com.yahoo.vespa.hosted.controller.maintenance.Upgrader; import com.yahoo.vespa.hosted.controller.persistence.CuratorDb; @@ -97,14 +95,12 @@ public class ContainerControllerTester { } public AthensDomain addTenantAthensDomain(String domainName, String userName) { - Athens athens = (AthensMock) containerTester.container().components().getComponent( - "com.yahoo.vespa.hosted.controller.api.integration.athens.mock.AthensMock" - ); - ZmsClientFactoryMock mock = (ZmsClientFactoryMock) athens.zmsClientFactory(); + AthenzClientFactoryMock mock = (AthenzClientFactoryMock) containerTester.container().components() + .getComponent(AthenzClientFactoryMock.class.getName()); AthensDomain athensDomain = new AthensDomain(domainName); AthensDbMock.Domain domain = new AthensDbMock.Domain(athensDomain); domain.markAsVespaTenant(); - domain.admin(new AthensPrincipal(new AthensDomain("domain"), new UserId(userName))); + domain.admin(new AthenzPrincipal(new AthensDomain("domain"), new UserId(userName))); mock.getSetup().addDomain(domain); return athensDomain; } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java index fd07428126a..5c102b0d9df 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java @@ -36,7 +36,7 @@ public class ControllerContainerTest { " <system>main</system>" + " </config>" + " <component id='com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb'/>" + - " <component id='com.yahoo.vespa.hosted.controller.api.integration.athens.mock.AthensMock'/>" + + " <component id='com.yahoo.vespa.hosted.controller.athenz.mock.AthenzClientFactoryMock'/>" + " <component id='com.yahoo.vespa.hosted.controller.api.integration.chef.ChefMock'/>" + " <component id='com.yahoo.vespa.hosted.controller.api.integration.dns.MemoryNameService'/>" + " <component id='com.yahoo.vespa.hosted.controller.api.integration.entity.MemoryEntityService'/>" + diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java index 13b1165ccb2..0c9ebedc09b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java @@ -10,17 +10,17 @@ import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.ConfigServerClientMock; import com.yahoo.vespa.hosted.controller.api.identifiers.AthensDomain; import com.yahoo.vespa.hosted.controller.api.identifiers.UserId; -import com.yahoo.vespa.hosted.controller.api.integration.athens.Athens; -import com.yahoo.vespa.hosted.controller.api.integration.athens.AthensPrincipal; -import com.yahoo.vespa.hosted.controller.api.integration.athens.mock.AthensDbMock; -import com.yahoo.vespa.hosted.controller.api.integration.athens.mock.AthensMock; -import com.yahoo.vespa.hosted.controller.api.integration.athens.mock.ZmsClientFactoryMock; import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException; import com.yahoo.vespa.hosted.controller.application.ApplicationPackage; import com.yahoo.vespa.hosted.controller.application.ClusterInfo; import com.yahoo.vespa.hosted.controller.application.ClusterUtilization; import com.yahoo.vespa.hosted.controller.application.Deployment; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs; +import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics; +import com.yahoo.vespa.hosted.controller.athenz.AthenzPrincipal; +import com.yahoo.vespa.hosted.controller.athenz.AthenzUtils; +import com.yahoo.vespa.hosted.controller.athenz.mock.AthensDbMock; +import com.yahoo.vespa.hosted.controller.athenz.mock.AthenzClientFactoryMock; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.restapi.ContainerControllerTester; import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; @@ -54,7 +54,7 @@ public class ApplicationApiTest extends ControllerContainerTest { .region("corp-us-east-1") .build(); private static final String athensUserDomain = "domain1"; - private static final String athensScrewdriverDomain = "screwdriver-domain"; + private static final String athensScrewdriverDomain = AthenzUtils.SCREWDRIVER_DOMAIN.id(); @Test @@ -173,7 +173,7 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", "", Request.Method.GET), new File("application.json")); // GET an application deployment - addMockObservedApplicationCost(controllerTester); + setDeploymentMaintainedInfo(controllerTester); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/corp-us-east-1/instance/default", "", Request.Method.GET), new File("deployment.json")); // POST a 'restart application' command @@ -671,14 +671,12 @@ public class ApplicationApiTest extends ControllerContainerTest { * mock setup to replicate the action. */ private AthensDomain addTenantAthensDomain(String domainName, String userName) { - Athens athens = (AthensMock) container.components().getComponent( - "com.yahoo.vespa.hosted.controller.api.integration.athens.mock.AthensMock" - ); - ZmsClientFactoryMock mock = (ZmsClientFactoryMock) athens.zmsClientFactory(); + AthenzClientFactoryMock mock = (AthenzClientFactoryMock) container.components() + .getComponent(AthenzClientFactoryMock.class.getName()); AthensDomain athensDomain = new AthensDomain(domainName); AthensDbMock.Domain domain = new AthensDbMock.Domain(athensDomain); domain.markAsVespaTenant(); - domain.admin(new AthensPrincipal(new AthensDomain(athensUserDomain), new UserId(userName))); + domain.admin(AthenzUtils.createPrincipal(new UserId(userName))); mock.getSetup().addDomain(domain); return athensDomain; } @@ -688,12 +686,10 @@ public class ApplicationApiTest extends ControllerContainerTest { * mock setup to replicate the action. */ private void addScrewdriverUserToDomain(String screwdriverUserId, String domainName) { - Athens athens = (AthensMock) container.components().getComponent( - "com.yahoo.vespa.hosted.controller.api.integration.athens.mock.AthensMock" - ); - ZmsClientFactoryMock mock = (ZmsClientFactoryMock) athens.zmsClientFactory(); + AthenzClientFactoryMock mock = (AthenzClientFactoryMock) container.components() + .getComponent(AthenzClientFactoryMock.class.getName()); AthensDbMock.Domain domain = mock.getSetup().domains.get(new AthensDomain(domainName)); - domain.admin(new AthensPrincipal(new AthensDomain(athensScrewdriverDomain), new UserId(screwdriverUserId))); + domain.admin(new AthenzPrincipal(new AthensDomain(athensScrewdriverDomain), new UserId(screwdriverUserId))); } private void startAndTestChange(ContainerControllerTester controllerTester, ApplicationId application, long projectId, @@ -732,7 +728,14 @@ public class ApplicationApiTest extends ControllerContainerTest { controllerTester.notifyJobCompletion(application, projectId, true, DeploymentJobs.JobType.stagingTest); } - private void addMockObservedApplicationCost(ContainerControllerTester controllerTester) { + /** + * Cluster info, utilization and deployment metrics are maintained async by maintainers. + * + * This sets these values as if the maintainers has been ran. + * + * @param controllerTester + */ + private void setDeploymentMaintainedInfo(ContainerControllerTester controllerTester) { for (Application application : controllerTester.controller().applications().asList()) { try (Lock lock = controllerTester.controller().applications().lock(application.id())) { for (Deployment deployment : application.deployments().values()) { @@ -740,11 +743,12 @@ public class ApplicationApiTest extends ControllerContainerTest { List<String> hostnames = new ArrayList<>(); hostnames.add("host1"); hostnames.add("host2"); - clusterInfo.put(ClusterSpec.Id.from("cluster1"), new ClusterInfo("flavor1", 37, ClusterSpec.Type.content, hostnames)); + clusterInfo.put(ClusterSpec.Id.from("cluster1"), new ClusterInfo("flavor1", 37, 2, 4, 50, ClusterSpec.Type.content, hostnames)); Map<ClusterSpec.Id, ClusterUtilization> clusterUtils = new HashMap<>(); clusterUtils.put(ClusterSpec.Id.from("cluster1"), new ClusterUtilization(0.3, 0.6, 0.4, 0.3)); deployment = deployment.withClusterInfo(clusterInfo); deployment = deployment.withClusterUtils(clusterUtils); + deployment = deployment.withMetrics(new DeploymentMetrics(1,2,3,4,5)); application = application.with(deployment); controllerTester.controller().applications().store(application, lock); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/MockAuthorizer.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/MockAuthorizer.java index 16557157cf5..6f8dfc681ac 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/MockAuthorizer.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/MockAuthorizer.java @@ -6,9 +6,10 @@ import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.TestIdentities; import com.yahoo.vespa.hosted.controller.api.identifiers.AthensDomain; import com.yahoo.vespa.hosted.controller.api.identifiers.UserId; -import com.yahoo.vespa.hosted.controller.api.integration.athens.AthensPrincipal; -import com.yahoo.vespa.hosted.controller.api.integration.athens.NToken; import com.yahoo.vespa.hosted.controller.api.integration.entity.EntityService; +import com.yahoo.vespa.hosted.controller.athenz.AthenzClientFactory; +import com.yahoo.vespa.hosted.controller.athenz.AthenzPrincipal; +import com.yahoo.vespa.hosted.controller.athenz.NToken; import javax.ws.rs.core.SecurityContext; import java.security.Principal; @@ -23,15 +24,15 @@ import java.util.Optional; @SuppressWarnings("unused") // injected public class MockAuthorizer extends Authorizer { - public MockAuthorizer(Controller controller, EntityService entityService) { - super(controller, entityService); + public MockAuthorizer(Controller controller, EntityService entityService, AthenzClientFactory athenzClientFactory) { + super(controller, entityService, athenzClientFactory); } /** Returns a principal given by the request parameters 'domain' and 'user' */ @Override public Optional<Principal> getPrincipalIfAny(HttpRequest request) { if (request.getProperty("user") == null) return Optional.empty(); - return Optional.of(new AthensPrincipal(new AthensDomain(request.getProperty("domain")), + return Optional.of(new AthenzPrincipal(new AthensDomain(request.getProperty("domain")), new UserId(request.getProperty("user")))); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json index 67fc48d4646..9174e7dd8b2 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json @@ -17,17 +17,21 @@ "gitBranch": "master", "gitCommit": "commit1", "cost": { - "tco": 37, - "utilization": 5.999999999999999, - "waste": 0.0, + "tco": 74, + "waste": 0, + "utilization": 2.999999999999999, "cluster": { "cluster1": { "count": 2, "resource": "cpu", "utilization": 2.999999999999999, - "tco": 37, - "flavor": "flavor1", + "tco": 74, "waste": 0, + "flavor": "flavor1", + "flavorCost":37.0, + "flavorCpu":2.0, + "flavorMem":4.0, + "flavorDisk":50.0, "type": "content", "util": { "cpu": 2.999999999999999, diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json index e974c315eb2..3633860772b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json @@ -4,10 +4,7 @@ "name": "DelayedDeployer" }, { - "name": "ClusterUtilizationMaintainer" - }, - { - "name":"BlockedChangeDeployer" + "name": "BlockedChangeDeployer" }, { "name": "Upgrader" @@ -16,25 +13,31 @@ "name": "FailureRedeployer" }, { - "name": "ClusterInfoMaintainer" + "name": "VersionStatusUpdater" }, { - "name": "DeploymentExpirer" + "name": "DeploymentIssueReporter" }, { - "name": "MetricsReporter" + "name": "DeploymentMetricsMaintainer" }, { - "name": "VersionStatusUpdater" + "name": "OutstandingChangeDeployer" }, { - "name": "DeploymentIssueReporter" + "name": "ClusterUtilizationMaintainer" }, { - "name": "OutstandingChangeDeployer" + "name": "ClusterInfoMaintainer" + }, + { + "name": "DeploymentExpirer" + }, + { + "name": "MetricsReporter" } ], "inactive": [ "DeploymentExpirer" ] -} +}
\ No newline at end of file diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiTest.java index c002c7fb24a..55a4b46f4a7 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiTest.java @@ -85,7 +85,8 @@ public class DeploymentApiTest extends ControllerContainerTest { version.releasedAt(), version.isCurrentSystemVersion(), ImmutableSet.of("config1.test", "config2.test"), - controller); + VespaVersion.confidenceFrom(version.statistics(), controller, version.releasedAt()) + ); censored.add(version); } return new VersionStatus(censored); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/root.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/root.json index c2e83373cf7..00bd1ed8208 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/root.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/root.json @@ -2,7 +2,7 @@ "versions": [ { "version": "(ignore)", - "confidence": "normal", + "confidence": "high", "commit": "(ignore)", "date": 0, "controllerVersion": false, diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java index 7bbbf8f0499..17935906186 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java @@ -6,6 +6,7 @@ import com.yahoo.component.Vtag; import com.yahoo.config.provision.Environment; import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.ApplicationController; +import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.api.identifiers.TenantId; import com.yahoo.vespa.hosted.controller.application.ApplicationPackage; @@ -13,6 +14,7 @@ import com.yahoo.vespa.hosted.controller.application.DeploymentJobs; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobError; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; +import com.yahoo.vespa.hosted.controller.versions.VespaVersion.Confidence; import org.junit.Test; import java.net.URI; @@ -46,7 +48,7 @@ public class VersionStatusTest { public void testSystemVersionIsControllerVersionIfConfigserversAreNewer() { ControllerTester tester = new ControllerTester(); Version largerThanCurrent = new Version(Vtag.currentVersion.getMajor() + 1); - tester.configServer().setDefaultConfigServerVersion(largerThanCurrent); + tester.configServer().setDefaultVersion(largerThanCurrent); VersionStatus versionStatus = VersionStatus.compute(tester.controller()); assertEquals(Vtag.currentVersion, versionStatus.systemVersion().get().versionNumber()); } @@ -55,7 +57,7 @@ public class VersionStatusTest { public void testSystemVersionIsVersionOfOldestConfigServer() throws URISyntaxException { ControllerTester tester = new ControllerTester(); Version oldest = new Version(5); - tester.configServer().configServerVersions().put(new URI("http://cfg.prod.corp-us-east-1.test"), oldest); + tester.configServer().versions().put(new URI("http://cfg.prod.corp-us-east-1.test"), oldest); VersionStatus versionStatus = VersionStatus.compute(tester.controller()); assertEquals(oldest, versionStatus.systemVersion().get().versionNumber()); } @@ -70,7 +72,6 @@ public class VersionStatusTest { .region("us-east-3") .build(); - // Application versions which are older than the current version Version version1 = new Version("5.1"); Version version2 = new Version("5.2"); tester.upgradeSystem(version1); @@ -90,14 +91,9 @@ public class VersionStatusTest { // - app3 is in production on version1, but then fails in staging test on version2 tester.completeUpgradeWithError(app3, version2, applicationPackage, stagingTest); - VersionStatus versionStatus = VersionStatus.compute(tester.controller()); - List<VespaVersion> versions = versionStatus.versions(); - assertEquals("The version of this controller, the default config server version, plus the two versions above exist", 4, versions.size()); - - VespaVersion v0 = versions.get(2); - assertEquals(tester.configServer().getDefaultConfigServerVersion(), v0.versionNumber()); - assertEquals(0, v0.statistics().failing().size()); - assertEquals(0, v0.statistics().production().size()); + tester.updateVersionStatus(); + List<VespaVersion> versions = tester.controller().versionStatus().versions(); + assertEquals("The two versions above exist", 2, versions.size()); VespaVersion v1 = versions.get(0); assertEquals(version1, v1.versionNumber()); @@ -116,11 +112,6 @@ public class VersionStatusTest { // Only one application is on v2 in at least one zone assertEquals(1, v2.statistics().production().size()); assertTrue(v2.statistics().production().contains(app2.id())); - - VespaVersion v3 = versions.get(3); - assertEquals(Vtag.currentVersion, v3.versionNumber()); - assertEquals(0, v3.statistics().failing().size()); - assertEquals(0, v3.statistics().production().size()); } @Test @@ -130,7 +121,7 @@ public class VersionStatusTest { Version version0 = new Version("5.0"); tester.upgradeSystem(version0); - // Setup applications + // Setup applications - all running on version0 Application canary0 = tester.createAndDeploy("canary0", 1, "canary"); Application canary1 = tester.createAndDeploy("canary1", 2, "canary"); Application canary2 = tester.createAndDeploy("canary2", 3, "canary"); @@ -146,8 +137,7 @@ public class VersionStatusTest { Application default9 = tester.createAndDeploy("default9", 13, "default"); Application conservative0 = tester.createAndDeploy("conservative1", 14, "conservative"); - - // The following applications should not affect confidence calculation: + // Applications that do not affect confidence calculation: // Application without deployment Application ignored0 = tester.createApplication("ignored0", "tenant1", 1000, 1000L); @@ -157,30 +147,40 @@ public class VersionStatusTest { "ignored1", "default-pr42", 1000); + assertEquals("All applications running on this version: High", + Confidence.high, confidence(tester.controller(), version0)); + + // New version is released Version version1 = new Version("5.1"); - Version version2 = new Version("5.2"); tester.upgradeSystem(version1); // Canaries upgrade to new versions and fail tester.completeUpgrade(canary0, version1, "canary"); tester.completeUpgradeWithError(canary1, version1, "canary", productionUsWest1); - tester.upgradeSystem(version2); - tester.completeUpgrade(canary2, version2, "canary"); - - VersionStatus versionStatus = VersionStatus.compute(tester.controller()); - List<VespaVersion> versions = versionStatus.versions(); - + tester.updateVersionStatus(); assertEquals("One canary failed: Broken", - VespaVersion.Confidence.broken, confidence(versions, version1)); - assertEquals("Nothing has failed but not all canaries has deployed: Low", - VespaVersion.Confidence.low, confidence(versions, version2)); - assertEquals("Current version of this - no deployments: Low", - VespaVersion.Confidence.low, confidence(versions, Vtag.currentVersion)); + Confidence.broken, confidence(tester.controller(), version1)); - // All canaries are upgraded to version2 which raises confidence to normal and more apps upgrade + // New version is released + Version version2 = new Version("5.2"); + tester.upgradeSystem(version2); + assertEquals("Confidence defaults to low for version with no applications", + Confidence.low, confidence(tester.controller(), version2)); + + // All canaries upgrade successfully tester.completeUpgrade(canary0, version2, "canary"); tester.completeUpgrade(canary1, version2, "canary"); + + assertEquals("Confidence for remains unchanged for version1: Broken", + Confidence.broken, confidence(tester.controller(), version1)); + assertEquals("Nothing has failed but not all canaries have upgraded: Low", + Confidence.low, confidence(tester.controller(), version2)); + + // Remaining canary upgrades to version2 which raises confidence to normal and more apps upgrade + tester.completeUpgrade(canary2, version2, "canary"); tester.upgradeSystem(version2); + assertEquals("Canaries have upgraded: Normal", + Confidence.normal, confidence(tester.controller(), version2)); tester.completeUpgrade(default0, version2, "default"); tester.completeUpgrade(default1, version2, "default"); tester.completeUpgrade(default2, version2, "default"); @@ -189,29 +189,27 @@ public class VersionStatusTest { tester.completeUpgrade(default5, version2, "default"); tester.completeUpgrade(default6, version2, "default"); tester.completeUpgrade(default7, version2, "default"); + tester.updateVersionStatus(); - versionStatus = VersionStatus.compute(tester.controller()); - versions = versionStatus.versions(); + // Remember confidence across restart + tester.restartController(); - assertEquals("No deployments: Low", - VespaVersion.Confidence.low, confidence(versions, version0)); + assertEquals("Confidence remains unchanged for version0: High", + Confidence.high, confidence(tester.controller(), version0)); assertEquals("All canaries deployed + < 90% of defaults: Normal", - VespaVersion.Confidence.normal, confidence(versions, version2)); - assertEquals("Current version of this - no deployments: Low", - VespaVersion.Confidence.low, confidence(versions, Vtag.currentVersion)); + Confidence.normal, confidence(tester.controller(), version2)); + assertTrue("Status for version without applications is removed", + tester.controller().versionStatus().versions().stream() + .noneMatch(vespaVersion -> vespaVersion.versionNumber().equals(version1))); // Another default application upgrades, raising confidence to high tester.completeUpgrade(default8, version2, "default"); + tester.updateVersionStatus(); - versionStatus = VersionStatus.compute(tester.controller()); - versions = versionStatus.versions(); - - assertEquals("No deployments: Low", - VespaVersion.Confidence.low, confidence(versions, version0)); + assertEquals("Confidence remains unchanged for version0: High", + Confidence.high, confidence(tester.controller(), version0)); assertEquals("90% of defaults deployed successfully: High", - VespaVersion.Confidence.high, confidence(versions, version2)); - assertEquals("Current version of this - no deployments: Low", - VespaVersion.Confidence.low, confidence(versions, Vtag.currentVersion)); + VespaVersion.Confidence.high, confidence(tester.controller(), version2)); // A new version is released, all canaries upgrade successfully, but enough "default" apps fail to mark version // as broken @@ -225,16 +223,14 @@ public class VersionStatusTest { tester.completeUpgradeWithError(default1, version3, "default", stagingTest); tester.completeUpgradeWithError(default2, version3, "default", stagingTest); tester.completeUpgradeWithError(default9, version3, "default", stagingTest); + tester.updateVersionStatus(); - versionStatus = VersionStatus.compute(tester.controller()); - versions = versionStatus.versions(); - - assertEquals("No deployments: Low", - VespaVersion.Confidence.low, confidence(versions, version0)); + assertEquals("Confidence remains unchanged for version0: High", + Confidence.high, confidence(tester.controller(), version0)); + assertEquals("Confidence remains unchanged for version2: High", + Confidence.high, confidence(tester.controller(), version2)); assertEquals("40% of defaults failed: Broken", - VespaVersion.Confidence.broken, confidence(versions, version3)); - assertEquals("Current version of this - no deployments: Low", - VespaVersion.Confidence.low, confidence(versions, Vtag.currentVersion)); + VespaVersion.Confidence.broken, confidence(tester.controller(), version3)); } @Test @@ -260,8 +256,8 @@ public class VersionStatusTest { vespaVersions.stream().noneMatch(v -> v.versionNumber().equals(versionWithUnknownTag))); } - private VespaVersion.Confidence confidence(List<VespaVersion> versions, Version version) { - return versions.stream() + private Confidence confidence(Controller controller, Version version) { + return controller.versionStatus().versions().stream() .filter(v -> v.statistics().version().equals(version)) .findFirst() .map(VespaVersion::confidence) |