summaryrefslogtreecommitdiffstats
path: root/config-model
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@yahooinc.com>2023-01-05 15:07:35 +0100
committerBjørn Christian Seime <bjorncs@yahooinc.com>2023-01-05 15:56:41 +0100
commit675c08c2eb8da55ec5730cdabad0d130c77d26e8 (patch)
tree4827a1c5a3b09a9d7a1db1798c4511764093f73b /config-model
parentb2892d9d8e4f8965d1698fb0a774c35d51e9916b (diff)
Add validator that fails on user configured http filters
Diffstat (limited to 'config-model')
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudUserFilterValidator.java50
-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/CloudUserFilterValidatorTest.java68
3 files changed, 119 insertions, 0 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudUserFilterValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudUserFilterValidator.java
new file mode 100644
index 00000000000..3d4a0e2c919
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudUserFilterValidator.java
@@ -0,0 +1,50 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.model.application.validation;
+
+import com.yahoo.config.model.deploy.DeployState;
+import com.yahoo.vespa.model.VespaModel;
+import com.yahoo.vespa.model.container.http.HttpFilterChain;
+
+import java.util.Comparator;
+import java.util.TreeSet;
+import java.util.logging.Level;
+import java.util.stream.Collectors;
+
+/**
+ * Validates that only allowed-listed cloud applications can set up user-specified filter chains
+ *
+ * @author bjorncs
+ */
+public class CloudUserFilterValidator extends Validator {
+
+ @Override
+ public void validate(VespaModel model, DeployState state) {
+ if (!state.isHostedTenantApplication(model.getAdmin().getApplicationType())) return;
+ if (state.getProperties().allowUserFilters()) return;
+ var violations = new TreeSet<Violation>();
+ for (var cluster : model.getContainerClusters().values()) {
+ if (cluster.getHttp() == null) continue;
+ for (var chain : cluster.getHttp().getFilterChains().allChains().allComponents()) {
+ if (chain.type() == HttpFilterChain.Type.USER) {
+ var msg = "Found filter chain violation - chain '%s' in cluster '%s'".formatted(cluster.name(), chain.id());
+ state.getDeployLogger().log(Level.WARNING, msg);
+ violations.add(new Violation(cluster.name(), chain.id()));
+ }
+ }
+ }
+ if (violations.isEmpty()) return;
+ var violationsStr = violations.stream()
+ .map(v -> "chain '%s' in cluster '%s'".formatted(v.chain(), v.cluster()))
+ .collect(Collectors.joining(", ", "[", "]"));
+ var msg = ("HTTP filter chains are currently not supported in Vespa Cloud (%s)").formatted(violationsStr);
+ throw new IllegalArgumentException(msg);
+ }
+
+ private record Violation(String cluster, String chain) implements Comparable<Violation> {
+ @Override
+ public int compareTo(Violation other) {
+ return Comparator.comparing(Violation::chain).thenComparing(Violation::cluster).compare(this, other);
+ }
+ }
+
+}
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 a558902d6ff..877dd12104f 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
@@ -89,6 +89,7 @@ public class Validation {
new UriBindingsValidator().validate(model, deployState);
new CloudDataPlaneFilterValidator().validate(model, deployState);
new AccessControlFilterExcludeValidator().validate(model, deployState);
+ new CloudUserFilterValidator().validate(model, deployState);
additionalValidators.forEach(v -> v.validate(model, deployState));
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/CloudUserFilterValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/CloudUserFilterValidatorTest.java
new file mode 100644
index 00000000000..12cc5a064a5
--- /dev/null
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/CloudUserFilterValidatorTest.java
@@ -0,0 +1,68 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+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.vespa.model.VespaModel;
+import org.junit.jupiter.api.Test;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * @author bjorncs
+ */
+class CloudUserFilterValidatorTest {
+
+ @Test
+ void fails_on_user_configured_filter_chain() {
+ var exception = assertThrows(IllegalArgumentException.class, () -> runValidatorOnApp(true, ""));
+ var expected = "HTTP filter chains are currently not supported in Vespa Cloud ([chain 'myChain' in cluster 'container'])";
+ assertEquals(expected, exception.getMessage());
+ }
+
+ @Test
+ void allows_user_configured_filter_chain_for_infrastructure_app() {
+ assertDoesNotThrow(() -> runValidatorOnApp(true, " application-type='hosted-infrastructure'"));
+ }
+
+ @Test
+ void allows_user_configured_filter_chain_for_self_hosted() {
+ assertDoesNotThrow(() -> runValidatorOnApp(false, ""));
+ }
+
+ private static void runValidatorOnApp(boolean isHosted, String applicationTypeAttribute) throws IOException, SAXException {
+ String servicesXml = """
+ <services version='1.0'%s>
+ <container version='1.0'>
+ <http>
+ <filtering>
+ <request-chain id='myChain'>
+ <filter id='myFilter'/>
+ <binding>http://*/search/</binding>
+ </request-chain>
+ </filtering>
+ </http>
+ </container>
+ </services>
+ """.formatted(applicationTypeAttribute);
+ ApplicationPackage app = new MockApplicationPackage.Builder()
+ .withServices(servicesXml)
+ .build();
+ DeployState deployState = new DeployState.Builder()
+ .applicationPackage(app)
+ .properties(new TestProperties().setHostedVespa(isHosted))
+ .build();
+ VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState);
+ new CloudUserFilterValidator().validate(model, deployState);
+ }
+
+} \ No newline at end of file