summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgjoranv <gv@verizonmedia.com>2020-02-28 15:34:26 +0100
committergjoranv <gv@verizonmedia.com>2020-03-02 12:09:35 +0100
commitd8f0bd58edaf6a173a74fbde2a1b5ce5797711d6 (patch)
treed9977f35e9b3f9457c4b8b691beacc8c82eecea4
parentaea10120b5a1d75aa9ae050a58443fa9a7a988d2 (diff)
Add validator for 'cloudwatch' in services.xml
- Only allowed in public zones and for self-hosted
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudWatchValidator.java37
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java1
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/CloudWatchValidatorTest.java101
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>"
+ );
+ }
+
+}