diff options
author | Bjørn Christian Seime <bjorncs@yahooinc.com> | 2023-01-05 15:07:35 +0100 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@yahooinc.com> | 2023-01-05 15:56:41 +0100 |
commit | 675c08c2eb8da55ec5730cdabad0d130c77d26e8 (patch) | |
tree | 4827a1c5a3b09a9d7a1db1798c4511764093f73b /config-model | |
parent | b2892d9d8e4f8965d1698fb0a774c35d51e9916b (diff) |
Add validator that fails on user configured http filters
Diffstat (limited to 'config-model')
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 |