diff options
author | gjoranv <gv@verizonmedia.com> | 2020-03-02 14:02:27 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-02 14:02:27 +0100 |
commit | c2d421329ab788653165ff5cb1e65f9c96dbe987 (patch) | |
tree | c6ebad8740311bcb301aa4d4b46401e9a6b8230f /config-model | |
parent | d55ede11d482906611cf69bcd002e2b1b8140419 (diff) | |
parent | d8f0bd58edaf6a173a74fbde2a1b5ce5797711d6 (diff) |
Merge pull request #12392 from vespa-engine/gjoranv/prohibit-cloudwatch-for-hosted
Add validator for 'cloudwatch' in services.xml
Diffstat (limited to 'config-model')
3 files changed, 139 insertions, 0 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudWatchValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudWatchValidator.java new file mode 100644 index 00000000000..462ac39fa84 --- /dev/null +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudWatchValidator.java @@ -0,0 +1,37 @@ +package com.yahoo.vespa.model.application.validation; + +import com.yahoo.config.model.ConfigModelContext; +import com.yahoo.config.model.deploy.DeployState; +import com.yahoo.vespa.model.VespaModel; +import com.yahoo.vespa.model.admin.monitoring.MetricsConsumer; + +import java.util.List; + +import static java.util.stream.Collectors.toList; + +/** + * @author gjoranv + */ +public class CloudWatchValidator extends Validator { + + @Override + public void validate(VespaModel model, DeployState deployState) { + if (!deployState.isHosted()) return; + if (deployState.zone().system().isPublic()) return; + if (model.getAdmin().getApplicationType() != ConfigModelContext.ApplicationType.DEFAULT) return; + + var offendingConsumers = model.getAdmin().getUserMetrics().getConsumers().values().stream() + .filter(consumer -> !consumer.cloudWatches().isEmpty()) + .collect(toList()); + + if (! offendingConsumers.isEmpty()) { + throw new IllegalArgumentException("CloudWatch cannot be set up for non-public hosted Vespa and must " + + "be removed for consumers: " + consumerIds(offendingConsumers)); + } + } + + private List<String> consumerIds(List<MetricsConsumer> offendingConsumers) { + return offendingConsumers.stream().map(MetricsConsumer::getId).collect(toList()); + } + +} diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java index 1e4a45428b8..b03917dec3b 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java @@ -59,6 +59,7 @@ public class Validation { new SecretStoreValidator().validate(model, deployState); new EndpointCertificateSecretsValidator().validate(model, deployState); new AccessControlFilterValidator().validate(model, deployState); + new CloudWatchValidator().validate(model, deployState); List<ConfigChangeAction> result = Collections.emptyList(); if (deployState.getProperties().isFirstTimeDeployment()) { diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/CloudWatchValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/CloudWatchValidatorTest.java new file mode 100644 index 00000000000..40b8223479d --- /dev/null +++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/CloudWatchValidatorTest.java @@ -0,0 +1,101 @@ +package com.yahoo.vespa.model.application.validation; + +import com.yahoo.config.application.api.ApplicationPackage; +import com.yahoo.config.model.NullConfigModelRegistry; +import com.yahoo.config.model.deploy.DeployState; +import com.yahoo.config.model.deploy.TestProperties; +import com.yahoo.config.model.test.MockApplicationPackage; +import com.yahoo.config.provision.Environment; +import com.yahoo.config.provision.RegionName; +import com.yahoo.config.provision.SystemName; +import com.yahoo.config.provision.Zone; +import com.yahoo.vespa.model.VespaModel; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.xml.sax.SAXException; + +import java.io.IOException; + +import static com.yahoo.config.provision.Environment.prod; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + + +/** + * @author gjoranv + */ +public class CloudWatchValidatorTest { + + @Rule + public final ExpectedException exceptionRule = ExpectedException.none(); + + @Test + public void cloudwatch_in_public_zones_passes_validation() throws IOException, SAXException { + DeployState deployState = deployState(servicesWithCloudwatch(), true, true); + VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState); + + new CloudWatchValidator().validate(model, deployState); + } + + @Test + public void cloudwatch_passes_validation_for_self_hosted_vespa() throws IOException, SAXException { + DeployState deployState = deployState(servicesWithCloudwatch(), false, false); + VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState); + + new CloudWatchValidator().validate(model, deployState); + } + + @Test + public void cloudwatch_in_non_public_zones_fails_validation() throws IOException, SAXException { + exceptionRule.expect(IllegalArgumentException.class); + exceptionRule.expectMessage( + "CloudWatch cannot be set up for non-public hosted Vespa and must be removed for consumers: [cloudwatch-consumer]"); + + DeployState deployState = deployState(servicesWithCloudwatch(), true, false); + VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState); + + new CloudWatchValidator().validate(model, deployState); + } + + private static DeployState deployState(String servicesXml, boolean isHosted, boolean isPublic) { + ApplicationPackage app = new MockApplicationPackage.Builder() + .withServices(servicesXml) + .build(); + + DeployState.Builder builder = new DeployState.Builder() + .applicationPackage(app) + .properties(new TestProperties().setHostedVespa(isHosted)); + if (isHosted) { + var system = isPublic ? SystemName.Public : SystemName.main; + builder.zone(new Zone(system, Environment.prod, RegionName.from("foo"))); + } + final DeployState deployState = builder.build(); + + if (isHosted) { + assertTrue("Test must emulate a hosted deployment.", deployState.isHosted()); + assertEquals("Test must emulate a prod environment.", prod, deployState.zone().environment()); + } + return deployState; + } + + private String servicesWithCloudwatch() { + return String.join("\n", + "<services>", + " <admin version='2.0'>", + " <adminserver hostalias='node1'/>", + " <metrics>", + " <consumer id='cloudwatch-consumer'>", + " <metric id='my-metric'/>", + " <cloudwatch region='us-east-1' namespace='my-namespace' >", + " <credentials access-key-name='my-access-key' ", + " secret-key-name='my-secret-key' />", + " </cloudwatch>", + " </consumer>", + " </metrics>", + " </admin>", + "</services>" + ); + } + +} |