diff options
Diffstat (limited to 'configserver/src/test/java')
36 files changed, 871 insertions, 192 deletions
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java index e0fa760b35d..9b76c349259 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java @@ -1,7 +1,6 @@ // Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.config.server; -import com.github.tomakehurst.wiremock.junit.WireMockRule; import com.google.common.io.Files; import com.yahoo.cloud.config.ConfigserverConfig; import com.yahoo.component.Version; @@ -21,6 +20,7 @@ import com.yahoo.test.ManualClock; import com.yahoo.text.Utf8; import com.yahoo.vespa.config.server.application.OrchestratorMock; import com.yahoo.vespa.config.server.deploy.DeployTester; +import com.yahoo.vespa.config.server.http.LogRetriever; import com.yahoo.vespa.config.server.http.SessionHandlerTest; import com.yahoo.vespa.config.server.http.v2.PrepareResult; import com.yahoo.vespa.config.server.session.LocalSession; @@ -37,6 +37,8 @@ import org.junit.rules.TemporaryFolder; import java.io.File; import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import java.time.Clock; import java.time.Duration; import java.time.Instant; @@ -46,17 +48,11 @@ import java.util.Collections; import java.util.Optional; import java.util.Set; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; -import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; /** @@ -83,21 +79,22 @@ public class ApplicationRepositoryTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); - @Rule - public final WireMockRule wireMock = new WireMockRule(options().port(8080), true); - @Before public void setup() { Curator curator = new MockCurator(); tenantRepository = new TenantRepository(new TestComponentRegistry.Builder() .curator(curator) .build()); + tenantRepository.addTenant(TenantRepository.HOSTED_VESPA_TENANT); tenantRepository.addTenant(tenant1); tenantRepository.addTenant(tenant2); tenantRepository.addTenant(tenant3); orchestrator = new OrchestratorMock(); provisioner = new SessionHandlerTest.MockProvisioner(); - applicationRepository = new ApplicationRepository(tenantRepository, provisioner, orchestrator, clock); + applicationRepository = new ApplicationRepository(tenantRepository, + provisioner, + orchestrator, + clock); timeoutBudget = new TimeoutBudget(clock, Duration.ofSeconds(60)); } @@ -151,7 +148,11 @@ public class ApplicationRepositoryTest { @Test public void getLogs() { - wireMock.stubFor(get(urlEqualTo("/logs")).willReturn(aResponse().withStatus(200))); + applicationRepository = new ApplicationRepository(tenantRepository, + provisioner, + orchestrator, + new MockLogRetriever(), + clock); deployApp(testAppLogServerWithContainer); HttpResponse response = applicationRepository.getLogs(applicationId(), Optional.empty(), ""); assertEquals(200, response.getStatus()); @@ -159,14 +160,24 @@ public class ApplicationRepositoryTest { @Test public void getLogsForHostname() { - wireMock.stubFor(get(urlEqualTo("/logs")).willReturn(aResponse().withStatus(200))); - deployApp(testAppLogServerWithContainer); - HttpResponse response = applicationRepository.getLogs(applicationId(), Optional.of("localhost"), ""); + applicationRepository = new ApplicationRepository(tenantRepository, + provisioner, + orchestrator, + new MockLogRetriever(), + clock); + ApplicationId applicationId = ApplicationId.from("hosted-vespa", "tenant-host", "default"); + deployApp(testAppLogServerWithContainer, new PrepareParams.Builder().applicationId(applicationId).build()); + HttpResponse response = applicationRepository.getLogs(applicationId, Optional.of("localhost"), ""); assertEquals(200, response.getStatus()); } @Test(expected = IllegalArgumentException.class) public void refuseToGetLogsFromHostnameNotInApplication() { + applicationRepository = new ApplicationRepository(tenantRepository, + provisioner, + orchestrator, + new MockLogRetriever(), + clock); deployApp(testAppLogServerWithContainer); HttpResponse response = applicationRepository.getLogs(applicationId(), Optional.of("host123.fake.yahoo.com"), ""); assertEquals(200, response.getStatus()); @@ -273,8 +284,8 @@ public class ApplicationRepositoryTest { assertNull(tenant.getLocalSessionRepo().getSession(sessionId)); assertNull(tenant.getRemoteSessionRepo().getSession(sessionId)); assertTrue(provisioner.removed); - assertThat(provisioner.lastApplicationId.tenant(), is(tenant.getName())); - assertThat(provisioner.lastApplicationId, is(applicationId())); + assertEquals(tenant.getName(), provisioner.lastApplicationId.tenant()); + assertEquals(applicationId(), provisioner.lastApplicationId); assertFalse(applicationRepository.delete(applicationId())); } @@ -292,7 +303,7 @@ public class ApplicationRepositoryTest { // Delete app with id fooId, should not affect original app assertTrue(applicationRepository.delete(fooId)); - assertThat(provisioner.lastApplicationId, is(fooId)); + assertEquals(fooId, provisioner.lastApplicationId); assertNotNull(applicationRepository.getActiveSession(applicationId())); assertTrue(applicationRepository.delete(applicationId())); @@ -332,7 +343,7 @@ public class ApplicationRepositoryTest { // All sessions except 3 should be removed after the call to deleteExpiredLocalSessions tester.applicationRepository().deleteExpiredLocalSessions(); - final Collection<LocalSession> sessions = tester.tenant().getLocalSessionRepo().listSessions(); + Collection<LocalSession> sessions = tester.tenant().getLocalSessionRepo().listSessions(); assertEquals(1, sessions.size()); assertEquals(3, new ArrayList<>(sessions).get(0).getSessionId()); @@ -372,4 +383,28 @@ public class ApplicationRepositoryTest { Tenant tenant = tenantRepository.getTenant(applicationId.tenant()); return applicationRepository.getMetadataFromSession(tenant, sessionId); } + + private static class MockLogRetriever extends LogRetriever { + + @Override + public HttpResponse getLogs(String logServerHostname) { + return new MockHttpResponse(); + } + + private static class MockHttpResponse extends HttpResponse { + + private MockHttpResponse() { + super(200); + } + + @Override + public void render(OutputStream outputStream) throws IOException { + outputStream.write("log line".getBytes(StandardCharsets.UTF_8)); + } + + } + + + } + } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ConfigServerBootstrapTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ConfigServerBootstrapTest.java index 2e7b5a1f4d9..0b56591d6a1 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/ConfigServerBootstrapTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ConfigServerBootstrapTest.java @@ -12,7 +12,6 @@ import com.yahoo.config.provision.RegionName; import com.yahoo.component.Version; import com.yahoo.config.provision.Zone; import com.yahoo.container.QrSearchersConfig; -import com.yahoo.container.core.VipStatusConfig; import com.yahoo.container.handler.ClustersStatus; import com.yahoo.container.handler.VipStatus; import com.yahoo.container.jdisc.config.HealthMonitorConfig; @@ -61,7 +60,7 @@ public class ConfigServerBootstrapTest { @Test public void testBootstrap() throws Exception { ConfigserverConfig configserverConfig = createConfigserverConfig(temporaryFolder); - InMemoryProvisioner provisioner = new InMemoryProvisioner(true, "host0", "host1", "host3"); + InMemoryProvisioner provisioner = new InMemoryProvisioner(true, "host0", "host1", "host3", "host4"); DeployTester tester = new DeployTester(configserverConfig, provisioner); tester.deployApp("src/test/apps/hosted/"); @@ -94,7 +93,7 @@ public class ConfigServerBootstrapTest { @Test public void testBootstrapWithVipStatusFile() throws Exception { ConfigserverConfig configserverConfig = createConfigserverConfig(temporaryFolder); - InMemoryProvisioner provisioner = new InMemoryProvisioner(true, "host0", "host1", "host3"); + InMemoryProvisioner provisioner = new InMemoryProvisioner(true, "host0", "host1", "host3", "host4"); DeployTester tester = new DeployTester(configserverConfig, provisioner); tester.deployApp("src/test/apps/hosted/"); diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistryTest.java index 476f77ae1db..e4ff8702ff1 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistryTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistryTest.java @@ -14,6 +14,8 @@ import com.yahoo.vespa.config.server.modelfactory.ModelFactoryRegistry; import com.yahoo.vespa.config.server.monitoring.Metrics; import com.yahoo.vespa.config.server.provision.HostProvisionerProvider; import com.yahoo.vespa.config.server.rpc.RpcServer; +import com.yahoo.vespa.config.server.rpc.RpcRequestHandlerProvider; +import com.yahoo.vespa.config.server.rpc.security.NoopRpcAuthorizer; import com.yahoo.vespa.config.server.session.SessionPreparer; import com.yahoo.vespa.config.server.session.SessionTest; import com.yahoo.vespa.config.server.zookeeper.ConfigCurator; @@ -66,7 +68,7 @@ public class InjectedGlobalComponentRegistryTest { .configDefinitionsDir(temporaryFolder.newFolder("configdefinitions").getAbsolutePath())); sessionPreparer = new SessionTest.MockSessionPreparer(); rpcServer = new RpcServer(configserverConfig, null, Metrics.createTestMetrics(), - new HostRegistries(), new ConfigRequestHostLivenessTracker(), new FileServer(temporaryFolder.newFolder("filereferences"))); + new HostRegistries(), new ConfigRequestHostLivenessTracker(), new FileServer(temporaryFolder.newFolder("filereferences")), new NoopRpcAuthorizer(), new RpcRequestHandlerProvider()); generationCounter = new SuperModelGenerationCounter(curator); defRepo = new StaticConfigDefinitionRepo(); permanentApplicationPackage = new PermanentApplicationPackage(configserverConfig); @@ -76,7 +78,7 @@ public class InjectedGlobalComponentRegistryTest { globalComponentRegistry = new InjectedGlobalComponentRegistry(curator, configCurator, metrics, modelFactoryRegistry, sessionPreparer, rpcServer, configserverConfig, generationCounter, defRepo, permanentApplicationPackage, hostRegistries, hostProvisionerProvider, zone, - new ConfigServerDB(configserverConfig), new InMemoryFlagSource()); + new ConfigServerDB(configserverConfig), new InMemoryFlagSource(), new MockSecretStore()); } @Test @@ -88,7 +90,6 @@ public class InjectedGlobalComponentRegistryTest { assertThat(globalComponentRegistry.getConfigserverConfig(), is(configserverConfig)); assertThat(globalComponentRegistry.getReloadListener().hashCode(), is(rpcServer.hashCode())); assertThat(globalComponentRegistry.getTenantListener().hashCode(), is(rpcServer.hashCode())); - assertThat(globalComponentRegistry.getSuperModelGenerationCounter(), is(generationCounter)); assertThat(globalComponentRegistry.getStaticConfigDefinitionRepo(), is(defRepo)); assertThat(globalComponentRegistry.getPermanentApplicationPackage(), is(permanentApplicationPackage)); assertThat(globalComponentRegistry.getHostRegistries(), is(hostRegistries)); diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/MockSecretStore.java b/configserver/src/test/java/com/yahoo/vespa/config/server/MockSecretStore.java new file mode 100644 index 00000000000..8a77b53875e --- /dev/null +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/MockSecretStore.java @@ -0,0 +1,35 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.server; + +import com.yahoo.container.jdisc.secretstore.SecretStore; + +import java.util.HashMap; +import java.util.Map; + +public class MockSecretStore implements SecretStore { + Map<String, String> secrets = new HashMap<>(); + + @Override + public String getSecret(String key) { + if(secrets.containsKey(key)) + return secrets.get(key); + throw new RuntimeException("Key not found: " + key); + } + + @Override + public String getSecret(String key, int version) { + return getSecret(key); + } + + public void put(String key, String value) { + secrets.put(key, value); + } + + public void remove(String key) { + secrets.remove(key); + } + + public void clear() { + secrets.clear(); + } +} diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java index 1b4ed2283ba..860bbdc134c 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java @@ -2,6 +2,7 @@ package com.yahoo.vespa.config.server; import com.yahoo.component.Version; +import com.yahoo.config.model.api.ContainerEndpoint; import com.yahoo.config.model.api.ModelContext; import com.yahoo.config.model.application.provider.BaseDeployLogger; import com.yahoo.config.model.application.provider.MockFileRegistry; @@ -14,6 +15,7 @@ import com.yahoo.vespa.flags.InMemoryFlagSource; import org.junit.Test; import java.util.Collections; +import java.util.List; import java.util.Optional; import java.util.Set; @@ -33,6 +35,10 @@ public class ModelContextImplTest { final Rotation rotation = new Rotation("this.is.a.mock.rotation"); final Set<Rotation> rotations = Collections.singleton(rotation); + + final ContainerEndpoint endpoint = new ContainerEndpoint("foo", List.of("a", "b")); + final Set<ContainerEndpoint> endpoints = Collections.singleton(endpoint); + final InMemoryFlagSource flagSource = new InMemoryFlagSource(); ModelContext context = new ModelContextImpl( @@ -53,9 +59,11 @@ public class ModelContextImplTest { false, Zone.defaultZone(), rotations, + endpoints, false, false, - flagSource), + flagSource, + null), Optional.empty(), new Version(6), new Version(6)); @@ -71,7 +79,8 @@ public class ModelContextImplTest { assertNotNull(context.properties().zone()); assertFalse(context.properties().hostedVespa()); assertThat(context.properties().rotations(), equalTo(rotations)); + assertThat(context.properties().endpoints(), equalTo(endpoints)); assertThat(context.properties().isFirstTimeDeployment(), equalTo(false)); - assertThat(context.properties().useDedicatedNodeForLogserver(), equalTo(false)); + assertThat(context.properties().useDedicatedNodeForLogserver(), equalTo(true)); } } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelRequestHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelRequestHandlerTest.java index 35ad97b7b43..f7b900c8f02 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelRequestHandlerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/SuperModelRequestHandlerTest.java @@ -56,17 +56,16 @@ public class SuperModelRequestHandlerTest { ApplicationId bar = applicationId("a", "foo"); assertNotNull(controller.getHandler()); - long gen = counter.increment(); + long gen = counter.get(); controller.reloadConfig(createApp(foo, 3l)); assertNotNull(controller.getHandler()); - assertThat(controller.getHandler().getGeneration(), is(gen)); + assertThat(controller.getHandler().getGeneration(), is(gen + 1)); controller.reloadConfig(createApp(foo, 4l)); - assertThat(controller.getHandler().getGeneration(), is(gen)); + assertThat(controller.getHandler().getGeneration(), is(gen + 2)); // Test that a new app is used when there already exist an application with the same id assertThat(controller.getHandler().getSuperModel().applicationModels().get(foo).getGeneration(), is(4l)); - gen = counter.increment(); controller.reloadConfig(createApp(bar, 2l)); - assertThat(controller.getHandler().getGeneration(), is(gen)); + assertThat(controller.getHandler().getGeneration(), is(gen + 3)); } @Test @@ -75,22 +74,21 @@ public class SuperModelRequestHandlerTest { ApplicationId bar = applicationId("a", "bar"); ApplicationId baz = applicationId("b", "baz"); - long gen = counter.increment(); + long gen = counter.get(); controller.reloadConfig(createApp(foo, 3l)); controller.reloadConfig(createApp(bar, 30l)); controller.reloadConfig(createApp(baz, 9l)); - assertThat(controller.getHandler().getGeneration(), is(gen)); + assertThat(controller.getHandler().getGeneration(), is(gen + 3)); assertThat(controller.getHandler().getSuperModel().applicationModels().size(), is(3)); assertEquals(Arrays.asList(foo, bar, baz), new ArrayList<>(controller.getHandler().getSuperModel().applicationModels().keySet())); controller.removeApplication(new ApplicationId.Builder().tenant("a").applicationName("unknown").build()); - assertThat(controller.getHandler().getGeneration(), is(gen)); + assertThat(controller.getHandler().getGeneration(), is(gen + 4)); assertThat(controller.getHandler().getSuperModel().applicationModels().size(), is(3)); assertEquals(Arrays.asList(foo, bar, baz), new ArrayList<>(controller.getHandler().getSuperModel().applicationModels().keySet())); - gen = counter.increment(); controller.removeApplication(bar); assertThat(controller.getHandler().getSuperModel().applicationModels().size(), is(2)); assertEquals(Arrays.asList(foo, baz), new ArrayList<>(controller.getHandler().getSuperModel().applicationModels().keySet())); - assertThat(controller.getHandler().getGeneration(), is(gen)); + assertThat(controller.getHandler().getGeneration(), is(gen + 5)); } @Test @@ -101,9 +99,9 @@ public class SuperModelRequestHandlerTest { manager = new SuperModelManager(configserverConfig, emptyNodeFlavors(), counter, new InMemoryFlagSource()); controller = new SuperModelRequestHandler(new TestConfigDefinitionRepo(), configserverConfig, manager); - long gen = counter.increment(); + long gen = counter.get(); controller.reloadConfig(createApp(foo, 3L)); - assertThat(controller.getHandler().getGeneration(), is(masterGen + gen)); + assertThat(controller.getHandler().getGeneration(), is(masterGen + gen + 1)); } @Test diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/TestComponentRegistry.java b/configserver/src/test/java/com/yahoo/vespa/config/server/TestComponentRegistry.java index ef1f0f380e3..a304f74858b 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/TestComponentRegistry.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/TestComponentRegistry.java @@ -3,10 +3,14 @@ package com.yahoo.vespa.config.server; import com.google.common.io.Files; import com.yahoo.cloud.config.ConfigserverConfig; +import com.yahoo.concurrent.InThreadExecutorService; +import com.yahoo.concurrent.StripedExecutor; import com.yahoo.config.model.NullConfigModelRegistry; import com.yahoo.config.model.api.ConfigDefinitionRepo; import com.yahoo.config.provision.Provisioner; +import com.yahoo.config.provision.TenantName; import com.yahoo.config.provision.Zone; +import com.yahoo.container.jdisc.secretstore.SecretStore; import com.yahoo.vespa.config.server.application.PermanentApplicationPackage; import com.yahoo.vespa.config.server.host.HostRegistries; import com.yahoo.vespa.config.server.modelfactory.ModelFactoryRegistry; @@ -28,6 +32,7 @@ import com.yahoo.vespa.model.VespaModelFactory; import java.time.Clock; import java.util.Collections; import java.util.Optional; +import java.util.concurrent.ExecutorService; /** @@ -40,7 +45,6 @@ public class TestComponentRegistry implements GlobalComponentRegistry { private final Metrics metrics; private final SessionPreparer sessionPreparer; private final ConfigserverConfig configserverConfig; - private final SuperModelGenerationCounter superModelGenerationCounter; private final ConfigDefinitionRepo defRepo; private final ReloadListener reloadListener; private final TenantListener tenantListener; @@ -52,6 +56,9 @@ public class TestComponentRegistry implements GlobalComponentRegistry { private final Zone zone; private final Clock clock; private final ConfigServerDB configServerDB; + private final StripedExecutor<TenantName> zkWatcherExecutor; + private final ExecutorService zkCacheExecutor; + private final SecretStore secretStore; private TestComponentRegistry(Curator curator, ConfigCurator configCurator, Metrics metrics, ModelFactoryRegistry modelFactoryRegistry, @@ -65,14 +72,14 @@ public class TestComponentRegistry implements GlobalComponentRegistry { ReloadListener reloadListener, TenantListener tenantListener, Zone zone, - Clock clock) { + Clock clock, + SecretStore secretStore) { this.curator = curator; this.configCurator = configCurator; this.metrics = metrics; this.configserverConfig = configserverConfig; this.reloadListener = reloadListener; this.tenantListener = tenantListener; - this.superModelGenerationCounter = new SuperModelGenerationCounter(curator); this.defRepo = defRepo; this.permanentApplicationPackage = permanentApplicationPackage; this.hostRegistries = hostRegistries; @@ -83,6 +90,9 @@ public class TestComponentRegistry implements GlobalComponentRegistry { this.zone = zone; this.clock = clock; this.configServerDB = new ConfigServerDB(configserverConfig); + this.zkWatcherExecutor = new StripedExecutor<>(new InThreadExecutorService()); + this.zkCacheExecutor = new InThreadExecutorService(); + this.secretStore = secretStore; } public static class Builder { @@ -92,6 +102,7 @@ public class TestComponentRegistry implements GlobalComponentRegistry { private ConfigserverConfig configserverConfig = new ConfigserverConfig( new ConfigserverConfig.Builder() .configServerDBDir(Files.createTempDir().getAbsolutePath()) + .sessionLifetime(5) .configDefinitionsDir(Files.createTempDir().getAbsolutePath())); private ConfigDefinitionRepo defRepo = new StaticConfigDefinitionRepo(); private TenantRequestHandlerTest.MockReloadListener reloadListener = new TenantRequestHandlerTest.MockReloadListener(); @@ -151,14 +162,15 @@ public class TestComponentRegistry implements GlobalComponentRegistry { .orElse(new MockFileDistributionFactory(configserverConfig)); HostProvisionerProvider hostProvisionerProvider = hostProvisioner. map(HostProvisionerProvider::withProvisioner).orElseGet(HostProvisionerProvider::empty); + SecretStore secretStore = new MockSecretStore(); SessionPreparer sessionPreparer = new SessionPreparer(modelFactoryRegistry, fileDistributionFactory, hostProvisionerProvider, permApp, configserverConfig, defRepo, curator, - zone, new InMemoryFlagSource()); + zone, new InMemoryFlagSource(), secretStore); return new TestComponentRegistry(curator, ConfigCurator.create(curator), metrics, modelFactoryRegistry, permApp, fileDistributionFactory, hostRegistries, configserverConfig, sessionPreparer, hostProvisioner, defRepo, reloadListener, tenantListener, - zone, clock); + zone, clock, secretStore); } } @@ -177,8 +189,6 @@ public class TestComponentRegistry implements GlobalComponentRegistry { @Override public ReloadListener getReloadListener() { return reloadListener; } @Override - public SuperModelGenerationCounter getSuperModelGenerationCounter() { return superModelGenerationCounter; } - @Override public ConfigDefinitionRepo getStaticConfigDefinitionRepo() { return defRepo; } @Override public PermanentApplicationPackage getPermanentApplicationPackage() { return permanentApplicationPackage; } @@ -198,9 +208,25 @@ public class TestComponentRegistry implements GlobalComponentRegistry { public Clock getClock() { return clock;} @Override public ConfigServerDB getConfigServerDB() { return configServerDB;} + + @Override + public StripedExecutor<TenantName> getZkWatcherExecutor() { + return zkWatcherExecutor; + } + @Override public FlagSource getFlagSource() { return new InMemoryFlagSource(); } + @Override + public ExecutorService getZkCacheExecutor() { + return zkCacheExecutor; + } + + @Override + public SecretStore getSecretStore() { + return secretStore; + } + public FileDistributionFactory getFileDistributionFactory() { return fileDistributionFactory; } } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/CompressedApplicationInputStreamTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/CompressedApplicationInputStreamTest.java index 7faf33b2ff2..496da2cf809 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/CompressedApplicationInputStreamTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/CompressedApplicationInputStreamTest.java @@ -1,8 +1,9 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.config.server.http; +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.server.application; import com.google.common.collect.ImmutableList; import com.google.common.io.ByteStreams; +import com.yahoo.vespa.config.server.application.CompressedApplicationInputStream; import org.apache.commons.compress.archivers.ArchiveOutputStream; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; @@ -26,7 +27,6 @@ import static org.junit.Assert.assertTrue; /** * @author Ulf Lilleengen - * @since 5.1 */ public class CompressedApplicationInputStreamTest { diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java index ac7ff1e85c5..e3335dded4c 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java @@ -6,6 +6,7 @@ import com.yahoo.config.provision.TenantName; import com.yahoo.text.Utf8; import com.yahoo.vespa.config.server.MockReloadHandler; +import com.yahoo.vespa.config.server.TestComponentRegistry; import com.yahoo.vespa.config.server.tenant.TenantRepository; import com.yahoo.vespa.curator.Curator; import com.yahoo.vespa.curator.mock.MockCurator; @@ -115,7 +116,7 @@ public class TenantApplicationsTest { } private TenantApplications createZKAppRepo(MockReloadHandler reloadHandler) { - return TenantApplications.create(curator, reloadHandler, tenantName); + return TenantApplications.create(new TestComponentRegistry.Builder().curator(curator).build(), reloadHandler, tenantName); } private static ApplicationId createApplicationId(String name) { diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java index 6b67dcc4e9a..f619bd92bef 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java @@ -30,6 +30,7 @@ import com.yahoo.vespa.config.server.ApplicationRepository; import com.yahoo.vespa.config.server.TestComponentRegistry; import com.yahoo.vespa.config.server.TimeoutBudget; import com.yahoo.vespa.config.server.application.OrchestratorMock; +import com.yahoo.vespa.config.server.http.LogRetriever; import com.yahoo.vespa.config.server.http.v2.PrepareResult; import com.yahoo.vespa.config.server.modelfactory.ModelFactoryRegistry; import com.yahoo.vespa.config.server.monitoring.Metrics; @@ -126,8 +127,9 @@ public class DeployTester { applicationRepository = new ApplicationRepository(tenantRepository, new ProvisionerAdapter(provisioner), new OrchestratorMock(), - clock, - configserverConfig); + configserverConfig, + new LogRetriever(), + clock); } public Tenant tenant() { diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java index c5d1e8dc0a1..b363c749212 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java @@ -15,11 +15,8 @@ import com.yahoo.config.model.provision.Host; import com.yahoo.config.model.provision.Hosts; import com.yahoo.config.model.provision.InMemoryProvisioner; import com.yahoo.config.provision.ApplicationId; -import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; -import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.RegionName; -import com.yahoo.config.provision.RotationName; import com.yahoo.config.provision.Zone; import com.yahoo.test.ManualClock; import com.yahoo.vespa.config.server.configchange.MockRestartAction; @@ -39,7 +36,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -97,7 +93,7 @@ public class HostedDeployTest { DeployTester.createModelFactory(Version.fromString("7.0.0"), clock)); DeployTester tester = new DeployTester(modelFactories, createConfigserverConfig(), clock, Zone.defaultZone()); tester.deployApp("src/test/apps/hosted/", "6.2.0", Instant.now()); - assertEquals(3, tester.getAllocatedHostsOf(tester.applicationId()).getHosts().size()); + assertEquals(4, tester.getAllocatedHostsOf(tester.applicationId()).getHosts().size()); } /** @@ -108,7 +104,8 @@ public class HostedDeployTest { public void testCreateOnlyNeededModelVersions() { List<Host> hosts = Arrays.asList(createHost("host1", "6.0.0"), createHost("host2", "6.1.0"), - createHost("host3")); //Use a host with no version as well + createHost("host3"), //Use a host with no version as well + createHost("host4", "6.1.0")); InMemoryProvisioner provisioner = new InMemoryProvisioner(new Hosts(hosts), true); CountingModelFactory factory600 = DeployTester.createModelFactory(Version.fromString("6.0.0")); @@ -127,7 +124,7 @@ public class HostedDeployTest { DeployTester tester = new DeployTester(modelFactories, createConfigserverConfig(), Clock.systemUTC(), provisioner); // Deploy with version that does not exist on hosts, the model for this version should also be created tester.deployApp("src/test/apps/hosted/", "7.0.0", Instant.now()); - assertEquals(3, tester.getAllocatedHostsOf(tester.applicationId()).getHosts().size()); + assertEquals(4, tester.getAllocatedHostsOf(tester.applicationId()).getHosts().size()); // Check >0 not ==0 as the session watcher thread is running and will redeploy models in the background assertTrue(factory600.creationCount() > 0); @@ -145,7 +142,7 @@ public class HostedDeployTest { */ @Test public void testCreateOnlyNeededModelVersionsNewNodes() { - List<Host> hosts = Arrays.asList(createHost("host1"), createHost("host2"), createHost("host3")); + List<Host> hosts = Arrays.asList(createHost("host1"), createHost("host2"), createHost("host3"), createHost("host4")); InMemoryProvisioner provisioner = new InMemoryProvisioner(new Hosts(hosts), true); CountingModelFactory factory600 = DeployTester.createModelFactory(Version.fromString("6.0.0")); @@ -157,7 +154,7 @@ public class HostedDeployTest { DeployTester tester = new DeployTester(modelFactories, createConfigserverConfig(), Clock.systemUTC(), provisioner); // Deploy with version that does not exist on hosts, the model for this version should also be created tester.deployApp("src/test/apps/hosted/", "7.0.0", Instant.now()); - assertEquals(3, tester.getAllocatedHostsOf(tester.applicationId()).getHosts().size()); + assertEquals(4, tester.getAllocatedHostsOf(tester.applicationId()).getHosts().size()); // Check >0 not ==0 as the session watcher thread is running and will redeploy models in the background assertTrue(factory700.creationCount() > 0); @@ -195,7 +192,8 @@ public class HostedDeployTest { String newestOnNewMajorVersion = newestMajorVersion + ".2.0"; List<Host> hosts = Arrays.asList(createHost("host1", oldestVersion), createHost("host2", newestOnOldMajorVersion), - createHost("host3", newestOnOldMajorVersion)); + createHost("host3", newestOnOldMajorVersion), + createHost("host4", newestOnOldMajorVersion)); InMemoryProvisioner provisioner = new InMemoryProvisioner(new Hosts(hosts), true); CountingModelFactory factory1 = DeployTester.createModelFactory(Version.fromString(oldestVersion)); @@ -205,7 +203,7 @@ public class HostedDeployTest { DeployTester tester = new DeployTester(modelFactories, createConfigserverConfig(), Clock.systemUTC(), provisioner); tester.deployApp("src/test/apps/hosted/", oldestVersion, Instant.now()); - assertEquals(3, tester.getAllocatedHostsOf(tester.applicationId()).getHosts().size()); + assertEquals(4, tester.getAllocatedHostsOf(tester.applicationId()).getHosts().size()); // Check >0 not ==0 as the session watcher thread is running and will redeploy models in the background assertTrue(factory1.creationCount() > 0); @@ -256,7 +254,7 @@ public class HostedDeployTest { @Test public void testAccessControlIsOnlyCheckedWhenNoProdDeploymentExists() { // Provisioner does not reuse hosts, so need twice as many hosts as app requires - List<Host> hosts = IntStream.rangeClosed(1,6).mapToObj(i -> createHost("host" + i, "6.0.0")).collect(Collectors.toList()); + List<Host> hosts = IntStream.rangeClosed(1,8).mapToObj(i -> createHost("host" + i, "6.0.0")).collect(Collectors.toList()); InMemoryProvisioner provisioner = new InMemoryProvisioner(new Hosts(hosts), true); CountingModelFactory factory600 = DeployTester.createModelFactory(Version.fromString("6.0.0")); @@ -269,12 +267,12 @@ public class HostedDeployTest { ApplicationId applicationId = tester.applicationId(); // Deploy with oldest version tester.deployApp("src/test/apps/hosted/", "6.0.0", Instant.now()); - assertEquals(3, tester.getAllocatedHostsOf(applicationId).getHosts().size()); + assertEquals(4, tester.getAllocatedHostsOf(applicationId).getHosts().size()); // Deploy with version that does not exist on hosts and with app package that has no write access control, // validation of access control should not be done, since the app is already deployed in prod tester.deployApp("src/test/apps/hosted-no-write-access-control", "6.1.0", Instant.now()); - assertEquals(3, tester.getAllocatedHostsOf(applicationId).getHosts().size()); + assertEquals(4, tester.getAllocatedHostsOf(applicationId).getHosts().size()); } @Test @@ -321,7 +319,8 @@ public class HostedDeployTest { public void testThatConfigChangeActionsAreCollectedFromAllModels() { List<Host> hosts = Arrays.asList(createHost("host1", "6.1.0"), createHost("host2", "6.2.0"), - createHost("host3", "6.2.0")); + createHost("host3", "6.2.0"), + createHost("host4", "6.2.0")); InMemoryProvisioner provisioner = new InMemoryProvisioner(new Hosts(hosts), true); List<ServiceInfo> services = Collections.singletonList( new ServiceInfo("serviceName", "serviceType", null, new HashMap<>(), "configId", "hostName")); @@ -333,30 +332,12 @@ public class HostedDeployTest { DeployTester tester = new DeployTester(modelFactories, createConfigserverConfig(), Clock.systemUTC(), provisioner); PrepareResult prepareResult = tester.deployApp("src/test/apps/hosted/", "6.2.0", Instant.now()); - assertEquals(3, tester.getAllocatedHostsOf(tester.applicationId()).getHosts().size()); + assertEquals(4, tester.getAllocatedHostsOf(tester.applicationId()).getHosts().size()); List<RestartActions.Entry> actions = prepareResult.configChangeActions().getRestartActions().getEntries(); assertThat(actions.size(), is(1)); assertThat(actions.get(0).getMessages(), equalTo(ImmutableSet.of("change", "other change"))); } - @Test - public void testDeployWithClusterRotations() { - CountingModelFactory modelFactory = DeployTester.createModelFactory(Version.fromString("4.5.6"), Clock.systemUTC()); - DeployTester tester = new DeployTester(Collections.singletonList(modelFactory), createConfigserverConfig()); - ApplicationId applicationId = tester.applicationId(); - - tester.deployApp("src/test/apps/hosted/", "4.5.6", Instant.now()); - Set<HostSpec> containers = tester.getAllocatedHostsOf(applicationId).getHosts().stream() - .filter(h -> h.membership().get().cluster().type() == ClusterSpec.Type.container) - .collect(Collectors.toSet()); - assertFalse("Allocated container hosts", containers.isEmpty()); - - Set<RotationName> expected = Set.of(RotationName.from("eu-cluster"), RotationName.from("us-cluster")); - for (HostSpec container : containers) { - assertEquals(expected, container.membership().get().cluster().rotations()); - } - } - private static ConfigserverConfig createConfigserverConfig() { return new ConfigserverConfig(new ConfigserverConfig.Builder() .configServerDBDir(Files.createTempDir().getAbsolutePath()) diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java index e01b2eccb35..918670d71f2 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java @@ -13,9 +13,9 @@ import com.yahoo.config.model.application.provider.MockFileRegistry; import com.yahoo.config.provision.AllocatedHosts; import com.yahoo.config.provision.HostSpec; import com.yahoo.path.Path; +import com.yahoo.vespa.config.server.zookeeper.ConfigCurator; import com.yahoo.vespa.config.server.zookeeper.ZKApplicationPackage; import com.yahoo.vespa.curator.mock.MockCurator; -import com.yahoo.vespa.config.server.zookeeper.ConfigCurator; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; @@ -30,10 +30,11 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import static com.yahoo.config.provision.serialization.AllocatedHostsSerializer.fromJson; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; /** * Unit tests for ZooKeeperClient. @@ -199,7 +200,7 @@ public class ZooKeeperClientTest { Path hostsPath = app.append(ZKApplicationPackage.allocatedHostsNode); assertTrue(zk.exists(hostsPath.getAbsolute())); - AllocatedHosts deserialized = AllocatedHosts.fromJson(zk.getBytes(hostsPath.getAbsolute()), Optional.empty()); + AllocatedHosts deserialized = fromJson(zk.getBytes(hostsPath.getAbsolute()), Optional.empty()); assertEquals(hosts, deserialized.getHosts()); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java index b818c39d433..0381af57cc3 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/SessionHandlerTest.java @@ -99,7 +99,7 @@ public class SessionHandlerTest { } private MockSession(long id, ApplicationPackage app, InMemoryFlagSource flagSource) { - super(TenantName.defaultName(), id, null, new SessionContext(null, new MockSessionZKClient(MockApplicationPackage.createEmpty()), null, null, new HostRegistry<>(), null, flagSource)); + super(TenantName.defaultName(), id, null, new SessionContext(null, new MockSessionZKClient(MockApplicationPackage.createEmpty()), null, null, new HostRegistry<>(), flagSource)); this.app = app; this.preparer = new SessionTest.MockSessionPreparer(); this.flagSource = flagSource; diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java index a843212927b..41db57ab1e0 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java @@ -197,7 +197,7 @@ public class ApplicationHandlerTest { ApplicationId unknown = new ApplicationId.Builder().applicationName("unknown").tenant(mytenantName).build(); HttpResponse responseForUnknown = fileDistributionStatus(unknown, zone); assertEquals(404, responseForUnknown.getStatus()); - assertEquals("{\"error-code\":\"NOT_FOUND\",\"message\":\"No such application id: mytenant.unknown\"}", + assertEquals("{\"error-code\":\"NOT_FOUND\",\"message\":\"No such application id: 'mytenant.unknown'\"}", SessionHandlerTest.getRenderedString(responseForUnknown)); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java index 858d1e0eaa7..d94194e58d9 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionActiveHandlerTest.java @@ -105,7 +105,7 @@ public class SessionActiveHandlerTest extends SessionHandlerTest { .modelFactoryRegistry(new ModelFactoryRegistry(Collections.singletonList(modelFactory))) .build(); tenantRepository = new TenantRepository(componentRegistry, false); - applicationRepo = TenantApplications.create(curator, new MockReloadHandler(), tenantName); + applicationRepo = TenantApplications.create(componentRegistry, new MockReloadHandler(), tenantName); localRepo = new LocalSessionRepo(clock, curator); pathPrefix = "/application/v2/tenant/" + tenantName + "/session/"; hostProvisioner = new MockProvisioner(); @@ -232,7 +232,7 @@ public class SessionActiveHandlerTest extends SessionHandlerTest { ApplicationPackage app = FilesApplicationPackage.fromFileWithDeployData(testApp, deployData); localRepo.addSession(new LocalSession(tenantName, sessionId, new SessionTest.MockSessionPreparer(), new SessionContext(app, zkc, new File(tenantFileSystemDirs.sessionsPath(), String.valueOf(sessionId)), - applicationRepo, new HostRegistry<>(), new SuperModelGenerationCounter(curator), + applicationRepo, new HostRegistry<>(), flagSource))); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java index 0946ef3992c..ac4b4ea9005 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java @@ -11,7 +11,7 @@ import com.yahoo.vespa.config.server.MockReloadHandler; import com.yahoo.vespa.config.server.TestComponentRegistry; import com.yahoo.vespa.config.server.application.OrchestratorMock; import com.yahoo.vespa.config.server.application.TenantApplications; -import com.yahoo.vespa.config.server.http.CompressedApplicationInputStreamTest; +import com.yahoo.vespa.config.server.application.CompressedApplicationInputStreamTest; import com.yahoo.vespa.config.server.http.HandlerTest; import com.yahoo.vespa.config.server.http.HttpErrorResponse; import com.yahoo.vespa.config.server.http.SessionHandlerTest; @@ -72,7 +72,7 @@ public class SessionCreateHandlerTest extends SessionHandlerTest { @Before public void setupRepo() { Curator curator = new MockCurator(); - applicationRepo = TenantApplications.create(curator, new MockReloadHandler(), tenant); + applicationRepo = TenantApplications.create(componentRegistry, new MockReloadHandler(), tenant); localSessionRepo = new LocalSessionRepo(Clock.systemUTC(), curator); tenantRepository = new TenantRepository(componentRegistry, false); sessionFactory = new MockSessionFactory(); diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareHandlerTest.java index 11c0cf057cc..b6c3de8a1b1 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareHandlerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionPrepareHandlerTest.java @@ -87,7 +87,7 @@ public class SessionPrepareHandlerTest extends SessionHandlerTest { .withSessionFactory(new MockSessionFactory()) .withLocalSessionRepo(localRepo) .withRemoteSessionRepo(remoteSessionRepo) - .withApplicationRepo(TenantApplications.create(curator, new MockReloadHandler(), tenant)); + .withApplicationRepo(TenantApplications.create(componentRegistry, new MockReloadHandler(), tenant)); tenantRepository.addTenant(tenantBuilder); } @@ -403,7 +403,6 @@ public class SessionPrepareHandlerTest extends SessionHandlerTest { null, null, new HostRegistry<>(), - null, null)); this.exception = exception; } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/maintenance/TenantsMaintainerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/maintenance/TenantsMaintainerTest.java index a2f52bc5321..fdc6ffeacf0 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/maintenance/TenantsMaintainerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/maintenance/TenantsMaintainerTest.java @@ -37,6 +37,7 @@ public class TenantsMaintainerTest { assertNotNull(tenantRepository.getTenant(shouldNotBeDeleted)); new TenantsMaintainer(applicationRepository, tester.curator(), Duration.ofDays(1)).run(); + tenantRepository.updateTenants(); // One tenant should now have been deleted assertNull(tenantRepository.getTenant(shouldBeDeleted)); diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/model/LbServicesProducerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/model/LbServicesProducerTest.java index 0e124addaf7..34a4074c7cf 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/model/LbServicesProducerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/model/LbServicesProducerTest.java @@ -5,6 +5,7 @@ import com.yahoo.cloud.config.LbServicesConfig; import com.yahoo.config.application.api.ApplicationPackage; import com.yahoo.config.model.NullConfigModelRegistry; import com.yahoo.config.model.api.ApplicationInfo; +import com.yahoo.config.model.api.ContainerEndpoint; import com.yahoo.config.model.api.Model; import com.yahoo.config.model.deploy.DeployState; import com.yahoo.config.model.deploy.TestProperties; @@ -20,11 +21,14 @@ import com.yahoo.vespa.flags.Flags; import com.yahoo.vespa.flags.InMemoryFlagSource; import com.yahoo.vespa.model.VespaModel; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import org.xml.sax.SAXException; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -33,20 +37,34 @@ import java.util.Random; import java.util.Set; import static com.yahoo.config.model.api.container.ContainerServiceType.QRSERVER; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; /** * @author Ulf Lilleengen */ +@RunWith(Parameterized.class) public class LbServicesProducerTest { private static final String rotation1 = "rotation-1"; private static final String rotation2 = "rotation-2"; private static final String rotationString = rotation1 + "," + rotation2; private static final Set<Rotation> rotations = Collections.singleton(new Rotation(rotationString)); + private static final Set<ContainerEndpoint> endpoints = Set.of( + new ContainerEndpoint("mydisc", List.of("rotation-1", "rotation-2")) + ); private final InMemoryFlagSource flagSource = new InMemoryFlagSource(); + private final boolean useGlobalServiceId; + + @Parameterized.Parameters + public static Object[] useGlobalServiceId() { + return new Object[] { true, false }; + } + + public LbServicesProducerTest(boolean useGlobalServiceId) { + this.useGlobalServiceId = useGlobalServiceId; + } @Test public void testDeterministicGetConfig() throws IOException, SAXException { @@ -110,7 +128,7 @@ public class LbServicesProducerTest { Zone zone = new Zone(Environment.prod, regionName); Map<TenantName, Set<ApplicationInfo>> testModel = createTestModel(new DeployState.Builder() .zone(zone) - .properties(new TestProperties())); + .properties(new TestProperties().setHostedVespa(true))); return getLbServicesConfig(new Zone(Environment.prod, regionName), testModel); } @@ -123,18 +141,40 @@ public class LbServicesProducerTest { @Test public void testConfigAliasesWithRotations() throws IOException, SAXException { - Map<TenantName, Set<ApplicationInfo>> testModel = createTestModel(new DeployState.Builder().rotations(rotations)); + assumeTrue(useGlobalServiceId); + + Map<TenantName, Set<ApplicationInfo>> testModel = createTestModel(new DeployState.Builder() + .rotations(rotations) + .properties(new TestProperties().setHostedVespa(true))); RegionName regionName = RegionName.from("us-east-1"); - LbServicesConfig conf = getLbServicesConfig(new Zone(Environment.prod, regionName), testModel); - final LbServicesConfig.Tenants.Applications.Hosts.Services services = conf.tenants("foo").applications("foo:prod:" + regionName.value() + ":default").hosts("foo.foo.yahoo.com").services(QRSERVER.serviceName); - assertThat(services.servicealiases().size(), is(1)); - assertThat(services.endpointaliases().size(), is(4)); - assertThat(services.servicealiases(0), is("service1")); - assertThat(services.endpointaliases(0), is("foo1.bar1.com")); - assertThat(services.endpointaliases(1), is("foo2.bar2.com")); - assertThat(services.endpointaliases(2), is(rotation1)); - assertThat(services.endpointaliases(3), is(rotation2)); + var services = getLbServicesConfig(new Zone(Environment.prod, regionName), testModel) + .tenants("foo") + .applications("foo:prod:" + regionName.value() + ":default") + .hosts("foo.foo.yahoo.com") + .services(QRSERVER.serviceName); + + assertThat(services.servicealiases(), contains("service1")); + assertThat("Missing rotations in list: " + services.endpointaliases(), services.endpointaliases(), containsInAnyOrder("foo1.bar1.com", "foo2.bar2.com", rotation1, rotation2)); + } + + @Test + public void testConfigAliasesWithEndpoints() throws IOException, SAXException { + assumeFalse(useGlobalServiceId); + + Map<TenantName, Set<ApplicationInfo>> testModel = createTestModel(new DeployState.Builder() + .endpoints(endpoints) + .properties(new TestProperties().setHostedVespa(true))); + RegionName regionName = RegionName.from("us-east-1"); + + var services = getLbServicesConfig(new Zone(Environment.prod, regionName), testModel) + .tenants("foo") + .applications("foo:prod:" + regionName.value() + ":default") + .hosts("foo.foo.yahoo.com") + .services(QRSERVER.serviceName); + + assertThat(services.servicealiases(), contains("service1")); + assertThat("Missing endpoints in list: " + services.endpointaliases(), services.endpointaliases(), containsInAnyOrder("foo1.bar1.com", "foo2.bar2.com", rotation1, rotation2)); } private Map<TenantName, Set<ApplicationInfo>> randomizeApplications(Map<TenantName, Set<ApplicationInfo>> testModel, int seed) { @@ -181,7 +221,7 @@ public class LbServicesProducerTest { private ApplicationPackage createApplicationPackage(String host1, String host2) { String hosts = "<hosts><host name='" + host1 + "'><alias>node1</alias></host><host name='" + host2 + "'><alias>node2</alias></host></hosts>"; String services = "<services><admin version='2.0'><adminserver hostalias='node1' /><logserver hostalias='node1' /><slobroks><slobrok hostalias='node1' /><slobrok hostalias='node2' /></slobroks></admin>" - + "<jdisc id='mydisc' version='1.0'>" + + + "<container id='mydisc' version='1.0'>" + " <aliases>" + " <endpoint-alias>foo2.bar2.com</endpoint-alias>" + " <service-alias>service1</service-alias>" + @@ -191,16 +231,34 @@ public class LbServicesProducerTest { " <node hostalias='node1' />" + " </nodes>" + " <search/>" + - "</jdisc>" + + "</container>" + "</services>"; - String deploymentInfo ="<?xml version='1.0' encoding='UTF-8'?>" + - "<deployment version='1.0'>" + - " <test />" + - " <prod global-service-id='mydisc'>" + - " <region active='true'>us-east-1</region>" + - " <region active='false'>us-east-2</region>" + - " </prod>" + - "</deployment>"; + + String deploymentInfo; + + if (useGlobalServiceId) { + deploymentInfo ="<?xml version='1.0' encoding='UTF-8'?>" + + "<deployment version='1.0'>" + + " <test />" + + " <prod global-service-id='mydisc'>" + + " <region active='true'>us-east-1</region>" + + " <region active='false'>us-east-2</region>" + + " </prod>" + + "</deployment>"; + } else { + deploymentInfo ="<?xml version='1.0' encoding='UTF-8'?>" + + "<deployment version='1.0'>" + + " <test />" + + " <prod>" + + " <region active='true'>us-east-1</region>" + + " <region active='false'>us-east-2</region>" + + " </prod>" + + " <endpoints>" + + " <endpoint container-id='mydisc' />" + + " </endpoints>" + + "</deployment>"; + } + return new MockApplicationPackage.Builder().withHosts(hosts).withServices(services).withDeploymentSpec(deploymentInfo).build(); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/model/RoutingProducerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/model/RoutingProducerTest.java index c6a607f81b1..7ce2c39c6a4 100755 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/model/RoutingProducerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/model/RoutingProducerTest.java @@ -77,7 +77,7 @@ public class RoutingProducerTest { private ApplicationPackage createApplicationPackage(String host1, String host2) { String hosts = "<hosts><host name='" + host1 + "'><alias>node1</alias></host><host name='" + host2 + "'><alias>node2</alias></host></hosts>"; String services = "<services><admin version='2.0'><adminserver hostalias='node1' /><logserver hostalias='node1' /><slobroks><slobrok hostalias='node1' /><slobrok hostalias='node2' /></slobroks></admin>" - + "<jdisc id='mydisc' version='1.0'>" + + + "<container id='mydisc' version='1.0'>" + " <aliases>" + " <endpoint-alias>foo2.bar2.com</endpoint-alias>" + " <service-alias>service1</service-alias>" + @@ -87,7 +87,7 @@ public class RoutingProducerTest { " <node hostalias='node1' />" + " </nodes>" + " <search/>" + - "</jdisc>" + + "</container>" + "</services>"; String deploymentInfo ="<?xml version='1.0' encoding='UTF-8'?>" + "<deployment version='1.0'>" + diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/MockRequestHandler.java b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/MockRequestHandler.java index bfc06a58b16..0f6cd10d564 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/MockRequestHandler.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/MockRequestHandler.java @@ -1,6 +1,7 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.config.server.rpc; +import com.yahoo.config.FileReference; import com.yahoo.config.provision.ApplicationId; import com.yahoo.component.Version; import com.yahoo.vespa.config.ConfigKey; @@ -91,6 +92,11 @@ public class MockRequestHandler implements RequestHandler, ReloadHandler, Tenant } @Override + public Set<FileReference> listFileReferences(ApplicationId applicationId) { + return Set.of(); + } + + @Override public RequestHandler getRequestHandler() { return this; } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/MockRpc.java b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/MockRpc.java index 0f8bfa5068c..5fa51e1c404 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/MockRpc.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/MockRpc.java @@ -11,6 +11,7 @@ import com.yahoo.vespa.config.server.filedistribution.FileServer; import com.yahoo.vespa.config.server.host.ConfigRequestHostLivenessTracker; import com.yahoo.vespa.config.server.host.HostRegistries; import com.yahoo.vespa.config.server.monitoring.Metrics; +import com.yahoo.vespa.config.server.rpc.security.NoopRpcAuthorizer; import com.yahoo.vespa.config.server.tenant.MockTenantProvider; import java.io.File; @@ -38,7 +39,7 @@ public class MockRpc extends RpcServer { public MockRpc(int port, boolean createDefaultTenant, boolean pretendToHaveLoadedAnyApplication, File tempDir) { super(createConfig(port), null, Metrics.createTestMetrics(), - new HostRegistries(), new ConfigRequestHostLivenessTracker(), new FileServer(tempDir)); + new HostRegistries(), new ConfigRequestHostLivenessTracker(), new FileServer(tempDir), new NoopRpcAuthorizer(), new RpcRequestHandlerProvider()); if (createDefaultTenant) { onTenantCreate(TenantName.from("default"), new MockTenantProvider(pretendToHaveLoadedAnyApplication)); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java index c8bc9364922..086dfa5d0d3 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java @@ -114,7 +114,6 @@ public class RpcServerTest { } private void testEnabled(RpcTester tester) throws IOException, SAXException { - tester.generationCounter().increment(); Application app = new Application(new VespaModel(MockApplicationPackage.createEmpty()), new ServerCache(), 2L, @@ -174,7 +173,7 @@ public class RpcServerTest { private void testPrintStatistics(RpcTester tester) { Request req = new Request("printStatistics"); - tester.rpcServer().printStatistics(req); + tester.performRequest(req); assertThat(req.returnValues().get(0).asString(), is("Delayed responses queue size: 0")); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcTester.java b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcTester.java index dd66f720b1f..53463585582 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcTester.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcTester.java @@ -16,6 +16,7 @@ import com.yahoo.vespa.config.server.filedistribution.FileServer; import com.yahoo.vespa.config.server.host.ConfigRequestHostLivenessTracker; import com.yahoo.vespa.config.server.host.HostRegistries; import com.yahoo.vespa.config.server.monitoring.Metrics; +import com.yahoo.vespa.config.server.rpc.security.NoopRpcAuthorizer; import com.yahoo.vespa.config.server.tenant.MockTenantProvider; import com.yahoo.vespa.config.server.tenant.TenantHandlerProvider; import com.yahoo.vespa.flags.InMemoryFlagSource; @@ -45,13 +46,13 @@ public class RpcTester implements AutoCloseable { private final ManualClock clock = new ManualClock(Instant.ofEpochMilli(100)); private final String myHostname = HostName.getLocalhost(); private final HostLivenessTracker hostLivenessTracker = new ConfigRequestHostLivenessTracker(clock); + private final MockTenantProvider tenantProvider; + private final GenerationCounter generationCounter; + private final Spec spec; private RpcServer rpcServer; - private MockTenantProvider tenantProvider; - private GenerationCounter generationCounter; private Thread t; private Supervisor sup; - private Spec spec; private List<Integer> allocatedPorts; @@ -97,7 +98,9 @@ public class RpcTester implements AutoCloseable { generationCounter, new InMemoryFlagSource())), Metrics.createTestMetrics(), new HostRegistries(), - hostLivenessTracker, new FileServer(temporaryFolder.newFolder())); + hostLivenessTracker, new FileServer(temporaryFolder.newFolder()), + new NoopRpcAuthorizer(), + new RpcRequestHandlerProvider()); rpcServer.onTenantCreate(TenantName.from("default"), tenantProvider); t = new Thread(rpcServer); t.start(); @@ -145,7 +148,4 @@ public class RpcTester implements AutoCloseable { return tenantProvider; } - GenerationCounter generationCounter() { - return generationCounter; - } } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizerTest.java new file mode 100644 index 00000000000..a1d4f28cb74 --- /dev/null +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizerTest.java @@ -0,0 +1,324 @@ +package com.yahoo.vespa.config.server.rpc.security;// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +import com.yahoo.cloud.config.LbServicesConfig; +import com.yahoo.cloud.config.RoutingConfig; +import com.yahoo.cloud.config.SentinelConfig; +import com.yahoo.config.FileReference; +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.HostName; +import com.yahoo.config.provision.NodeType; +import com.yahoo.config.provision.TenantName; +import com.yahoo.config.provision.security.NodeIdentifier; +import com.yahoo.config.provision.security.NodeIdentifierException; +import com.yahoo.config.provision.security.NodeIdentity; +import com.yahoo.jrt.Request; +import com.yahoo.jrt.SecurityContext; +import com.yahoo.jrt.StringValue; +import com.yahoo.jrt.Target; +import com.yahoo.jrt.Values; +import com.yahoo.security.KeyAlgorithm; +import com.yahoo.security.KeyUtils; +import com.yahoo.security.SignatureAlgorithm; +import com.yahoo.security.X509CertificateBuilder; +import com.yahoo.slime.Cursor; +import com.yahoo.slime.JsonFormat; +import com.yahoo.slime.Slime; +import com.yahoo.vespa.config.ConfigKey; +import com.yahoo.vespa.config.server.RequestHandler; +import com.yahoo.vespa.config.server.host.HostRegistry; +import com.yahoo.vespa.config.server.rpc.RequestHandlerProvider; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import javax.security.auth.x500.X500Principal; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.math.BigInteger; +import java.security.cert.X509Certificate; +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; + +import static com.yahoo.vespa.config.server.rpc.security.MultiTenantRpcAuthorizer.Mode.ENFORCE; +import static java.time.temporal.ChronoUnit.DAYS; +import static org.hamcrest.core.IsInstanceOf.instanceOf; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * @author bjorncs + */ +public class MultiTenantRpcAuthorizerTest { + + private static final List<X509Certificate> PEER_CERTIFICATE_CHAIN = List.of(createDummyCertificate()); + private static final ApplicationId APPLICATION_ID = ApplicationId.from("mytenant", "myapplication", "default"); + private static final ApplicationId EVIL_APP_ID = ApplicationId.from("malice", "malice-app", "default"); + private static final HostName HOSTNAME = HostName.from("myhostname"); + private static final FileReference FILE_REFERENCE = new FileReference("myfilereference"); + + @Rule + public ExpectedException exceptionRule = ExpectedException.none(); + + @Test + public void configserver_can_access_files_and_config() throws InterruptedException, ExecutionException { + RpcAuthorizer authorizer = createAuthorizer(new NodeIdentity.Builder(NodeType.config).build(), + new HostRegistry<>()); + + Request configRequest = createConfigRequest(new ConfigKey<>("name", "configid", "namespace"), HOSTNAME); + authorizer.authorizeConfigRequest(configRequest) + .get(); + + Request fileRequest = createFileRequest(FILE_REFERENCE); + authorizer.authorizeFileRequest(fileRequest) + .get(); + } + + @Test + public void tenant_node_can_access_its_own_files_and_config() throws ExecutionException, InterruptedException { + NodeIdentity identity = new NodeIdentity.Builder(NodeType.tenant) + .applicationId(APPLICATION_ID) + .build(); + + HostRegistry<TenantName> hostRegistry = new HostRegistry<>(); + hostRegistry.update(APPLICATION_ID.tenant(), List.of(HOSTNAME.value())); + + RpcAuthorizer authorizer = createAuthorizer(identity, hostRegistry); + + Request configRequest = createConfigRequest(new ConfigKey<>("name", "configid", "namespace"), HOSTNAME); + authorizer.authorizeConfigRequest(configRequest) + .get(); + + Request fileRequest = createFileRequest(FILE_REFERENCE); + authorizer.authorizeFileRequest(fileRequest) + .get(); + } + + @Test + public void proxy_node_can_access_lbservice_config() throws ExecutionException, InterruptedException { + RpcAuthorizer authorizer = createAuthorizer(new NodeIdentity.Builder(NodeType.proxy).build(), new HostRegistry<>()); + + Request configRequest = createConfigRequest( + new ConfigKey<>(LbServicesConfig.CONFIG_DEF_NAME, "*", LbServicesConfig.CONFIG_DEF_NAMESPACE), + HOSTNAME); + authorizer.authorizeConfigRequest(configRequest) + .get(); + } + + @Test + public void tenant_node_can_access_routing_config() throws ExecutionException, InterruptedException { + RpcAuthorizer authorizer = createAuthorizer(new NodeIdentity.Builder(NodeType.tenant).build(), new HostRegistry<>()); + + Request configRequest = createConfigRequest( + new ConfigKey<>(RoutingConfig.CONFIG_DEF_NAME, "*", RoutingConfig.CONFIG_DEF_NAMESPACE), + HOSTNAME); + authorizer.authorizeConfigRequest(configRequest) + .get(); + } + + @Test + public void tenant_node_cannot_access_lbservice_config() throws ExecutionException, InterruptedException { + RpcAuthorizer authorizer = createAuthorizer(new NodeIdentity.Builder(NodeType.tenant).build(), new HostRegistry<>()); + + Request configRequest = createConfigRequest( + new ConfigKey<>(LbServicesConfig.CONFIG_DEF_NAME, "*", LbServicesConfig.CONFIG_DEF_NAMESPACE), + HOSTNAME); + + exceptionRule.expectMessage("Node with type 'tenant' is not allowed to access global config [name=lb-services,namespace=cloud.config,configId=*]"); + exceptionRule.expectCause(instanceOf(AuthorizationException.class)); + + authorizer.authorizeConfigRequest(configRequest) + .get(); + } + + @Test + public void tenant_node_cannot_access_other_files() throws ExecutionException, InterruptedException { + NodeIdentity identity = new NodeIdentity.Builder(NodeType.tenant) + .applicationId(APPLICATION_ID) + .build(); + + HostRegistry<TenantName> hostRegistry = new HostRegistry<>(); + hostRegistry.update(APPLICATION_ID.tenant(), List.of(HOSTNAME.value())); + + RpcAuthorizer authorizer = createAuthorizer(identity, hostRegistry); + + Request fileRequest = createFileRequest(new FileReference("other-file-reference")); + + exceptionRule.expectMessage("Peer is not allowed to access file other-file-reference"); + exceptionRule.expectCause(instanceOf(AuthorizationException.class)); + + authorizer.authorizeFileRequest(fileRequest) + .get(); + } + + @Test + public void tenant_node_cannot_access_other_config() throws ExecutionException, InterruptedException { + NodeIdentity identity = new NodeIdentity.Builder(NodeType.tenant) + .applicationId(EVIL_APP_ID) + .build(); + + HostRegistry<TenantName> hostRegistry = new HostRegistry<>(); + hostRegistry.update(APPLICATION_ID.tenant(), List.of(HOSTNAME.value())); + + RpcAuthorizer authorizer = createAuthorizer(identity, hostRegistry); + + Request configRequest = createConfigRequest(new ConfigKey<>("name", "configid", "namespace"), HOSTNAME); + + exceptionRule.expectMessage("Peer is not allowed to access config for owned by mytenant.myapplication. Peer is owned by malice.malice-app"); + exceptionRule.expectCause(instanceOf(AuthorizationException.class)); + + authorizer.authorizeConfigRequest(configRequest) + .get(); + } + + @Test + public void tenant_node_must_be_registered_in_host_registry() throws ExecutionException, InterruptedException { + NodeIdentity identity = new NodeIdentity.Builder(NodeType.tenant) + .applicationId(EVIL_APP_ID) + .build(); + + HostRegistry<TenantName> hostRegistry = new HostRegistry<>(); + + RpcAuthorizer authorizer = createAuthorizer(identity, hostRegistry); + + Request configRequest = createConfigRequest(new ConfigKey<>("name", "configid", "namespace"), HOSTNAME); + + exceptionRule.expectMessage("Host 'myhostname' not found in host registry"); + exceptionRule.expectCause(instanceOf(AuthorizationException.class)); + + authorizer.authorizeConfigRequest(configRequest) + .get(); + } + + @Test + public void tenant_must_have_a_request_handler() throws ExecutionException, InterruptedException { + NodeIdentity identity = new NodeIdentity.Builder(NodeType.tenant) + .applicationId(EVIL_APP_ID) + .build(); + + HostRegistry<TenantName> hostRegistry = new HostRegistry<>(); + hostRegistry.update(EVIL_APP_ID.tenant(), List.of(HOSTNAME.value())); + + RpcAuthorizer authorizer = createAuthorizer(identity, hostRegistry); + + Request configRequest = createConfigRequest(new ConfigKey<>("name", "configid", "namespace"), HOSTNAME); + + exceptionRule.expectMessage("No handler exists for tenant 'malice'"); + exceptionRule.expectCause(instanceOf(AuthorizationException.class)); + + authorizer.authorizeConfigRequest(configRequest) + .get(); + } + + @Test + public void tenant_node_not_in_hostregistry_allowed_to_access_sentinel_config() throws ExecutionException, InterruptedException { + NodeIdentity identity = new NodeIdentity.Builder(NodeType.tenant) + .applicationId(APPLICATION_ID) + .build(); + + HostRegistry<TenantName> hostRegistry = new HostRegistry<>(); + + RpcAuthorizer authorizer = createAuthorizer(identity, hostRegistry); + + Request configRequest = createConfigRequest(new ConfigKey<>(SentinelConfig.CONFIG_DEF_NAME, "configid", SentinelConfig.CONFIG_DEF_NAMESPACE), HOSTNAME); + + authorizer.authorizeConfigRequest(configRequest) + .get(); + } + + + private static RpcAuthorizer createAuthorizer(NodeIdentity identity, HostRegistry<TenantName> hostRegistry) { + return new MultiTenantRpcAuthorizer( + new StaticNodeIdentifier(identity), + hostRegistry, + createRequestHandlerProviderMock(), + new DirectExecutor(), + ENFORCE); + } + + private static Request createConfigRequest(ConfigKey<?> configKey, HostName hostName) { + return mockJrtRpcRequest(createConfigPayload(configKey, hostName.value())); + } + + private static Request createFileRequest(FileReference fileReference) { + return mockJrtRpcRequest(fileReference.value()); + } + + private static RequestHandlerProvider createRequestHandlerProviderMock() { + RequestHandler requestHandler = mock(RequestHandler.class); + when(requestHandler.hasApplication(APPLICATION_ID, Optional.empty())).thenReturn(true); + when(requestHandler.resolveApplicationId(HOSTNAME.value())).thenReturn(APPLICATION_ID); + when(requestHandler.listFileReferences(APPLICATION_ID)).thenReturn(Set.of(FILE_REFERENCE)); + + RequestHandlerProvider handlerProvider = mock(RequestHandlerProvider.class); + when(handlerProvider.getRequestHandler(APPLICATION_ID.tenant())).thenReturn(Optional.of(requestHandler)); + when(handlerProvider.getRequestHandler(EVIL_APP_ID.tenant())).thenReturn(Optional.empty()); + return handlerProvider; + } + + private static Request mockJrtRpcRequest(String payload) { + SecurityContext securityContext = mock(SecurityContext.class); + when(securityContext.peerCertificateChain()).thenReturn(PEER_CERTIFICATE_CHAIN); + Target target = mock(Target.class); + when(target.getSecurityContext()).thenReturn(Optional.of(securityContext)); + Request request = mock(Request.class); + when(request.target()).thenReturn(target); + Values values = new Values(); + values.add(new StringValue(payload)); + when(request.parameters()).thenReturn(values); + return request; + } + + private static String createConfigPayload(ConfigKey<?> configKey, String hostname) { + Slime data = new Slime(); + Cursor request = data.setObject(); + request.setString("defName", configKey.getName()); + request.setString("defNamespace", configKey.getNamespace()); + request.setString("defMD5", configKey.getMd5()); + request.setString("configId", configKey.getConfigId()); + request.setString("clientHostname", hostname); + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + new JsonFormat(false).encode(out, data); + return new String(out.toByteArray()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private static X509Certificate createDummyCertificate() { + return X509CertificateBuilder.fromKeypair( + KeyUtils.generateKeypair(KeyAlgorithm.EC), + new X500Principal("CN=" + HOSTNAME), + Instant.EPOCH, + Instant.EPOCH.plus(1, DAYS), + SignatureAlgorithm.SHA256_WITH_ECDSA, + BigInteger.ONE) + .build(); + } + + private static class DirectExecutor implements Executor { + + @Override + public void execute(Runnable command) { + command.run(); + } + } + + private static class StaticNodeIdentifier implements NodeIdentifier { + final NodeIdentity identity; + + StaticNodeIdentifier(NodeIdentity identity) { + this.identity = identity; + } + + @Override + public NodeIdentity identifyNode(List<X509Certificate> peerCertificateChain) throws NodeIdentifierException { + return identity; + } + } + +}
\ No newline at end of file diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionRepoTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionRepoTest.java index 3626c6269cc..d3f364e30ac 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionRepoTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionRepoTest.java @@ -46,7 +46,6 @@ public class LocalSessionRepoTest { } private void setupSessions(TenantName tenantName, boolean createInitialSessions) throws Exception { - GlobalComponentRegistry globalComponentRegistry = new TestComponentRegistry.Builder().curator(new MockCurator()).build(); TenantFileSystemDirs tenantFileSystemDirs = new TenantFileSystemDirs(temporaryFolder.newFolder(), tenantName); if (createInitialSessions) { IOUtils.copyDirectory(testApp, new File(tenantFileSystemDirs.sessionsPath(), "1")); @@ -54,11 +53,14 @@ public class LocalSessionRepoTest { IOUtils.copyDirectory(testApp, new File(tenantFileSystemDirs.sessionsPath(), "3")); } clock = new ManualClock(Instant.ofEpochSecond(1)); + GlobalComponentRegistry globalComponentRegistry = new TestComponentRegistry.Builder().curator(new MockCurator()) + .clock(clock) + .build(); LocalSessionLoader loader = new SessionFactoryImpl(globalComponentRegistry, - TenantApplications.create(new MockCurator(), new MockReloadHandler(), tenantName), + TenantApplications.create(globalComponentRegistry, new MockReloadHandler(), tenantName), tenantFileSystemDirs, new HostRegistry<>(), tenantName); - repo = new LocalSessionRepo(tenantFileSystemDirs, loader, clock, 5, globalComponentRegistry.getCurator()); + repo = new LocalSessionRepo(tenantName, globalComponentRegistry, tenantFileSystemDirs, loader); } @Test diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionTest.java index a4432dcbfcd..96caff9b3a7 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/LocalSessionTest.java @@ -16,7 +16,7 @@ import com.yahoo.path.Path; import com.yahoo.slime.Slime; import com.yahoo.transaction.NestedTransaction; import com.yahoo.vespa.config.server.MockReloadHandler; -import com.yahoo.vespa.config.server.SuperModelGenerationCounter; +import com.yahoo.vespa.config.server.TestComponentRegistry; import com.yahoo.vespa.config.server.application.TenantApplications; import com.yahoo.vespa.config.server.deploy.DeployHandlerLogger; import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs; @@ -54,13 +54,11 @@ public class LocalSessionTest { private Curator curator; private ConfigCurator configCurator; private TenantFileSystemDirs tenantFileSystemDirs; - private SuperModelGenerationCounter superModelGenerationCounter; @Before public void setupTest() { curator = new MockCurator(); configCurator = ConfigCurator.create(curator); - superModelGenerationCounter = new SuperModelGenerationCounter(curator); tenantFileSystemDirs = new TenantFileSystemDirs(Files.createTempDir(), TenantName.from("test_tenant")); } @@ -130,11 +128,9 @@ public class LocalSessionTest { String sessionNode = TenantRepository.getSessionsPath(tenantName).append(String.valueOf(3)).getAbsolute(); assertTrue(configCurator.exists(sessionNode)); assertTrue(new File(tenantFileSystemDirs.sessionsPath(), "3").exists()); - long gen = superModelGenerationCounter.get(); NestedTransaction transaction = new NestedTransaction(); session.delete(transaction); transaction.commit(); - assertThat(superModelGenerationCounter.get(), is(gen + 1)); assertFalse(configCurator.exists(sessionNode)); assertFalse(new File(tenantFileSystemDirs.sessionsPath(), "3").exists()); } @@ -196,7 +192,7 @@ public class LocalSessionTest { zkClient.write(Collections.singletonMap(new Version(0, 0, 0), new MockFileRegistry())); File sessionDir = new File(tenantFileSystemDirs.sessionsPath(), String.valueOf(sessionId)); sessionDir.createNewFile(); - TenantApplications applications = TenantApplications.create(curator, new MockReloadHandler(), tenant); + TenantApplications applications = TenantApplications.create(new TestComponentRegistry.Builder().curator(curator).build(), new MockReloadHandler(), tenant); applications.createApplication(zkc.readApplicationId()); return new LocalSession(tenant, sessionId, preparer, new SessionContext( @@ -205,7 +201,6 @@ public class LocalSessionTest { sessionDir, applications, new HostRegistry<>(), - superModelGenerationCounter, flagSource)); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java index f2fb4aa1c40..f5fd6053b07 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java @@ -6,10 +6,13 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Rotation; import com.yahoo.config.provision.TenantName; import com.yahoo.container.jdisc.HttpRequest; - +import com.yahoo.config.model.api.ContainerEndpoint; import org.junit.Test; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.time.Duration; +import java.util.List; import java.util.Optional; import java.util.Set; @@ -17,6 +20,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -26,6 +30,15 @@ import static org.junit.Assert.assertTrue; */ public class PrepareParamsTest { + private static final String rotation = "rotation-042.vespa.a02.yahoodns.net"; + private static final String vespaVersion = "6.37.49"; + private static final String request = "http://foo:19071/application/v2/tenant/foo/application/bar?" + + PrepareParams.DRY_RUN_PARAM_NAME + "=true&" + + PrepareParams.VERBOSE_PARAM_NAME+ "=true&" + + PrepareParams.IGNORE_VALIDATION_PARAM_NAME + "=false&" + + PrepareParams.APPLICATION_NAME_PARAM_NAME + "=baz&" + + PrepareParams.VESPA_VERSION_PARAM_NAME + "=" + vespaVersion; + @Test public void testCorrectParsing() { PrepareParams prepareParams = createParams("http://foo:19071/application/v2/", TenantName.defaultName()); @@ -38,15 +51,6 @@ public class PrepareParamsTest { assertTrue(prepareParams.getTimeoutBudget().hasTimeLeft()); assertThat(prepareParams.rotations().size(), is(0)); } - - private static final String rotation = "rotation-042.vespa.a02.yahoodns.net"; - private static final String vespaVersion = "6.37.49"; - private static final String request = "http://foo:19071/application/v2/tenant/foo/application/bar?" + - PrepareParams.DRY_RUN_PARAM_NAME + "=true&" + - PrepareParams.VERBOSE_PARAM_NAME+ "=true&" + - PrepareParams.IGNORE_VALIDATION_PARAM_NAME + "=false&" + - PrepareParams.APPLICATION_NAME_PARAM_NAME + "=baz&" + - PrepareParams.VESPA_VERSION_PARAM_NAME + "=" + vespaVersion; @Test public void testCorrectParsingWithRotation() { @@ -77,6 +81,31 @@ public class PrepareParamsTest { assertThat(rotations, containsInAnyOrder(new Rotation(rotation), new Rotation(rotationTwo))); } + @Test + public void testCorrectParsingWithContainerEndpoints() { + var endpoints = List.of(new ContainerEndpoint("qrs1", + List.of("c1.example.com", + "c2.example.com")), + new ContainerEndpoint("qrs2", + List.of("c3.example.com", + "c4.example.com"))); + var param = "[\n" + + " {\n" + + " \"clusterId\": \"qrs1\",\n" + + " \"names\": [\"c1.example.com\", \"c2.example.com\"]\n" + + " },\n" + + " {\n" + + " \"clusterId\": \"qrs2\",\n" + + " \"names\": [\"c3.example.com\", \"c4.example.com\"]\n" + + " }\n" + + "]"; + + var encoded = URLEncoder.encode(param, StandardCharsets.UTF_8); + var prepareParams = createParams(request + "&" + PrepareParams.CONTAINER_ENDPOINTS_PARAM_NAME + + "=" + encoded, TenantName.from("foo")); + assertEquals(endpoints, prepareParams.containerEndpoints()); + } + // Create PrepareParams from a request (based on uri and tenant name) private static PrepareParams createParams(String uri, TenantName tenantName) { return PrepareParams.fromHttpRequest( @@ -84,4 +113,5 @@ public class PrepareParamsTest { tenantName, Duration.ofSeconds(60)); } + } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionRepoTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionRepoTest.java index 9dda653dbc1..83183a27666 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionRepoTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionRepoTest.java @@ -10,6 +10,7 @@ import com.yahoo.config.provision.TenantName; import com.yahoo.path.Path; import com.yahoo.text.Utf8; +import com.yahoo.vespa.config.server.GlobalComponentRegistry; import com.yahoo.vespa.config.server.MockReloadHandler; import com.yahoo.vespa.config.server.TestComponentRegistry; import com.yahoo.vespa.config.server.application.TenantApplications; @@ -100,11 +101,12 @@ public class RemoteSessionRepoTest { public void testBadApplicationRepoOnActivate() { long sessionId = 3L; TenantName mytenant = TenantName.from("mytenant"); - TenantApplications applicationRepo = TenantApplications.create(curator, new MockReloadHandler(), mytenant); + GlobalComponentRegistry registry = new TestComponentRegistry.Builder().curator(curator).build(); + TenantApplications applicationRepo = TenantApplications.create(registry, new MockReloadHandler(), mytenant); curator.set(TenantRepository.getApplicationsPath(mytenant).append("mytenant:appX:default"), new byte[0]); // Invalid data - Tenant tenant = TenantBuilder.create(new TestComponentRegistry.Builder().curator(curator).build(), mytenant) - .withApplicationRepo(applicationRepo) - .build(); + Tenant tenant = TenantBuilder.create(registry, mytenant) + .withApplicationRepo(applicationRepo) + .build(); curator.create(TenantRepository.getSessionsPath(mytenant)); remoteSessionRepo = tenant.getRemoteSessionRepo(); assertThat(remoteSessionRepo.listSessions().size(), is(0)); diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java index 9ad90e84d86..651dde375ee 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java @@ -1,22 +1,24 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.config.server.session; +import com.yahoo.component.Version; import com.yahoo.config.application.api.DeployLogger; import com.yahoo.config.model.api.ModelContext; +import com.yahoo.config.model.api.TlsSecrets; import com.yahoo.config.model.application.provider.BaseDeployLogger; import com.yahoo.config.model.application.provider.FilesApplicationPackage; +import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ApplicationName; +import com.yahoo.config.provision.CertificateNotReadyException; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.Rotation; import com.yahoo.config.provision.TenantName; -import com.yahoo.component.Version; import com.yahoo.io.IOUtils; import com.yahoo.log.LogLevel; import com.yahoo.path.Path; import com.yahoo.slime.Slime; -import com.yahoo.config.provision.ApplicationId; import com.yahoo.vespa.config.server.MockReloadHandler; -import com.yahoo.vespa.config.server.SuperModelGenerationCounter; +import com.yahoo.vespa.config.server.MockSecretStore; import com.yahoo.vespa.config.server.TestComponentRegistry; import com.yahoo.vespa.config.server.TimeoutBudgetTest; import com.yahoo.vespa.config.server.application.PermanentApplicationPackage; @@ -27,9 +29,11 @@ import com.yahoo.vespa.config.server.http.InvalidApplicationException; import com.yahoo.vespa.config.server.model.TestModelFactory; import com.yahoo.vespa.config.server.modelfactory.ModelFactoryRegistry; import com.yahoo.vespa.config.server.provision.HostProvisionerProvider; +import com.yahoo.config.model.api.ContainerEndpoint; +import com.yahoo.vespa.config.server.tenant.ContainerEndpointsCache; import com.yahoo.vespa.config.server.tenant.Rotations; +import com.yahoo.vespa.config.server.tenant.TlsSecretsKeys; import com.yahoo.vespa.config.server.zookeeper.ConfigCurator; - import com.yahoo.vespa.curator.mock.MockCurator; import com.yahoo.vespa.flags.InMemoryFlagSource; import org.junit.Before; @@ -42,6 +46,7 @@ import java.io.IOException; import java.time.Instant; import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Optional; import java.util.Set; @@ -49,8 +54,8 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.contains; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; /** * @author Ulf Lilleengen @@ -70,7 +75,7 @@ public class SessionPreparerTest { private SessionPreparer preparer; private TestComponentRegistry componentRegistry; private MockFileDistributionFactory fileDistributionFactory; - + private MockSecretStore secretStore = new MockSecretStore(); @Rule public TemporaryFolder folder = new TemporaryFolder(); @@ -105,7 +110,8 @@ public class SessionPreparerTest { componentRegistry.getStaticConfigDefinitionRepo(), curator, componentRegistry.getZone(), - flagSource); + flagSource, + secretStore); } @Test(expected = InvalidApplicationException.class) @@ -170,6 +176,10 @@ public class SessionPreparerTest { assertThat(zkc.readApplicationId(), is(origId)); } + private List<ContainerEndpoint> readContainerEndpoints(ApplicationId application) { + return new ContainerEndpointsCache(tenantPath, curator).read(application); + } + private Set<Rotation> readRotationsFromZK(ApplicationId applicationId) { return new Rotations(curator, tenantPath).readRotationsFromZooKeeper(applicationId); } @@ -205,6 +215,85 @@ public class SessionPreparerTest { assertThat(readRotationsFromZK(applicationId), contains(new Rotation(rotations))); } + @Test + public void require_that_rotations_are_written_as_container_endpoints() throws Exception { + var rotations = "app1.tenant1.global.vespa.example.com,rotation-042.vespa.global.routing"; + var applicationId = applicationId("test"); + var params = new PrepareParams.Builder().applicationId(applicationId).rotations(rotations).build(); + prepare(new File("src/test/resources/deploy/hosted-app"), params); + + var expected = List.of(new ContainerEndpoint("qrs", + List.of("app1.tenant1.global.vespa.example.com", + "rotation-042.vespa.global.routing"))); + assertEquals(expected, readContainerEndpoints(applicationId)); + } + + @Test + public void require_that_container_endpoints_are_written() throws Exception { + var endpoints = "[\n" + + " {\n" + + " \"clusterId\": \"foo\",\n" + + " \"names\": [\n" + + " \"foo.app1.tenant1.global.vespa.example.com\",\n" + + " \"rotation-042.vespa.global.routing\"\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"clusterId\": \"bar\",\n" + + " \"names\": [\n" + + " \"bar.app1.tenant1.global.vespa.example.com\",\n" + + " \"rotation-043.vespa.global.routing\"\n" + + " ]\n" + + " }\n" + + "]"; + var applicationId = applicationId("test"); + var params = new PrepareParams.Builder().applicationId(applicationId) + .containerEndpoints(endpoints) + .build(); + prepare(new File("src/test/resources/deploy/hosted-app"), params); + + var expected = List.of(new ContainerEndpoint("foo", + List.of("foo.app1.tenant1.global.vespa.example.com", + "rotation-042.vespa.global.routing")), + new ContainerEndpoint("bar", + List.of("bar.app1.tenant1.global.vespa.example.com", + "rotation-043.vespa.global.routing"))); + assertEquals(expected, readContainerEndpoints(applicationId)); + } + + @Test + public void require_that_tlssecretkey_is_written() throws IOException { + var tlskey = "vespa.tlskeys.tenant1--app1"; + var applicationId = applicationId("test"); + var params = new PrepareParams.Builder().applicationId(applicationId).tlsSecretsKeyName(tlskey).build(); + secretStore.put(tlskey+"-cert", "CERT"); + secretStore.put(tlskey+"-key", "KEY"); + prepare(new File("src/test/resources/deploy/hosted-app"), params); + + // Read from zk and verify cert and key are available + Optional<TlsSecrets> tlsSecrets = new TlsSecretsKeys(curator, tenantPath, secretStore).readTlsSecretsKeyFromZookeeper(applicationId); + assertTrue(tlsSecrets.isPresent()); + assertEquals("KEY", tlsSecrets.get().key()); + assertEquals("CERT", tlsSecrets.get().certificate()); + } + + @Test(expected = CertificateNotReadyException.class) + public void require_that_tlssecretkey_is_missing_when_not_in_secretstore() throws IOException { + var tlskey = "vespa.tlskeys.tenant1--app1"; + var applicationId = applicationId("test"); + var params = new PrepareParams.Builder().applicationId(applicationId).tlsSecretsKeyName(tlskey).build(); + prepare(new File("src/test/resources/deploy/hosted-app"), params); + } + + @Test(expected = CertificateNotReadyException.class) + public void require_that_tlssecretkey_is_missing_when_certificate_not_in_secretstore() throws IOException { + var tlskey = "vespa.tlskeys.tenant1--app1"; + var applicationId = applicationId("test"); + var params = new PrepareParams.Builder().applicationId(applicationId).tlsSecretsKeyName(tlskey).build(); + secretStore.put(tlskey+"-key", "KEY"); + prepare(new File("src/test/resources/deploy/hosted-app"), params); + } + private void prepare(File app) throws IOException { prepare(app, new PrepareParams.Builder().build()); } @@ -217,9 +306,8 @@ public class SessionPreparerTest { return new SessionContext(app, new SessionZooKeeperClient(curator, sessionsPath), app.getAppDir(), - TenantApplications.create(curator, new MockReloadHandler(), TenantName.from("tenant")), + TenantApplications.create(componentRegistry, new MockReloadHandler(), TenantName.from("tenant")), new HostRegistry<>(), - new SuperModelGenerationCounter(curator), flagSource); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionTest.java index 95f6c7718e2..b2ad0af8f9a 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionTest.java @@ -21,7 +21,7 @@ public class SessionTest { public boolean isPrepared = false; public MockSessionPreparer() { - super(null, null, null, null, null, null, new MockCurator(), null, null); + super(null, null, null, null, null, null, new MockCurator(), null, null, null); } @Override diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializerTest.java new file mode 100644 index 00000000000..053a3f7a15d --- /dev/null +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializerTest.java @@ -0,0 +1,50 @@ +package com.yahoo.vespa.config.server.tenant; + +import com.yahoo.config.model.api.ContainerEndpoint; +import com.yahoo.slime.Slime; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * @author ogronnesby + */ +public class ContainerEndpointSerializerTest { + + @Test + public void readSingleEndpoint() { + final var slime = new Slime(); + final var entry = slime.setObject(); + + entry.setString("clusterId", "foobar"); + final var entryNames = entry.setArray("names"); + entryNames.addString("a"); + entryNames.addString("b"); + + final var endpoint = ContainerEndpointSerializer.endpointFromSlime(slime.get()); + assertEquals("foobar", endpoint.clusterId().toString()); + assertEquals(List.of("a", "b"), endpoint.names()); + } + + @Test + public void writeReadSingleEndpoint() { + final var endpoint = new ContainerEndpoint("foo", List.of("a", "b")); + final var serialized = new Slime(); + ContainerEndpointSerializer.endpointToSlime(serialized.setObject(), endpoint); + final var deserialized = ContainerEndpointSerializer.endpointFromSlime(serialized.get()); + + assertEquals(endpoint, deserialized); + } + + @Test + public void writeReadEndpoints() { + final var endpoints = List.of(new ContainerEndpoint("foo", List.of("a", "b"))); + final var serialized = ContainerEndpointSerializer.endpointListToSlime(endpoints); + final var deserialized = ContainerEndpointSerializer.endpointListFromSlime(serialized); + + assertEquals(endpoints, deserialized); + } + +} diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointsCacheTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointsCacheTest.java new file mode 100644 index 00000000000..4400b424d1b --- /dev/null +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointsCacheTest.java @@ -0,0 +1,36 @@ +// Copyright 2019 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.server.tenant; + +import com.yahoo.config.model.api.ContainerEndpoint; +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.path.Path; +import com.yahoo.vespa.curator.mock.MockCurator; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class ContainerEndpointsCacheTest { + @Test + public void readWriteFromCache() { + final var cache = new ContainerEndpointsCache(Path.createRoot(), new MockCurator()); + final var endpoints = List.of( + new ContainerEndpoint("the-cluster-1", List.of("a", "b", "c")) + ); + + cache.write(ApplicationId.defaultId(), endpoints); + + final var deserialized = cache.read(ApplicationId.defaultId()); + + assertEquals(endpoints, deserialized); + } + + @Test + public void readingNonExistingEntry() { + final var cache = new ContainerEndpointsCache(Path.createRoot(), new MockCurator()); + final var endpoints = cache.read(ApplicationId.defaultId()); + assertTrue(endpoints.isEmpty()); + } +}
\ No newline at end of file diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandlerTest.java index 6aa5aa7cd70..36bb7a926b5 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandlerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRequestHandlerTest.java @@ -93,11 +93,11 @@ public class TenantRequestHandlerTest { Metrics sh = Metrics.createTestMetrics(); List<ReloadListener> listeners = new ArrayList<>(); listeners.add(listener); - server = new TenantRequestHandler(sh, tenant, listeners, new UncompressedConfigResponseFactory(), new HostRegistries(), curator); componentRegistry = new TestComponentRegistry.Builder() .curator(curator) .modelFactoryRegistry(createRegistry()) .build(); + server = new TenantRequestHandler(sh, tenant, listeners, new UncompressedConfigResponseFactory(), componentRegistry); } private void feedApp(File appDir, long sessionId, ApplicationId appId, boolean internalRedeploy) throws IOException { @@ -357,12 +357,12 @@ public class TenantRequestHandlerTest { configNames = server.listConfigs(ApplicationId.defaultId(), Optional.of(vespaVersion), true); System.out.println(configNames); - assertTrue(configNames.contains(new ConfigKey<>("documentmanager", "jdisc", "document.config"))); + assertTrue(configNames.contains(new ConfigKey<>("documentmanager", "container", "document.config"))); assertTrue(configNames.contains(new ConfigKey<>("documentmanager", "", "document.config"))); assertTrue(configNames.contains(new ConfigKey<>("documenttypes", "", "document"))); - assertTrue(configNames.contains(new ConfigKey<>("documentmanager", "jdisc", "document.config"))); - assertTrue(configNames.contains(new ConfigKey<>("health-monitor", "jdisc", "container.jdisc.config"))); - assertTrue(configNames.contains(new ConfigKey<>("specific", "jdisc", "project"))); + assertTrue(configNames.contains(new ConfigKey<>("documentmanager", "container", "document.config"))); + assertTrue(configNames.contains(new ConfigKey<>("health-monitor", "container", "container.jdisc.config"))); + assertTrue(configNames.contains(new ConfigKey<>("specific", "container", "project"))); } @Test diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantTest.java index baab250a508..e140dae3650 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantTest.java @@ -38,7 +38,7 @@ public class TenantTest { TenantRepository tenantRepository = new TenantRepository(componentRegistry, false); TenantName tenantName = TenantName.from(name); TenantBuilder tenantBuilder = TenantBuilder.create(componentRegistry, tenantName) - .withApplicationRepo(TenantApplications.create(new MockCurator(), new MockReloadHandler(), tenantName)); + .withApplicationRepo(TenantApplications.create(componentRegistry, new MockReloadHandler(), tenantName)); tenantRepository.addTenant(tenantBuilder); return tenantRepository.getTenant(tenantName); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackageTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackageTest.java index e8e2dd07756..8b8be1a27d7 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackageTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackageTest.java @@ -1,27 +1,15 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.config.server.zookeeper; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertThat; - -import java.io.File; -import java.io.IOException; -import java.io.Reader; -import java.util.Collections; -import java.util.Optional; -import java.util.regex.Pattern; - +import com.yahoo.component.Version; import com.yahoo.config.application.api.DeploymentSpec; import com.yahoo.config.model.deploy.DeployState; +import com.yahoo.config.provision.AllocatedHosts; import com.yahoo.config.provision.Flavor; import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.NodeFlavors; -import com.yahoo.config.provision.AllocatedHosts; -import com.yahoo.component.Version; import com.yahoo.config.provisioning.FlavorsConfig; +import com.yahoo.io.IOUtils; import com.yahoo.path.Path; import com.yahoo.text.Utf8; import com.yahoo.vespa.curator.mock.MockCurator; @@ -30,7 +18,19 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import com.yahoo.io.IOUtils; +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.util.Collections; +import java.util.Optional; +import java.util.regex.Pattern; + +import static com.yahoo.config.provision.serialization.AllocatedHostsSerializer.toJson; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; public class ZKApplicationPackageTest { @@ -77,7 +77,7 @@ public class ZKApplicationPackageTest { assertFalse(zkApp.getFileRegistries().containsKey(new Version(0, 0, 0))); assertThat(zkApp.getFileRegistries().get(goodVersion).fileSourceHost(), is("dummyfiles")); AllocatedHosts readInfo = zkApp.getAllocatedHosts().get(); - assertThat(Utf8.toString(readInfo.toJson()), is(Utf8.toString(ALLOCATED_HOSTS.toJson()))); + assertThat(Utf8.toString(toJson(readInfo)), is(Utf8.toString(toJson(ALLOCATED_HOSTS)))); assertThat(readInfo.getHosts().iterator().next().flavor(), is(TEST_FLAVOR)); assertEquals("6.0.1", readInfo.getHosts().iterator().next().version().get().toString()); assertTrue(zkApp.getDeployment().isPresent()); @@ -90,7 +90,7 @@ public class ZKApplicationPackageTest { String metaData = "{\"deploy\":{\"user\":\"foo\",\"from\":\"bar\",\"timestamp\":1},\"application\":{\"name\":\"foo\",\"checksum\":\"abc\",\"generation\":4,\"previousActiveGeneration\":3}}"; zk.putData("/0", ConfigCurator.META_ZK_PATH, metaData); zk.putData("/0/" + ZKApplicationPackage.fileRegistryNode + "/3.0.0", "dummyfiles"); - zk.putData("/0/" + ZKApplicationPackage.allocatedHostsNode, ALLOCATED_HOSTS.toJson()); + zk.putData("/0/" + ZKApplicationPackage.allocatedHostsNode, toJson(ALLOCATED_HOSTS)); } private static class MockNodeFlavors extends NodeFlavors{ |