diff options
author | Harald Musum <musum@verizonmedia.com> | 2019-03-11 09:06:47 +0100 |
---|---|---|
committer | Harald Musum <musum@verizonmedia.com> | 2019-03-11 09:06:47 +0100 |
commit | 2f6a31090c98fdbe1ffbb0515f30252d89b7cb78 (patch) | |
tree | 6f8f32b96924e719581ad2f949f68e7faa2a758c /configserver | |
parent | 804fa9e5f1e1fa4c482725b42d44a85b78b49829 (diff) |
Add config for returning empty sentinel config when app is removed
For making it easier to create system test
Diffstat (limited to 'configserver')
5 files changed, 51 insertions, 138 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java index a7c04f33d5f..bbdef71129a 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java @@ -84,7 +84,7 @@ class GetConfigProcessor implements Runnable { // If we are certain that this request is from a node that no longer belongs to this application, // fabricate an empty request to cause the sentinel to stop all running services - if (rpcServer.isHostedVespa() && rpcServer.allTenantsLoaded() && !tenant.isPresent() && isSentinelConfigRequest(request)) { + if (rpcServer.canReturnEmptySentinelConfig() && rpcServer.allTenantsLoaded() && tenant.isEmpty() && isSentinelConfigRequest(request)) { returnEmpty(request); return null; } @@ -164,6 +164,7 @@ class GetConfigProcessor implements Runnable { } private void returnEmpty(JRTServerConfigRequest request) { + log.log(LogLevel.INFO, "Returning empty sentinel config for request from " + request.getClientHostName()); ConfigPayload emptyPayload = ConfigPayload.empty(); String configMd5 = ConfigUtils.getMd5(emptyPayload); ConfigResponse config = SlimeConfigResponse.fromConfigPayload(emptyPayload, null, 0, false, configMd5); diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java index 061a3b3302b..1df72522310 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java @@ -84,6 +84,7 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener { private Spec spec; private final boolean useRequestVersion; private final boolean hostedVespa; + private final boolean canReturnEmptySentinelConfig; private static final Logger log = Logger.getLogger(RpcServer.class.getName()); @@ -136,6 +137,7 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener { hostRegistry = hostRegistries.getTenantHostRegistry(); this.useRequestVersion = config.useVespaVersionInRequest(); this.hostedVespa = config.hostedVespa(); + this.canReturnEmptySentinelConfig = config.canReturnEmptySentinelConfig(); this.fileServer = fileServer; downloader = fileServer.downloader(); setUpHandlers(); @@ -441,6 +443,10 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener { /** Returns true if this rpc server is currently running in a hosted Vespa configuration */ public boolean isHostedVespa() { return hostedVespa; } + + /** Returns true if empty sentinel config can be returned when a request from a host that is + * not part of an application asks for sentinel config */ + public boolean canReturnEmptySentinelConfig() { return canReturnEmptySentinelConfig; } MetricUpdaterFactory metricUpdaterFactory() { return metricUpdaterFactory; diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessorTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessorTest.java deleted file mode 100644 index 28f111a04d2..00000000000 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessorTest.java +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.config.server.rpc; - -import com.yahoo.cloud.config.SentinelConfig; -import com.yahoo.config.provision.TenantName; -import com.yahoo.text.Utf8; -import com.yahoo.text.Utf8Array; -import com.yahoo.text.Utf8String; -import com.yahoo.vespa.config.ConfigKey; - -import com.yahoo.vespa.config.protocol.CompressionInfo; -import com.yahoo.vespa.config.protocol.CompressionType; -import com.yahoo.vespa.config.protocol.ConfigResponse; -import com.yahoo.vespa.config.protocol.DefContent; -import com.yahoo.vespa.config.protocol.JRTClientConfigRequestV3; -import com.yahoo.vespa.config.protocol.JRTServerConfigRequest; -import com.yahoo.vespa.config.protocol.JRTServerConfigRequestV3; -import com.yahoo.vespa.config.protocol.Trace; -import com.yahoo.vespa.config.server.tenant.MockTenantProvider; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import static org.junit.Assert.assertFalse; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -/** - * @author Ulf Lilleengen - */ -public class GetConfigProcessorTest { - - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); - - @Test - public void testSentinelConfig() throws IOException { - MockRpc rpc = new MockRpc(13337, false, temporaryFolder.newFolder()); - rpc.response = new MockConfigResponse("foo"); // should be a sentinel config, but it does not matter for this test - - // one tenant, which has host1 assigned - boolean pretendToHaveLoadedApplications = true; - TenantName testTenant = TenantName.from("test"); - rpc.onTenantCreate(testTenant, new MockTenantProvider(pretendToHaveLoadedApplications)); - rpc.hostsUpdated(testTenant, Collections.singleton("host1")); - - { // a config is returned normally - JRTServerConfigRequest req = createV3SentinelRequest("host1"); - GetConfigProcessor proc = new GetConfigProcessor(rpc, req, false); - proc.run(); - assertTrue(rpc.tryResolveConfig); - assertTrue(rpc.tryRespond); - assertThat(rpc.errorCode, is(0)); - } - - rpc.resetChecks(); - // host1 is replaced by host2 for this tenant - rpc.hostsUpdated(testTenant, Collections.singleton("host2")); - - { // this causes us to get an empty config instead of normal config resolution - JRTServerConfigRequest req = createV3SentinelRequest("host1"); - GetConfigProcessor proc = new GetConfigProcessor(rpc, req, false); - proc.run(); - assertFalse(rpc.tryResolveConfig); // <-- no normal config resolution happening - assertTrue(rpc.tryRespond); - assertThat(rpc.errorCode, is(0)); - } - } - - private static JRTServerConfigRequest createV3SentinelRequest(String fromHost) { - final ConfigKey<?> configKey = new ConfigKey<>(SentinelConfig.CONFIG_DEF_NAME, "myid", SentinelConfig.CONFIG_DEF_NAMESPACE); - return JRTServerConfigRequestV3.createFromRequest(JRTClientConfigRequestV3. - createWithParams(configKey, DefContent.fromList(Arrays.asList(SentinelConfig.CONFIG_DEF_SCHEMA)), - fromHost, "", 0, 100, Trace.createDummy(), CompressionType.UNCOMPRESSED, - Optional.empty()).getRequest()); - } - - private class MockConfigResponse implements ConfigResponse { - - private final String line; - public MockConfigResponse(String line) { - this.line = line; - } - - @Override - public Utf8Array getPayload() { - return new Utf8String(""); - } - - @Override - public List<String> getLegacyPayload() { - return Arrays.asList(line); - } - - @Override - public long getGeneration() { - return 1; - } - - @Override - public boolean isInternalRedeploy() { return false; } - - @Override - public String getConfigMd5() { - return "mymd5"; - } - - @Override - public void serialize(OutputStream os, CompressionType uncompressed) throws IOException { - os.write(Utf8.toBytes(line)); - } - - @Override - public CompressionInfo getCompressionInfo() { - return CompressionInfo.uncompressed(); - } - } - -} 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 972446ca267..c8bc9364922 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 @@ -2,7 +2,9 @@ package com.yahoo.vespa.config.server.rpc; import com.google.common.base.Joiner; +import com.yahoo.cloud.config.ConfigserverConfig; import com.yahoo.cloud.config.LbServicesConfig; +import com.yahoo.cloud.config.SentinelConfig; import com.yahoo.config.SimpletypesConfig; import com.yahoo.config.codegen.DefParser; import com.yahoo.config.codegen.InnerCNode; @@ -55,11 +57,12 @@ public class RpcServerTest { testPrintStatistics(tester); testGetConfig(tester); testEnabled(tester); - testEmptyConfigHostedVespa(tester); + testApplicationNotLoadedErrorWhenAppDeleted(tester); + testEmptySentinelConfigWhenAppDeletedOnHostedVespa(); } } - private void testEmptyConfigHostedVespa(RpcTester tester) throws InterruptedException, IOException { + private void testApplicationNotLoadedErrorWhenAppDeleted(RpcTester tester) throws InterruptedException, IOException { tester.rpcServer().onTenantDelete(TenantName.defaultName()); tester.rpcServer().onTenantsLoaded(); JRTClientConfigRequest clientReq = createSimpleRequest(); @@ -74,6 +77,28 @@ public class RpcServerTest { assertTrue(clientReq.validateResponse()); } + @Test + public void testEmptySentinelConfigWhenAppDeletedOnHostedVespa() throws IOException, InterruptedException { + ConfigserverConfig.Builder configBuilder = new ConfigserverConfig.Builder().canReturnEmptySentinelConfig(true); + try (RpcTester tester = new RpcTester(temporaryFolder, configBuilder)) { + tester.rpcServer().onTenantDelete(TenantName.defaultName()); + tester.rpcServer().onTenantsLoaded(); + JRTClientConfigRequest clientReq = createSentinelRequest(); + + // Should get empty sentinel config when on hosted vespa + tester.performRequest(clientReq.getRequest()); + assertTrue(clientReq.validateResponse()); + assertEquals(0, clientReq.errorCode()); + + ConfigPayload payload = ConfigPayload.fromUtf8Array(clientReq.getNewPayload().getData()); + assertNotNull(payload); + SentinelConfig.Builder builder = new SentinelConfig.Builder(); + new ConfigPayloadApplier<>(builder).applyPayload(payload); + SentinelConfig config = new SentinelConfig(builder); + assertEquals(0, config.service().size()); + } + } + private JRTClientConfigRequest createSimpleRequest() { ConfigKey<?> key = new ConfigKey<>(SimpletypesConfig.class, ""); JRTClientConfigRequest clientReq = createRequest(new RawConfig(key, SimpletypesConfig.getDefMd5())); @@ -81,6 +106,13 @@ public class RpcServerTest { return clientReq; } + private JRTClientConfigRequest createSentinelRequest() { + ConfigKey<?> key = new ConfigKey<>(SentinelConfig.class, ""); + JRTClientConfigRequest clientReq = createRequest(new RawConfig(key, SentinelConfig.getDefMd5())); + assertTrue(clientReq.validateParameters()); + return clientReq; + } + private void testEnabled(RpcTester tester) throws IOException, SAXException { tester.generationCounter().increment(); Application app = new Application(new VespaModel(MockApplicationPackage.createEmpty()), 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 3849fc899de..43a2c01f26a 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 @@ -51,19 +51,25 @@ public class RpcTester implements AutoCloseable { private Thread t; private Supervisor sup; private Spec spec; - private int port; private List<Integer> allocatedPorts; private final TemporaryFolder temporaryFolder; + private final ConfigserverConfig configserverConfig; RpcTester(TemporaryFolder temporaryFolder) throws InterruptedException, IOException { + this(temporaryFolder, new ConfigserverConfig.Builder()); + } + + RpcTester(TemporaryFolder temporaryFolder, ConfigserverConfig.Builder configBuilder) throws InterruptedException, IOException { this.temporaryFolder = temporaryFolder; allocatedPorts = new ArrayList<>(); - port = allocatePort(); + int port = allocatePort(); spec = createSpec(port); tenantProvider = new MockTenantProvider(); generationCounter = new MemoryGenerationCounter(); + configBuilder.rpcport(port); + configserverConfig = new ConfigserverConfig(configBuilder); createAndStartRpcServer(); assertFalse(hostLivenessTracker.lastRequestFrom(myHostname).isPresent()); } @@ -81,11 +87,7 @@ public class RpcTester implements AutoCloseable { } void createAndStartRpcServer() throws IOException { - ConfigserverConfig configserverConfig = new ConfigserverConfig(new ConfigserverConfig.Builder()); - rpcServer = new RpcServer(new ConfigserverConfig(new ConfigserverConfig.Builder() - .rpcport(port) - .numRpcThreads(1) - .maxgetconfigclients(1)), + rpcServer = new RpcServer(configserverConfig, new SuperModelRequestHandler(new TestConfigDefinitionRepo(), configserverConfig, new SuperModelManager( |