diff options
author | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2023-01-06 13:44:29 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-06 13:44:29 +0100 |
commit | b75f439e480861ece1517353d7f22e356daa8d1d (patch) | |
tree | bb49cc8c619404fed48a8c8bfbc87e0cf10af8da /config-model | |
parent | 5e102423eb410449268bd5161e870f14d47de50d (diff) | |
parent | a65f9f4a87aa56032eadb2c66ad7bd1a4970c447 (diff) |
Merge pull request #25416 from vespa-engine/bjorncs/hosted-http-filter
Bjorncs/hosted http filter
Diffstat (limited to 'config-model')
13 files changed, 179 insertions, 31 deletions
diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java b/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java index 985fc5fd22e..54521b946dd 100644 --- a/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java +++ b/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java @@ -11,6 +11,7 @@ import com.yahoo.config.application.api.DeployLogger; import com.yahoo.config.application.api.FileRegistry; import com.yahoo.config.application.api.UnparsedConfigDefinition; import com.yahoo.config.application.api.ValidationOverrides; +import com.yahoo.config.model.ConfigModelContext.ApplicationType; import com.yahoo.config.model.api.ConfigDefinitionRepo; import com.yahoo.config.model.api.ContainerEndpoint; import com.yahoo.config.model.api.EndpointCertificateSecrets; @@ -28,11 +29,11 @@ import com.yahoo.config.model.test.MockApplicationPackage; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.Zone; import com.yahoo.io.IOUtils; -import com.yahoo.search.query.profile.QueryProfileRegistry; import com.yahoo.schema.Application; +import com.yahoo.schema.ApplicationBuilder; import com.yahoo.schema.RankProfileRegistry; import com.yahoo.schema.Schema; -import com.yahoo.schema.ApplicationBuilder; +import com.yahoo.search.query.profile.QueryProfileRegistry; import com.yahoo.vespa.config.ConfigDefinition; import com.yahoo.vespa.config.ConfigDefinitionBuilder; import com.yahoo.vespa.config.ConfigDefinitionKey; @@ -302,6 +303,11 @@ public class DeployState implements ConfigDefinitionStore { public Optional<Reindexing> reindexing() { return Optional.ofNullable(reindexing); } + public boolean isHostedTenantApplication(ApplicationType type) { + boolean isTesterApplication = getProperties().applicationId().instance().isTester(); + return isHosted() && type == ApplicationType.DEFAULT && !isTesterApplication; + } + public static class Builder { private ApplicationPackage applicationPackage = MockApplicationPackage.createEmpty(); diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java index 6b8428a07ac..fba2f711111 100644 --- a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java +++ b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java @@ -82,6 +82,7 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea private Architecture adminClusterNodeResourcesArchitecture = Architecture.getDefault(); private boolean useRestrictedDataPlaneBindings = false; private Optional<CloudAccount> cloudAccount = Optional.empty(); + private boolean allowUserFilters = true; @Override public ModelContext.FeatureFlags featureFlags() { return this; } @Override public boolean multitenant() { return multitenant; } @@ -137,6 +138,7 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea @Override public boolean useTwoPhaseDocumentGc() { return useTwoPhaseDocumentGc; } @Override public boolean useRestrictedDataPlaneBindings() { return useRestrictedDataPlaneBindings; } @Override public Optional<CloudAccount> cloudAccount() { return cloudAccount; } + @Override public boolean allowUserFilters() { return allowUserFilters; } public TestProperties sharedStringRepoNoReclaim(boolean sharedStringRepoNoReclaim) { this.sharedStringRepoNoReclaim = sharedStringRepoNoReclaim; @@ -366,6 +368,8 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea return this; } + public TestProperties setAllowUserFilters(boolean b) { this.allowUserFilters = b; return this; } + public static class Spec implements ConfigServerSpec { private final String hostName; 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/UriBindingsValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/UriBindingsValidator.java index a57a8fa9e70..f4aa4f649bd 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/UriBindingsValidator.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/UriBindingsValidator.java @@ -12,8 +12,6 @@ import com.yahoo.vespa.model.container.http.Http; import java.util.logging.Level; -import static com.yahoo.config.model.ConfigModelContext.ApplicationType.HOSTED_INFRASTRUCTURE; - /** * Validates URI bindings for filters and handlers * @@ -71,7 +69,7 @@ class UriBindingsValidator extends Validator { } private static boolean isHostedApplication(VespaModel model, DeployState deployState) { - return deployState.isHosted() && model.getAdmin().getApplicationType() != HOSTED_INFRASTRUCTURE; + return deployState.isHostedTenantApplication(model.getAdmin().getApplicationType()); } private static String createErrorMessage(BindingPattern binding, String message) { 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/main/java/com/yahoo/vespa/model/container/http/AccessControl.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java index 65164ac857b..6f4c35ad6ed 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java @@ -10,7 +10,6 @@ import com.yahoo.vespa.model.container.component.BindingPattern; import com.yahoo.vespa.model.container.component.FileStatusHandlerComponent; import com.yahoo.vespa.model.container.component.Handler; import com.yahoo.vespa.model.container.component.SystemBindingPattern; -import com.yahoo.vespa.model.container.component.chain.Chain; import com.yahoo.vespa.model.container.http.ssl.HostedSslConnectorFactory; import java.util.Collection; @@ -144,7 +143,7 @@ public class AccessControl { // Add a filter chain used by default hosted connector private void addDefaultHostedRequestChain(Http http) { - Chain<Filter> chain = createChain(DEFAULT_CONNECTOR_HOSTED_REQUEST_CHAIN_ID); + HttpFilterChain chain = createChain(DEFAULT_CONNECTOR_HOSTED_REQUEST_CHAIN_ID); http.getFilterChains().add(chain); } @@ -184,7 +183,7 @@ public class AccessControl { rewrittenBinding); } - private static Chain<Filter> createChain(ComponentId id) { return new Chain<>(FilterChains.emptyChainSpec(id)); } + private static HttpFilterChain createChain(ComponentId id) { return new HttpFilterChain(id, HttpFilterChain.Type.SYSTEM); } private static boolean isExcludedHandler(Handler handler) { return EXCLUDED_HANDLERS.contains(handler.getClassId().getName()); } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/FilterChains.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/FilterChains.java index 2139fd731e2..64506ed60e8 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/FilterChains.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/FilterChains.java @@ -6,7 +6,6 @@ import com.yahoo.component.ComponentSpecification; import com.yahoo.component.chain.model.ChainSpecification; import com.yahoo.config.model.producer.AbstractConfigProducer; import com.yahoo.vespa.model.container.component.SimpleComponent; -import com.yahoo.vespa.model.container.component.chain.Chain; import com.yahoo.vespa.model.container.component.chain.Chains; import java.util.Set; @@ -14,16 +13,16 @@ import java.util.Set; /** * @author Tony Vaagenes */ -public class FilterChains extends Chains<Chain<Filter>> { +public class FilterChains extends Chains<HttpFilterChain> { - public FilterChains(AbstractConfigProducer parent) { + public FilterChains(AbstractConfigProducer<?> parent) { super(parent, "filters"); addChild(new SimpleComponent("com.yahoo.container.http.filter.FilterChainRepository")); } public boolean hasChain(ComponentId filterChain) { - for (Chain<Filter> chain : allChains().allComponents()) { + for (HttpFilterChain chain : allChains().allComponents()) { if (chain.getId().equals(filterChain)) return true; } @@ -31,7 +30,7 @@ public class FilterChains extends Chains<Chain<Filter>> { } public boolean hasChainThatInherits(ComponentId filterChain) { - for (Chain<Filter> chain : allChains().allComponents()) { + for (HttpFilterChain chain : allChains().allComponents()) { for (ComponentSpecification spec : chain.getChainSpecification().inheritance.chainSpecifications) { if(spec.toId().equals(filterChain)) return true; diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/Http.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/Http.java index eea3ec68cc0..e6096dc75b3 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/Http.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/Http.java @@ -4,7 +4,6 @@ package com.yahoo.vespa.model.container.http; import com.yahoo.component.provider.ComponentRegistry; import com.yahoo.config.model.producer.AbstractConfigProducer; import com.yahoo.jdisc.http.ServerConfig; -import com.yahoo.vespa.model.container.component.chain.Chain; import com.yahoo.vespa.model.container.component.chain.ChainedComponent; import java.util.Collection; @@ -99,7 +98,7 @@ public class Http extends AbstractConfigProducer<AbstractConfigProducer<?>> impl throw new IllegalArgumentException("Null FilterChains are not allowed when there are filter bindings"); ComponentRegistry<ChainedComponent<?>> filters = filterChains.componentsRegistry(); - ComponentRegistry<Chain<Filter>> chains = filterChains.allChains(); + var chains = filterChains.allChains(); for (FilterBinding binding: bindings) { if (filters.getComponent(binding.chainId()) == null && chains.getComponent(binding.chainId()) == null) diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/HttpFilterChain.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/HttpFilterChain.java new file mode 100644 index 00000000000..2a1c4d1665c --- /dev/null +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/HttpFilterChain.java @@ -0,0 +1,27 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.model.container.http; + +import com.yahoo.component.ComponentId; +import com.yahoo.component.chain.model.ChainSpecification; +import com.yahoo.vespa.model.container.component.chain.Chain; + +/** + * @author bjorncs + */ +public class HttpFilterChain extends Chain<Filter> { + + public enum Type { USER, SYSTEM } + + private final Type type; + + public HttpFilterChain(ChainSpecification inner, Type type) { + super(inner); + this.type = type; + } + + public HttpFilterChain(ComponentId id, Type type) { this(FilterChains.emptyChainSpec(id), type); } + public HttpFilterChain(String id, Type type) { this(ComponentId.fromString(id), type); } + + public Type type() { return type; } + public String id() { return getComponentId().stringValue(); } +} diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterChainBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterChainBuilder.java index 293480e6c7f..7b81b3052a4 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterChainBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterChainBuilder.java @@ -5,8 +5,8 @@ import com.yahoo.component.chain.model.ChainSpecification; import com.yahoo.config.model.deploy.DeployState; import com.yahoo.config.model.producer.AbstractConfigProducer; import com.yahoo.vespa.model.builder.xml.dom.chains.DomChainBuilderBase; -import com.yahoo.vespa.model.container.component.chain.Chain; import com.yahoo.vespa.model.container.http.Filter; +import com.yahoo.vespa.model.container.http.HttpFilterChain; import org.w3c.dom.Element; import java.util.Collection; @@ -18,7 +18,7 @@ import static com.yahoo.vespa.model.builder.xml.dom.chains.ComponentsBuilder.Com /** * @author Tony Vaagenes */ -public class FilterChainBuilder extends DomChainBuilderBase<Filter, Chain<Filter>> { +public class FilterChainBuilder extends DomChainBuilderBase<Filter, HttpFilterChain> { private static final Collection<ComponentType<Filter>> allowedComponentTypes = Collections.singleton(ComponentType.filter); @@ -27,7 +27,7 @@ public class FilterChainBuilder extends DomChainBuilderBase<Filter, Chain<Filter } @Override - protected Chain<Filter> buildChain(DeployState deployState, AbstractConfigProducer<?> ancestor, Element producerSpec, ChainSpecification specWithoutInnerComponents) { - return new Chain<>(specWithoutInnerComponents); + protected HttpFilterChain buildChain(DeployState deployState, AbstractConfigProducer<?> ancestor, Element producerSpec, ChainSpecification specWithoutInnerComponents) { + return new HttpFilterChain(specWithoutInnerComponents, HttpFilterChain.Type.USER); } } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterChainsBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterChainsBuilder.java index 35a91fc95ab..6325ee8da05 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterChainsBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/FilterChainsBuilder.java @@ -8,9 +8,9 @@ import com.yahoo.vespa.model.builder.xml.dom.chains.ComponentsBuilder; import com.yahoo.vespa.model.builder.xml.dom.chains.ComponentsBuilder.ComponentType; import com.yahoo.vespa.model.builder.xml.dom.chains.DomChainBuilderBase; import com.yahoo.vespa.model.builder.xml.dom.chains.DomChainsBuilder; -import com.yahoo.vespa.model.container.component.chain.Chain; import com.yahoo.vespa.model.container.http.Filter; import com.yahoo.vespa.model.container.http.FilterChains; +import com.yahoo.vespa.model.container.http.HttpFilterChain; import org.w3c.dom.Element; import java.util.Collection; @@ -21,11 +21,11 @@ import java.util.Map; /** * @author Tony Vaagenes */ -public class FilterChainsBuilder extends DomChainsBuilder<Filter, Chain<Filter>, FilterChains> { +public class FilterChainsBuilder extends DomChainsBuilder<Filter, HttpFilterChain, FilterChains> { private static final Collection<ComponentType<Filter>> allowedComponentTypes = Collections.singleton(ComponentType.filter); - private static final Map<String, Class<? extends DomChainBuilderBase<? extends Filter, ? extends Chain<Filter>>>> chainType2BuilderClass = + private static final Map<String, Class<? extends DomChainBuilderBase<? extends Filter, ? extends HttpFilterChain>>> chainType2BuilderClass = Map.of( HttpBuilder.REQUEST_CHAIN_TAG_NAME, FilterChainBuilder.class, HttpBuilder.RESPONSE_CHAIN_TAG_NAME, FilterChainBuilder.class); @@ -40,7 +40,7 @@ public class FilterChainsBuilder extends DomChainsBuilder<Filter, Chain<Filter>, } @Override - protected ChainsBuilder<Filter, Chain<Filter>> readChains( + protected ChainsBuilder<Filter, HttpFilterChain> readChains( DeployState deployState, AbstractConfigProducer<?> ancestor, List<Element> allChainsElems, Map<String, ComponentsBuilder.ComponentType<?>> outerComponentTypeByComponentName) { diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java index f48f91e8cd0..d0a03be2869 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java @@ -1,7 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.model.container.xml; -import com.yahoo.component.ComponentId; import com.yahoo.component.ComponentSpecification; import com.yahoo.component.Version; import com.yahoo.component.chain.dependencies.Dependencies; @@ -13,7 +12,6 @@ import com.yahoo.config.application.api.DeployLogger; import com.yahoo.config.application.api.DeploymentInstanceSpec; import com.yahoo.config.application.api.DeploymentSpec; import com.yahoo.config.model.ConfigModelContext; -import com.yahoo.config.model.ConfigModelContext.ApplicationType; import com.yahoo.config.model.api.ApplicationClusterEndpoint; import com.yahoo.config.model.api.ConfigServerSpec; import com.yahoo.config.model.api.ContainerEndpoint; @@ -77,7 +75,6 @@ import com.yahoo.vespa.model.container.component.Handler; import com.yahoo.vespa.model.container.component.SimpleComponent; import com.yahoo.vespa.model.container.component.SystemBindingPattern; import com.yahoo.vespa.model.container.component.UserBindingPattern; -import com.yahoo.vespa.model.container.component.chain.Chain; import com.yahoo.vespa.model.container.docproc.ContainerDocproc; import com.yahoo.vespa.model.container.docproc.DocprocChains; import com.yahoo.vespa.model.container.http.AccessControl; @@ -90,6 +87,7 @@ import com.yahoo.vespa.model.container.http.Http; import com.yahoo.vespa.model.container.http.JettyHttpServer; import com.yahoo.vespa.model.container.http.ssl.HostedSslConnectorFactory; import com.yahoo.vespa.model.container.http.xml.HttpBuilder; +import com.yahoo.vespa.model.container.http.HttpFilterChain; import com.yahoo.vespa.model.container.processing.ProcessingChains; import com.yahoo.vespa.model.container.search.ContainerSearch; import com.yahoo.vespa.model.container.search.PageTemplates; @@ -98,6 +96,7 @@ import com.yahoo.vespa.model.container.xml.document.DocumentFactoryBuilder; import com.yahoo.vespa.model.content.StorageGroup; import org.w3c.dom.Element; import org.w3c.dom.Node; + import java.io.IOException; import java.io.Reader; import java.net.URI; @@ -462,7 +461,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { if (!deployState.isHosted() || !deployState.zone().system().isPublic()) return; // Setup secure filter chain - var secureChain = new Chain<Filter>(FilterChains.emptyChainSpec(ComponentId.fromString("cloud-data-plane-secure"))); + var secureChain = new HttpFilterChain("cloud-data-plane-secure", HttpFilterChain.Type.SYSTEM); secureChain.addInnerComponent(new CloudDataPlaneFilter(cluster, cluster.clientsLegacyMode())); cluster.getHttp().getFilterChains().add(secureChain); // Set cloud data plane filter as default request filter chain for data plane connector @@ -471,7 +470,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { .setDefaultRequestFilterChain(secureChain.getComponentId()); // Setup insecure filter chain - var insecureChain = new Chain<Filter>(FilterChains.emptyChainSpec(ComponentId.fromString("cloud-data-plane-insecure"))); + var insecureChain = new HttpFilterChain("cloud-data-plane-insecure", HttpFilterChain.Type.SYSTEM); insecureChain.addInnerComponent(new Filter( new ChainedComponentModel( new BundleInstantiationSpecification( @@ -597,9 +596,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { } private static boolean isHostedTenantApplication(ConfigModelContext context) { - var deployState = context.getDeployState(); - boolean isTesterApplication = deployState.getProperties().applicationId().instance().isTester(); - return deployState.isHosted() && context.getApplicationType() == ApplicationType.DEFAULT && !isTesterApplication; + return context.getDeployState().isHostedTenantApplication(context.getApplicationType()); } private static void addHostedImplicitHttpIfNotPresent(DeployState deployState, ApplicationContainerCluster cluster) { 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..62a8b1131fa --- /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).setAllowUserFilters(false)) + .build(); + VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState); + new CloudUserFilterValidator().validate(model, deployState); + } + +}
\ No newline at end of file |