diff options
author | Harald Musum <musum@verizonmedia.com> | 2020-08-18 10:39:52 +0200 |
---|---|---|
committer | Harald Musum <musum@verizonmedia.com> | 2020-08-18 10:39:52 +0200 |
commit | 9dc4c09252afbdb352805e87d54a3c4a75d4d49b (patch) | |
tree | ebc43a6e8fc2be1b071b56b3b4b8be3c36d3cca3 | |
parent | f201978bbe446989bd2b6b749ff4d6c42807026f (diff) | |
parent | ef6227ebc92c625081f703a806e0b0c2c02a5a1c (diff) |
Merge branch 'master' into hmusum/move-methods-to-Session
72 files changed, 1223 insertions, 825 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainerCluster.java index f9338f9cb35..9ae9a158631 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainerCluster.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainerCluster.java @@ -7,6 +7,7 @@ import com.yahoo.container.handler.ThreadpoolConfig; import com.yahoo.search.config.QrStartConfig; import com.yahoo.vespa.model.container.ContainerCluster; import com.yahoo.vespa.model.container.component.Handler; +import com.yahoo.vespa.model.container.component.SystemBindingPattern; /** * @author hmusum @@ -39,7 +40,7 @@ public class LogserverContainerCluster extends ContainerCluster<LogserverContain private void addLogHandler() { Handler<?> logHandler = Handler.fromClassName(ContainerCluster.LOG_HANDLER_CLASS); - logHandler.addServerBindings("http://*/logs"); + logHandler.addServerBindings(SystemBindingPattern.fromHttpPath("/logs")); addComponent(logHandler); } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java index 08f4e2fa12f..5b3e4e1479e 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java @@ -14,6 +14,7 @@ import com.yahoo.vespa.model.container.Container; import com.yahoo.vespa.model.container.component.AccessLogComponent; import com.yahoo.vespa.model.container.component.Component; import com.yahoo.vespa.model.container.component.Handler; +import com.yahoo.vespa.model.container.component.SystemBindingPattern; import com.yahoo.vespa.model.container.xml.PlatformBundles; import java.util.Set; @@ -36,10 +37,10 @@ public class ClusterControllerContainer extends Container implements super(parent, "" + index, index); addHandler("clustercontroller-status", "com.yahoo.vespa.clustercontroller.apps.clustercontroller.StatusHandler", - "clustercontroller-status/*"); + "/clustercontroller-status/*"); addHandler("clustercontroller-state-restapi-v2", "com.yahoo.vespa.clustercontroller.apps.clustercontroller.StateRestApiV2Handler", - "cluster/v2/*"); + "/cluster/v2/*"); if (runStandaloneZooKeeper) { addComponent("clustercontroller-zkrunner", "com.yahoo.vespa.zookeeper.VespaZooKeeperServerImpl", @@ -77,8 +78,8 @@ public class ClusterControllerContainer extends Container implements return ContainerServiceType.CLUSTERCONTROLLER_CONTAINER; } - private void addHandler(Handler h, String binding) { - h.addServerBindings("http://*/" + binding); + private void addHandler(Handler h, String path) { + h.addServerBindings(SystemBindingPattern.fromHttpPath(path)); super.addHandler(h); } @@ -96,9 +97,8 @@ public class ClusterControllerContainer extends Container implements addComponent(new Component<>(createComponentModel(id, className, bundle))); } - private void addHandler(String id, String className, String binding) { - addHandler(new Handler(createComponentModel(id, className, CLUSTERCONTROLLER_BUNDLE)), - binding); + private void addHandler(String id, String className, String path) { + addHandler(new Handler(createComponentModel(id, className, CLUSTERCONTROLLER_BUNDLE)), path); } @Override diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java index 4dc9811a024..b5936887b50 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java @@ -7,12 +7,12 @@ import ai.vespa.metricsproxy.core.MetricsConsumers; import ai.vespa.metricsproxy.core.MetricsManager; import ai.vespa.metricsproxy.core.MonitoringConfig; import ai.vespa.metricsproxy.core.VespaMetrics; -import ai.vespa.metricsproxy.http.metrics.MetricsV1Handler; import ai.vespa.metricsproxy.http.application.ApplicationMetricsHandler; import ai.vespa.metricsproxy.http.application.ApplicationMetricsRetriever; import ai.vespa.metricsproxy.http.application.MetricsNodesConfig; -import ai.vespa.metricsproxy.http.yamas.YamasHandler; +import ai.vespa.metricsproxy.http.metrics.MetricsV1Handler; import ai.vespa.metricsproxy.http.prometheus.PrometheusHandler; +import ai.vespa.metricsproxy.http.yamas.YamasHandler; import ai.vespa.metricsproxy.metric.ExternalMetrics; import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensions; import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensionsConfig; @@ -38,6 +38,7 @@ import com.yahoo.vespa.model.admin.monitoring.MetricsConsumer; import com.yahoo.vespa.model.admin.monitoring.Monitoring; import com.yahoo.vespa.model.container.ContainerCluster; import com.yahoo.vespa.model.container.component.Handler; +import com.yahoo.vespa.model.container.component.SystemBindingPattern; import com.yahoo.vespa.model.container.xml.PlatformBundles; import java.nio.file.Path; @@ -129,8 +130,9 @@ public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyC static Handler<AbstractConfigProducer<?>> createMetricsHandler(Class<? extends ThreadedHttpRequestHandler> clazz, String bindingPath) { Handler<AbstractConfigProducer<?>> metricsHandler = new Handler<>( new ComponentModel(clazz.getName(), null, METRICS_PROXY_BUNDLE_NAME, null)); - metricsHandler.addServerBindings("http://*" + bindingPath, - "http://*" + bindingPath + "/*"); + metricsHandler.addServerBindings( + SystemBindingPattern.fromHttpPath(bindingPath), + SystemBindingPattern.fromHttpPath(bindingPath + "/*")); return metricsHandler; } 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 new file mode 100644 index 00000000000..249827b11d9 --- /dev/null +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/UriBindingsValidator.java @@ -0,0 +1,80 @@ +// Copyright Verizon Media. 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.ApplicationContainerCluster; +import com.yahoo.vespa.model.container.component.BindingPattern; +import com.yahoo.vespa.model.container.component.Handler; +import com.yahoo.vespa.model.container.component.SystemBindingPattern; +import com.yahoo.vespa.model.container.http.FilterBinding; +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 + * + * @author bjorncs + */ +class UriBindingsValidator extends Validator { + + @Override + public void validate(VespaModel model, DeployState deployState) { + for (ApplicationContainerCluster cluster : model.getContainerClusters().values()) { + for (Handler<?> handler : cluster.getHandlers()) { + for (BindingPattern binding : handler.getServerBindings()) { + validateUserBinding(binding, model, deployState); + } + } + Http http = cluster.getHttp(); + if (http != null) { + for (FilterBinding binding : cluster.getHttp().getBindings()) { + validateUserBinding(binding.binding(), model, deployState); + } + } + } + } + + private static void validateUserBinding(BindingPattern binding, VespaModel model, DeployState deployState) { + validateScheme(binding, deployState); + if (isHostedApplication(model, deployState)) { + validateHostedApplicationUserBinding(binding); + } + } + + private static void validateScheme(BindingPattern binding, DeployState deployState) { + if (binding.scheme().equals("https")) { + String message = createErrorMessage( + binding, "'https' bindings are deprecated, use 'http' instead to bind to both http and https traffic."); + deployState.getDeployLogger().log(Level.WARNING, message); + } + } + + private static void validateHostedApplicationUserBinding(BindingPattern binding) { + // only perform these validation for used-generated bindings + // bindings produced by the hosted config model amender will violate some of the rules below + if (binding instanceof SystemBindingPattern) return; + + if (binding.port().isPresent()) { + throw new IllegalArgumentException(createErrorMessage(binding, "binding with port is not allowed")); + } + if (!binding.host().equals(BindingPattern.WILDCARD_PATTERN)) { + throw new IllegalArgumentException(createErrorMessage(binding, "only binding with wildcard ('*') for hostname is allowed")); + } + if (!binding.scheme().equals("http") && !binding.scheme().equals("https")) { + throw new IllegalArgumentException(createErrorMessage(binding, "only 'http' is allowed as scheme")); + } + } + + private static boolean isHostedApplication(VespaModel model, DeployState deployState) { + return deployState.isHosted() && model.getAdmin().getApplicationType() != HOSTED_INFRASTRUCTURE; + } + + private static String createErrorMessage(BindingPattern binding, String message) { + return String.format("For binding '%s': %s", binding.patternString(), 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 22dd0289390..3a4dee300da 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 @@ -61,6 +61,7 @@ public class Validation { new AccessControlFilterValidator().validate(model, deployState); new CloudWatchValidator().validate(model, deployState); new AwsAccessControlValidator().validate(model, deployState); + new UriBindingsValidator().validate(model, deployState); List<ConfigChangeAction> result = Collections.emptyList(); if (deployState.getProperties().isFirstTimeDeployment()) { diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomClientProviderBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomClientProviderBuilder.java index 11fab0ada29..0fdd1af56f3 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomClientProviderBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomClientProviderBuilder.java @@ -2,11 +2,12 @@ package com.yahoo.vespa.model.builder.xml.dom; import com.yahoo.config.model.deploy.DeployState; -import com.yahoo.text.XML; import com.yahoo.config.model.producer.AbstractConfigProducer; +import com.yahoo.text.XML; import com.yahoo.vespa.model.container.ApplicationContainerCluster; import com.yahoo.vespa.model.container.component.Component; import com.yahoo.vespa.model.container.component.Handler; +import com.yahoo.vespa.model.container.component.UserBindingPattern; import org.w3c.dom.Element; /** @@ -24,10 +25,10 @@ public class DomClientProviderBuilder extends DomHandlerBuilder { Handler<? super Component<?, ?>> client = createHandler(clientElement); for (Element binding : XML.getChildren(clientElement, "binding")) - client.addClientBindings(XML.getValue(binding)); + client.addClientBindings(UserBindingPattern.fromPattern(XML.getValue(binding))); for (Element serverBinding : XML.getChildren(clientElement, "serverBinding")) - client.addServerBindings(XML.getValue(serverBinding)); + client.addServerBindings(UserBindingPattern.fromPattern(XML.getValue(serverBinding))); DomComponentBuilder.addChildren(deployState, parent, clientElement, client); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java index ac6d089cf24..145535fe06f 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java @@ -8,8 +8,10 @@ import com.yahoo.container.bundle.BundleInstantiationSpecification; import com.yahoo.osgi.provider.model.ComponentModel; import com.yahoo.text.XML; import com.yahoo.vespa.model.container.ApplicationContainerCluster; +import com.yahoo.vespa.model.container.component.BindingPattern; import com.yahoo.vespa.model.container.component.Component; import com.yahoo.vespa.model.container.component.Handler; +import com.yahoo.vespa.model.container.component.UserBindingPattern; import com.yahoo.vespa.model.container.xml.BundleInstantiationSpecificationBuilder; import org.w3c.dom.Element; @@ -27,11 +29,14 @@ import static java.util.logging.Level.INFO; */ public class DomHandlerBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Handler> { - private static final Set<String> reservedBindings = Set.of(METRICS_V2_HANDLER_BINDING_1, - METRICS_V2_HANDLER_BINDING_2, - STATE_HANDLER_BINDING_1, - STATE_HANDLER_BINDING_2, - VIP_HANDLER_BINDING); + private static final Set<BindingPattern> reservedBindings = + Set.of( + METRICS_V2_HANDLER_BINDING_1, + METRICS_V2_HANDLER_BINDING_2, + STATE_HANDLER_BINDING_1, + STATE_HANDLER_BINDING_2, + VIP_HANDLER_BINDING); + private final ApplicationContainerCluster cluster; public DomHandlerBuilder(ApplicationContainerCluster cluster) { @@ -43,10 +48,10 @@ public class DomHandlerBuilder extends VespaDomBuilder.DomConfigProducerBuilder< Handler<? super Component<?, ?>> handler = createHandler(handlerElement); for (Element binding : XML.getChildren(handlerElement, "binding")) - addServerBinding(handler, XML.getValue(binding), deployState.getDeployLogger()); + addServerBinding(handler, UserBindingPattern.fromPattern(XML.getValue(binding)), deployState.getDeployLogger()); for (Element clientBinding : XML.getChildren(handlerElement, "clientBinding")) - handler.addClientBindings(XML.getValue(clientBinding)); + handler.addClientBindings(UserBindingPattern.fromPattern(XML.getValue(clientBinding))); DomComponentBuilder.addChildren(deployState, parent, handlerElement, handler); @@ -58,27 +63,30 @@ public class DomHandlerBuilder extends VespaDomBuilder.DomConfigProducerBuilder< return new Handler<>(new ComponentModel(bundleSpec)); } - private void addServerBinding(Handler<? super Component<?, ?>> handler, String binding, DeployLogger log) { + private void addServerBinding(Handler<? super Component<?, ?>> handler, BindingPattern binding, DeployLogger log) { throwIfBindingIsReserved(binding, handler); handler.addServerBindings(binding); removeExistingServerBinding(binding, handler, log); } - private void throwIfBindingIsReserved(String binding, Handler<?> newHandler) { + private void throwIfBindingIsReserved(BindingPattern binding, Handler<?> newHandler) { for (var reserved : reservedBindings) { - if (binding.equals(reserved)) { - throw new IllegalArgumentException("Binding '" + binding + "' is a reserved Vespa binding and " + + if (binding.hasSamePattern(reserved)) { + throw new IllegalArgumentException("Binding '" + binding.patternString() + "' is a reserved Vespa binding and " + "cannot be used by handler: " + newHandler.getComponentId()); } } } - private void removeExistingServerBinding(String binding, Handler<?> newHandler, DeployLogger log) { + private void removeExistingServerBinding(BindingPattern binding, Handler<?> newHandler, DeployLogger log) { for (var handler : cluster.getHandlers()) { - if (handler.getServerBindings().contains(binding)) { - handler.removeServerBinding(binding); - log.log(INFO, "Binding '" + binding + "' was already in use by handler '" + - handler.getComponentId() + "', but will now be taken over by handler: " + newHandler.getComponentId()); + for (BindingPattern serverBinding : handler.getServerBindings()) { + if (serverBinding.hasSamePattern(binding)) { + handler.removeServerBinding(serverBinding); + log.log(INFO, "Binding '" + binding.patternString() + "' was already in use by handler '" + + handler.getComponentId() + "', but will now be taken over by handler: " + newHandler.getComponentId()); + + } } } } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java b/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java index 58f03bffb30..159a87be27d 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java @@ -6,6 +6,8 @@ import com.yahoo.container.bundle.BundleInstantiationSpecification; import com.yahoo.osgi.provider.model.ComponentModel; import com.yahoo.vespa.model.container.ContainerCluster; import com.yahoo.vespa.model.container.component.Handler; +import com.yahoo.vespa.model.container.component.SystemBindingPattern; +import com.yahoo.vespa.model.container.component.UserBindingPattern; import java.util.Collection; import java.util.Collections; @@ -24,7 +26,7 @@ public class ContainerDocumentApi { } private void setupHandlers(ContainerCluster cluster) { - cluster.addComponent(newVespaClientHandler("com.yahoo.document.restapi.resource.RestApi", "document/v1/*")); + cluster.addComponent(newVespaClientHandler("com.yahoo.document.restapi.resource.RestApi", "/document/v1/*")); cluster.addComponent(newVespaClientHandler("com.yahoo.vespa.http.server.FeedHandler", ContainerCluster.RESERVED_URI_PREFIX + "/feedapi")); } @@ -32,9 +34,18 @@ public class ContainerDocumentApi { Handler<AbstractConfigProducer<?>> handler = new Handler<>(new ComponentModel( BundleInstantiationSpecification.getFromStrings(componentId, null, vespaClientBundleSpecification), "")); - for (String rootBinding : options.bindings) { - handler.addServerBindings(rootBinding + bindingSuffix, - rootBinding + bindingSuffix + '/'); + if (options.bindings.isEmpty()) { + handler.addServerBindings( + SystemBindingPattern.fromHttpPath(bindingSuffix), + SystemBindingPattern.fromHttpPath(bindingSuffix + '/')); + } else { + for (String rootBinding : options.bindings) { + String pathWithoutLeadingSlash = bindingSuffix.substring(1); + handler.addServerBindings( + UserBindingPattern.fromPattern(rootBinding + pathWithoutLeadingSlash), + UserBindingPattern.fromPattern(rootBinding + pathWithoutLeadingSlash + '/')); + } + } return handler; } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java index b0ac02d0fe8..1427fa492dc 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java @@ -22,10 +22,12 @@ import com.yahoo.search.config.QrStartConfig; import com.yahoo.vespa.config.search.RankProfilesConfig; import com.yahoo.vespa.config.search.core.RankingConstantsConfig; import com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainer; +import com.yahoo.vespa.model.container.component.BindingPattern; import com.yahoo.vespa.model.container.component.Component; import com.yahoo.vespa.model.container.component.ConfigProducerGroup; import com.yahoo.vespa.model.container.component.Handler; import com.yahoo.vespa.model.container.component.Servlet; +import com.yahoo.vespa.model.container.component.SystemBindingPattern; import com.yahoo.vespa.model.container.jersey.Jersey2Servlet; import com.yahoo.vespa.model.container.jersey.RestApi; import com.yahoo.vespa.model.container.xml.PlatformBundles; @@ -55,12 +57,12 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat MetricsProxyApiConfig.Producer { public static final String METRICS_V2_HANDLER_CLASS = MetricsV2Handler.class.getName(); - public static final String METRICS_V2_HANDLER_BINDING_1 = "http://*" + MetricsV2Handler.V2_PATH; - public static final String METRICS_V2_HANDLER_BINDING_2 = METRICS_V2_HANDLER_BINDING_1 + "/*"; + public static final BindingPattern METRICS_V2_HANDLER_BINDING_1 = SystemBindingPattern.fromHttpPath(MetricsV2Handler.V2_PATH); + public static final BindingPattern METRICS_V2_HANDLER_BINDING_2 = SystemBindingPattern.fromHttpPath(MetricsV2Handler.V2_PATH + "/*"); public static final String PROMETHEUS_V1_HANDLER_CLASS = PrometheusV1Handler.class.getName(); - private static final String PROMETHEUS_V1_HANDLER_BINDING_1 = "http://*" + PrometheusV1Handler.V1_PATH; - private static final String PROMETHEUS_V1_HANDLER_BINDING_2 = PROMETHEUS_V1_HANDLER_BINDING_1 + "/*"; + private static final BindingPattern PROMETHEUS_V1_HANDLER_BINDING_1 = SystemBindingPattern.fromHttpPath(PrometheusV1Handler.V1_PATH); + private static final BindingPattern PROMETHEUS_V1_HANDLER_BINDING_2 = SystemBindingPattern.fromHttpPath(PrometheusV1Handler.V1_PATH + "/*"); public static final int heapSizePercentageOfTotalNodeMemory = 60; public static final int heapSizePercentageOfTotalNodeMemoryWhenCombinedCluster = 17; @@ -125,7 +127,7 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat addMetricsHandler(PROMETHEUS_V1_HANDLER_CLASS, PROMETHEUS_V1_HANDLER_BINDING_1, PROMETHEUS_V1_HANDLER_BINDING_2); } - private void addMetricsHandler(String handlerClass, String rootBinding, String innerBinding) { + private void addMetricsHandler(String handlerClass, BindingPattern rootBinding, BindingPattern innerBinding) { Handler<AbstractConfigProducer<?>> handler = new Handler<>( new ComponentModel(handlerClass, null, null, null)); handler.addServerBindings(rootBinding, innerBinding); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java index 240157fb7aa..8bb456ab7e7 100755 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java @@ -39,6 +39,7 @@ import com.yahoo.vespa.model.Service; import com.yahoo.vespa.model.admin.monitoring.Monitoring; import com.yahoo.vespa.model.clients.ContainerDocumentApi; import com.yahoo.vespa.model.container.component.AccessLogComponent; +import com.yahoo.vespa.model.container.component.BindingPattern; import com.yahoo.vespa.model.container.component.Component; import com.yahoo.vespa.model.container.component.ComponentGroup; import com.yahoo.vespa.model.container.component.ComponentsConfigGenerator; @@ -47,6 +48,7 @@ import com.yahoo.vespa.model.container.component.FileStatusHandlerComponent; import com.yahoo.vespa.model.container.component.Handler; import com.yahoo.vespa.model.container.component.SimpleComponent; import com.yahoo.vespa.model.container.component.StatisticsComponent; +import com.yahoo.vespa.model.container.component.SystemBindingPattern; import com.yahoo.vespa.model.container.component.chain.ProcessingHandler; import com.yahoo.vespa.model.container.docproc.ContainerDocproc; import com.yahoo.vespa.model.container.docproc.DocprocChains; @@ -107,7 +109,7 @@ public abstract class ContainerCluster<CONTAINER extends Container> * normal compatibility concerns only applies to libraries using the URIs in * question, not contents served from the URIs themselves. */ - public static final String RESERVED_URI_PREFIX = "reserved-for-internal-use"; + public static final String RESERVED_URI_PREFIX = "/reserved-for-internal-use"; public static final String APPLICATION_STATUS_HANDLER_CLASS = "com.yahoo.container.handler.observability.ApplicationStatusHandler"; public static final String BINDINGS_OVERVIEW_HANDLER_CLASS = BindingsOverviewHandler.class.getName(); @@ -117,13 +119,13 @@ public abstract class ContainerCluster<CONTAINER extends Container> public static final String G1GC = "-XX:+UseG1GC -XX:MaxTenuringThreshold=15"; public static final String STATE_HANDLER_CLASS = "com.yahoo.container.jdisc.state.StateHandler"; - public static final String STATE_HANDLER_BINDING_1 = "http://*" + StateHandler.STATE_API_ROOT; - public static final String STATE_HANDLER_BINDING_2 = STATE_HANDLER_BINDING_1 + "/*"; + public static final BindingPattern STATE_HANDLER_BINDING_1 = SystemBindingPattern.fromHttpPath(StateHandler.STATE_API_ROOT); + public static final BindingPattern STATE_HANDLER_BINDING_2 = SystemBindingPattern.fromHttpPath(StateHandler.STATE_API_ROOT + "/*"); public static final String ROOT_HANDLER_PATH = "/"; - public static final String ROOT_HANDLER_BINDING = "http://*" + ROOT_HANDLER_PATH; + public static final BindingPattern ROOT_HANDLER_BINDING = SystemBindingPattern.fromHttpPath(ROOT_HANDLER_PATH); - public static final String VIP_HANDLER_BINDING = "http://*/status.html"; + public static final BindingPattern VIP_HANDLER_BINDING = SystemBindingPattern.fromHttpPath("/status.html"); private final String name; @@ -234,7 +236,7 @@ public abstract class ContainerCluster<CONTAINER extends Container> Handler<AbstractConfigProducer<?>> statusHandler = new Handler<>( new ComponentModel(BundleInstantiationSpecification.getInternalHandlerSpecificationFromStrings( APPLICATION_STATUS_HANDLER_CLASS, null), null)); - statusHandler.addServerBindings("http://*/ApplicationStatus"); + statusHandler.addServerBindings(SystemBindingPattern.fromHttpPath("/ApplicationStatus")); addComponent(statusHandler); } @@ -309,7 +311,7 @@ public abstract class ContainerCluster<CONTAINER extends Container> containers.forEach(this::addContainer); } - public void setProcessingChains(ProcessingChains processingChains, String... serverBindings) { + public void setProcessingChains(ProcessingChains processingChains, BindingPattern... serverBindings) { if (this.processingChains != null) throw new IllegalStateException("ProcessingChains should only be set once."); @@ -320,7 +322,7 @@ public abstract class ContainerCluster<CONTAINER extends Container> processingChains, "com.yahoo.processing.handler.ProcessingHandler"); - for (String binding: serverBindings) + for (BindingPattern binding: serverBindings) processingHandler.addServerBindings(binding); addComponent(processingHandler); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModelEvaluation.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModelEvaluation.java index 6b4f8d486ec..72f1921e6a2 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModelEvaluation.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModelEvaluation.java @@ -7,6 +7,7 @@ import com.yahoo.searchdefinition.derived.RankProfileList; import com.yahoo.vespa.config.search.RankProfilesConfig; import com.yahoo.vespa.config.search.core.RankingConstantsConfig; import com.yahoo.vespa.model.container.component.Handler; +import com.yahoo.vespa.model.container.component.SystemBindingPattern; import java.util.List; import java.util.Objects; @@ -21,7 +22,7 @@ public class ContainerModelEvaluation implements RankProfilesConfig.Producer, Ra private final static String BUNDLE_NAME = "model-evaluation"; private final static String EVALUATOR_NAME = ModelsEvaluator.class.getName(); private final static String REST_HANDLER_NAME = "ai.vespa.models.handler.ModelsEvaluationHandler"; - private final static String REST_BINDING = "model-evaluation/v1"; + private final static String REST_BINDING_PATH = "/model-evaluation/v1"; /** Global rank profiles, aka models */ private final RankProfileList rankProfileList; @@ -48,8 +49,9 @@ public class ContainerModelEvaluation implements RankProfilesConfig.Producer, Ra public static Handler<?> getHandler() { Handler<?> handler = new Handler<>(new ComponentModel(REST_HANDLER_NAME, null, BUNDLE_NAME)); - handler.addServerBindings("http://*/" + REST_BINDING, - "http://*/" + REST_BINDING + "/*"); + handler.addServerBindings( + SystemBindingPattern.fromHttpPath(REST_BINDING_PATH), + SystemBindingPattern.fromHttpPath(REST_BINDING_PATH + "/*")); return handler; } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/BindingPattern.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/BindingPattern.java new file mode 100644 index 00000000000..c8b9d3293a7 --- /dev/null +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/BindingPattern.java @@ -0,0 +1,90 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.model.container.component; + +import java.util.Comparator; +import java.util.Objects; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * URI binding pattern used by filter and handler bindings. + * + * @author bjorncs + */ +public abstract class BindingPattern implements Comparable<BindingPattern> { + + private static final Pattern BINDING_PATTERN = + Pattern.compile("([^:]+)://([^:/]+)(:((\\*)|([0-9]+)))?(/.*)", Pattern.UNICODE_CASE | Pattern.CANON_EQ); + + public static final String WILDCARD_PATTERN = "*"; + + private final String scheme; + private final String host; + private final String port; + private final String path; + + protected BindingPattern( + String scheme, + String host, + String port, + String path) { + this.scheme = Objects.requireNonNull(scheme, "Scheme in binding must be specified"); + this.host = Objects.requireNonNull(host, "Host must be specified"); + this.port = port; + this.path = validatePath(path); + } + + protected BindingPattern(String binding) { + Matcher matcher = BINDING_PATTERN.matcher(binding); + if (!matcher.matches()) throw new IllegalArgumentException("Invalid binding: " + binding); + this.scheme = matcher.group(1); + this.host = matcher.group(2); + this.port = matcher.group(4); + this.path = matcher.group(7); + } + + private static String validatePath(String path) { + Objects.requireNonNull(path, "Path must be specified"); + if (!path.startsWith("/")) throw new IllegalArgumentException("Path must have '/' as prefix:" + path); + return path; + } + + public String scheme() { return scheme; } + public String host() { return host; } + public Optional<String> port() { return Optional.ofNullable(port); } + public String path() { return path; } + + public String patternString() { + StringBuilder builder = new StringBuilder(scheme).append("://").append(host); + if (port != null) { + builder.append(':').append(port); + } + return builder.append(path).toString(); + } + + /** Compares the underlying pattern string for equality */ + public boolean hasSamePattern(BindingPattern other) { return this.patternString().equals(other.patternString()); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BindingPattern that = (BindingPattern) o; + return Objects.equals(scheme, that.scheme) && + Objects.equals(host, that.host) && + Objects.equals(port, that.port) && + Objects.equals(path, that.path); + } + + @Override public int hashCode() { return Objects.hash(scheme, host, port, path); } + + @Override + public int compareTo(BindingPattern o) { + return Comparator.comparing(BindingPattern::scheme) + .thenComparing(BindingPattern::host) + .thenComparing(pattern -> pattern.port().orElse(null)) + .thenComparing(BindingPattern::path) + .compare(this, o); + } +} diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/DiscBindingsConfigGenerator.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/DiscBindingsConfigGenerator.java index d7e393ee474..02face328d9 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/DiscBindingsConfigGenerator.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/DiscBindingsConfigGenerator.java @@ -1,13 +1,16 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.model.container.component; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; import static com.yahoo.container.jdisc.JdiscBindingsConfig.Handlers; +import static java.util.stream.Collectors.toList; /** * @author gjoranv - * @since 5.1.8 */ public class DiscBindingsConfigGenerator { @@ -26,7 +29,11 @@ public class DiscBindingsConfigGenerator { return Collections.singletonMap(handler.model.getComponentId().stringValue(), new Handlers.Builder() - .serverBindings(handler.getServerBindings()) - .clientBindings(handler.getClientBindings())); + .serverBindings(toStrings(handler.getServerBindings())) + .clientBindings(toStrings(handler.getClientBindings()))); + } + + private static Collection<String> toStrings(Collection<BindingPattern> bindings) { + return bindings.stream().map(BindingPattern::patternString).collect(toList()); } } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/FileStatusHandlerComponent.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/FileStatusHandlerComponent.java index 3d9a1b2e665..839594502c6 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/FileStatusHandlerComponent.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/FileStatusHandlerComponent.java @@ -15,7 +15,7 @@ public class FileStatusHandlerComponent extends Handler implements VipStatusConf private final String fileName; - public FileStatusHandlerComponent(String id, String fileName, String... bindings) { + public FileStatusHandlerComponent(String id, String fileName, BindingPattern... bindings) { super(new ComponentModel(id, CLASS, null, null)); this.fileName = fileName; diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/Handler.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/Handler.java index 82484e07773..efee5c6a9a0 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/Handler.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/Handler.java @@ -1,9 +1,8 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.model.container.component; -import com.yahoo.container.bundle.BundleInstantiationSpecification; -import com.yahoo.osgi.provider.model.ComponentModel; import com.yahoo.config.model.producer.AbstractConfigProducer; +import com.yahoo.osgi.provider.model.ComponentModel; import java.util.ArrayList; import java.util.Arrays; @@ -23,8 +22,8 @@ import java.util.Set; */ public class Handler<CHILD extends AbstractConfigProducer<?>> extends Component<CHILD, ComponentModel> { - private Set<String> serverBindings = new LinkedHashSet<>(); - private List<String> clientBindings = new ArrayList<>(); + private final Set<BindingPattern> serverBindings = new LinkedHashSet<>(); + private final List<BindingPattern> clientBindings = new ArrayList<>(); public Handler(ComponentModel model) { super(model); @@ -34,27 +33,23 @@ public class Handler<CHILD extends AbstractConfigProducer<?>> extends Component< return new Handler<>(new ComponentModel(className, null, null, null)); } - public static Handler<AbstractConfigProducer<?>> getVespaHandlerFromClassName(String className) { - return new Handler<>(new ComponentModel(BundleInstantiationSpecification.getInternalHandlerSpecificationFromStrings(className, null), null)); - } - - public void addServerBindings(String... bindings) { + public void addServerBindings(BindingPattern... bindings) { serverBindings.addAll(Arrays.asList(bindings)); } - public void removeServerBinding(String binding) { + public void removeServerBinding(BindingPattern binding) { serverBindings.remove(binding); } - public void addClientBindings(String... bindings) { + public void addClientBindings(BindingPattern... bindings) { clientBindings.addAll(Arrays.asList(bindings)); } - public final Set<String> getServerBindings() { + public final Set<BindingPattern> getServerBindings() { return Collections.unmodifiableSet(serverBindings); } - public final List<String> getClientBindings() { + public final List<BindingPattern> getClientBindings() { return Collections.unmodifiableList(clientBindings); } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/SystemBindingPattern.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/SystemBindingPattern.java new file mode 100644 index 00000000000..559651b3b4f --- /dev/null +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/SystemBindingPattern.java @@ -0,0 +1,26 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.model.container.component; + +/** + * A {@link BindingPattern} which is implicitly constructed by the model, e.g for built-in handlers and filter chains. + * + * @author bjorncs + */ +public class SystemBindingPattern extends BindingPattern { + + private SystemBindingPattern(String scheme, String host, String port, String path) { super(scheme, host, port, path); } + private SystemBindingPattern(String binding) { super(binding); } + + public static SystemBindingPattern fromHttpPath(String path) { return new SystemBindingPattern("http", "*", null, path);} + public static SystemBindingPattern fromPattern(String binding) { return new SystemBindingPattern(binding);} + + @Override + public String toString() { + return "SystemBindingPattern{" + + "scheme='" + scheme() + '\'' + + ", host='" + host() + '\'' + + ", port='" + port().orElse(null) + '\'' + + ", path='" + path() + '\'' + + '}'; + } +} diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/UserBindingPattern.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/UserBindingPattern.java new file mode 100644 index 00000000000..43f57fa0343 --- /dev/null +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/UserBindingPattern.java @@ -0,0 +1,26 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.model.container.component; + +/** + * A {@link BindingPattern} which is constructed directly from a user provided 'binding' element from services.xml. + * + * @author bjorncs + */ +public class UserBindingPattern extends BindingPattern { + + private UserBindingPattern(String scheme, String host, String port, String path) { super(scheme, host, port, path); } + private UserBindingPattern(String binding) { super(binding); } + + public static UserBindingPattern fromHttpPath(String path) { return new UserBindingPattern("http", "*", null, path); } + public static UserBindingPattern fromPattern(String binding) { return new UserBindingPattern(binding); } + + @Override + public String toString() { + return "UserBindingPattern{" + + "scheme='" + scheme() + '\'' + + ", host='" + host() + '\'' + + ", port='" + port().orElse(null) + '\'' + + ", path='" + path() + '\'' + + '}'; + } +} diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/ContainerDocproc.java b/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/ContainerDocproc.java index d4b4dcea78e..82061a0425f 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/ContainerDocproc.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/ContainerDocproc.java @@ -9,6 +9,7 @@ import com.yahoo.container.jdisc.config.SessionConfig; import com.yahoo.docproc.jdisc.messagebus.MbusRequestContext; import com.yahoo.vespa.model.container.ContainerCluster; import com.yahoo.vespa.model.container.component.ContainerSubsystem; +import com.yahoo.vespa.model.container.component.SystemBindingPattern; import java.util.HashMap; import java.util.Map; @@ -44,7 +45,7 @@ public class ContainerDocproc extends ContainerSubsystem<DocprocChains> private void addSource( final ContainerCluster cluster, final String name, final SessionConfig.Type.Enum type) { final MbusClient mbusClient = new MbusClient(name, type); - mbusClient.addClientBindings("mbus://*/" + mbusClient.getSessionName()); + mbusClient.addClientBindings(SystemBindingPattern.fromPattern("mbus://*/" + mbusClient.getSessionName())); cluster.addComponent(mbusClient); } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocprocChains.java b/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocprocChains.java index 5d08a0a6998..68dc2518c23 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocprocChains.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocprocChains.java @@ -7,6 +7,7 @@ import com.yahoo.container.jdisc.config.SessionConfig; import com.yahoo.vespa.model.container.ApplicationContainerCluster; import com.yahoo.vespa.model.container.ContainerCluster; import com.yahoo.vespa.model.container.component.Component; +import com.yahoo.vespa.model.container.component.SystemBindingPattern; import com.yahoo.vespa.model.container.component.chain.Chains; import com.yahoo.vespa.model.container.component.chain.ProcessingHandler; @@ -38,12 +39,12 @@ public class DocprocChains extends Chains<DocprocChain> { } private void addServerAndClientForChain(ApplicationContainerCluster cluster, DocprocChain docprocChain) { - docprocHandler.addServerBindings("mbus://*/" + docprocChain.getSessionName()); + docprocHandler.addServerBindings(SystemBindingPattern.fromPattern("mbus://*/" + docprocChain.getSessionName())); cluster.addMbusServer(ComponentId.fromString(docprocChain.getSessionName())); MbusClient client = new MbusClient(docprocChain.getSessionName(), SessionConfig.Type.INTERMEDIATE); - client.addClientBindings("mbus://*/" + client.getSessionName()); + client.addClientBindings(SystemBindingPattern.fromPattern("mbus://*/" + client.getSessionName())); addComponent(client); } } 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 9676b8b1e4a..87c6d41c80d 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 @@ -3,12 +3,13 @@ package com.yahoo.vespa.model.container.http; import com.yahoo.component.ComponentId; import com.yahoo.component.ComponentSpecification; -import com.yahoo.config.application.api.DeployLogger; import com.yahoo.vespa.model.container.ApplicationContainerCluster; import com.yahoo.vespa.model.container.ContainerCluster; +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.Servlet; +import com.yahoo.vespa.model.container.component.SystemBindingPattern; import java.util.ArrayList; import java.util.Collection; @@ -43,14 +44,12 @@ public final class AccessControl { private String domain; private boolean readEnabled = false; private boolean writeEnabled = true; - private final Set<String> excludeBindings = new LinkedHashSet<>(); + private final Set<BindingPattern> excludeBindings = new LinkedHashSet<>(); private Collection<Handler<?>> handlers = Collections.emptyList(); private Collection<Servlet> servlets = Collections.emptyList(); - private final DeployLogger logger; - public Builder(String domain, DeployLogger logger) { + public Builder(String domain) { this.domain = domain; - this.logger = logger; } public Builder readEnabled(boolean readEnabled) { @@ -63,7 +62,7 @@ public final class AccessControl { return this; } - public Builder excludeBinding(String binding) { + public Builder excludeBinding(BindingPattern binding) { this.excludeBindings.add(binding); return this; } @@ -76,35 +75,32 @@ public final class AccessControl { public AccessControl build() { return new AccessControl(domain, writeEnabled, readEnabled, - excludeBindings, servlets, handlers, logger); + excludeBindings, servlets, handlers); } } public final String domain; public final boolean readEnabled; public final boolean writeEnabled; - private final Set<String> excludedBindings; + private final Set<BindingPattern> excludedBindings; private final Collection<Handler<?>> handlers; private final Collection<Servlet> servlets; - private final DeployLogger logger; private AccessControl(String domain, boolean writeEnabled, boolean readEnabled, - Set<String> excludedBindings, + Set<BindingPattern> excludedBindings, Collection<Servlet> servlets, - Collection<Handler<?>> handlers, - DeployLogger logger) { + Collection<Handler<?>> handlers) { this.domain = domain; this.readEnabled = readEnabled; this.writeEnabled = writeEnabled; this.excludedBindings = Collections.unmodifiableSet(excludedBindings); this.handlers = handlers; this.servlets = servlets; - this.logger = logger; } - public List<Binding> getBindings() { + public List<FilterBinding> getBindings() { return Stream.concat(getHandlerBindings(), getServletBindings()) .collect(Collectors.toCollection(ArrayList::new)); } @@ -113,18 +109,18 @@ public final class AccessControl { return cluster.getHandlers().stream().anyMatch(AccessControl::handlerNeedsProtection); } - private Stream<Binding> getHandlerBindings() { + private Stream<FilterBinding> getHandlerBindings() { return handlers.stream() .filter(this::shouldHandlerBeProtected) .flatMap(handler -> handler.getServerBindings().stream()) - .map(binding -> accessControlBinding(binding, logger)); + .map(binding -> accessControlBinding(binding)); } - private Stream<Binding> getServletBindings() { + private Stream<FilterBinding> getServletBindings() { return servlets.stream() .filter(this::shouldServletBeProtected) .flatMap(AccessControl::servletBindings) - .map(binding -> accessControlBinding(binding, logger)); + .map(binding -> accessControlBinding(binding)); } private boolean shouldHandlerBeProtected(Handler<?> handler) { @@ -140,12 +136,12 @@ public final class AccessControl { return servletBindings(servlet).noneMatch(excludedBindings::contains); } - private static Binding accessControlBinding(String binding, DeployLogger logger) { - return Binding.create(new ComponentSpecification(ACCESS_CONTROL_CHAIN_ID.stringValue()), binding, logger); + private static FilterBinding accessControlBinding(BindingPattern binding) { + return FilterBinding.create(new ComponentSpecification(ACCESS_CONTROL_CHAIN_ID.stringValue()), binding); } - private static Stream<String> servletBindings(Servlet servlet) { - return Stream.of("http://*/").map(protocol -> protocol + servlet.bindingPath); + private static Stream<BindingPattern> servletBindings(Servlet servlet) { + return Stream.of(SystemBindingPattern.fromHttpPath("/" + servlet.bindingPath)); } private static boolean handlerNeedsProtection(Handler<?> handler) { @@ -153,7 +149,7 @@ public final class AccessControl { } private static boolean hasNonMbusBinding(Handler<?> handler) { - return handler.getServerBindings().stream().anyMatch(binding -> ! binding.startsWith("mbus")); + return handler.getServerBindings().stream().anyMatch(binding -> ! binding.scheme().equals("mbus")); } } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/Binding.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/Binding.java deleted file mode 100644 index 28f4949f210..00000000000 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/Binding.java +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2019 Oath Inc. 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.ComponentSpecification; -import com.yahoo.config.application.api.DeployLogger; - -import java.util.logging.Level; - -/** - * @author bjorncs - */ -public class Binding { - - private final ComponentSpecification filterId; - private final String binding; - - private Binding(ComponentSpecification filterId, String binding) { - this.filterId = filterId; - this.binding = binding; - } - - public static Binding create(ComponentSpecification filterId, String binding, DeployLogger logger) { - if (binding.startsWith("https://")) { - logger.log(Level.WARNING, String.format("For binding '%s' on '%s': 'https' bindings are deprecated, " + - "use 'http' instead to bind to both http and https traffic.", - binding, filterId)); - } - return new Binding(filterId, binding); - } - - public ComponentSpecification filterId() { - return filterId; - } - - public String binding() { - return binding; - } - -} diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/FilterBinding.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/FilterBinding.java new file mode 100644 index 00000000000..8ae06b7cebd --- /dev/null +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/FilterBinding.java @@ -0,0 +1,32 @@ +// Copyright 2019 Oath Inc. 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.ComponentSpecification; +import com.yahoo.vespa.model.container.component.BindingPattern; + +/** + * @author bjorncs + */ +public class FilterBinding { + + private final ComponentSpecification filterId; + private final BindingPattern binding; + + private FilterBinding(ComponentSpecification filterId, BindingPattern binding) { + this.filterId = filterId; + this.binding = binding; + } + + public static FilterBinding create(ComponentSpecification filterId, BindingPattern binding) { + return new FilterBinding(filterId, binding); + } + + public ComponentSpecification filterId() { + return filterId; + } + + public BindingPattern binding() { + return binding; + } + +} 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 0fcf7b2d06c..3155669527a 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 @@ -21,7 +21,7 @@ import java.util.concurrent.CopyOnWriteArrayList; public class Http extends AbstractConfigProducer<AbstractConfigProducer<?>> implements ServerConfig.Producer { private final FilterChains filterChains; - private final List<Binding> bindings = new CopyOnWriteArrayList<>(); + private final List<FilterBinding> bindings = new CopyOnWriteArrayList<>(); private volatile JettyHttpServer httpServer; private volatile AccessControl accessControl; @@ -64,7 +64,7 @@ public class Http extends AbstractConfigProducer<AbstractConfigProducer<?>> impl setHttpServer(null); } - public List<Binding> getBindings() { + public List<FilterBinding> getBindings() { return bindings; } @@ -74,16 +74,16 @@ public class Http extends AbstractConfigProducer<AbstractConfigProducer<?>> impl @Override public void getConfig(ServerConfig.Builder builder) { - for (Binding binding : bindings) { + for (FilterBinding binding : bindings) { builder.filter(new ServerConfig.Filter.Builder() .id(binding.filterId().stringValue()) - .binding(binding.binding())); + .binding(binding.binding().patternString())); } } @Override public void validate() { - if (((Collection<Binding>) bindings).isEmpty()) return; + if (((Collection<FilterBinding>) bindings).isEmpty()) return; if (filterChains == null) throw new IllegalArgumentException("Null FilterChains are not allowed when there are filter bindings"); @@ -91,7 +91,7 @@ public class Http extends AbstractConfigProducer<AbstractConfigProducer<?>> impl ComponentRegistry<ChainedComponent<?>> filters = filterChains.componentsRegistry(); ComponentRegistry<Chain<Filter>> chains = filterChains.allChains(); - for (Binding binding: bindings) { + for (FilterBinding binding: bindings) { if (filters.getComponent(binding.filterId()) == null && chains.getComponent(binding.filterId()) == null) throw new RuntimeException("Can't find filter " + binding.filterId() + " for binding " + binding.binding()); } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java index bfde9b9add1..9d5fead7dfb 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java @@ -13,9 +13,10 @@ import com.yahoo.vespa.model.builder.xml.dom.ModelElement; import com.yahoo.vespa.model.builder.xml.dom.VespaDomBuilder; import com.yahoo.vespa.model.container.ApplicationContainerCluster; import com.yahoo.vespa.model.container.Container; +import com.yahoo.vespa.model.container.component.UserBindingPattern; import com.yahoo.vespa.model.container.component.chain.Chain; import com.yahoo.vespa.model.container.http.AccessControl; -import com.yahoo.vespa.model.container.http.Binding; +import com.yahoo.vespa.model.container.http.FilterBinding; import com.yahoo.vespa.model.container.http.FilterChains; import com.yahoo.vespa.model.container.http.Http; import org.w3c.dom.Element; @@ -36,13 +37,13 @@ public class HttpBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Http> @Override protected Http doBuild(DeployState deployState, AbstractConfigProducer ancestor, Element spec) { FilterChains filterChains; - List<Binding> bindings = new ArrayList<>(); + List<FilterBinding> bindings = new ArrayList<>(); AccessControl accessControl = null; Element filteringElem = XML.getChild(spec, "filtering"); if (filteringElem != null) { filterChains = new FilterChainsBuilder().build(deployState, ancestor, filteringElem); - bindings = readFilterBindings(filteringElem, deployState.getDeployLogger()); + bindings = readFilterBindings(filteringElem); Element accessControlElem = XML.getChild(filteringElem, "access-control"); if (accessControlElem != null) { @@ -63,7 +64,7 @@ public class HttpBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Http> private AccessControl buildAccessControl(DeployState deployState, AbstractConfigProducer ancestor, Element accessControlElem) { AthenzDomain domain = getAccessControlDomain(deployState, accessControlElem); - AccessControl.Builder builder = new AccessControl.Builder(domain.value(), deployState.getDeployLogger()); + AccessControl.Builder builder = new AccessControl.Builder(domain.value()); getContainerCluster(ancestor).ifPresent(builder::setHandlers); @@ -75,7 +76,7 @@ public class HttpBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Http> Element excludeElem = XML.getChild(accessControlElem, "exclude"); if (excludeElem != null) { XML.getChildren(excludeElem, "binding").stream() - .map(XML::getValue) + .map(xml -> UserBindingPattern.fromPattern(XML.getValue(xml))) .forEach(builder::excludeBinding); } return builder.build(); @@ -113,8 +114,8 @@ public class HttpBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Http> return Optional.of((ApplicationContainerCluster) currentProducer); } - private List<Binding> readFilterBindings(Element filteringSpec, DeployLogger logger) { - List<Binding> result = new ArrayList<>(); + private List<FilterBinding> readFilterBindings(Element filteringSpec) { + List<FilterBinding> result = new ArrayList<>(); for (Element child: XML.getChildren(filteringSpec)) { String tagName = child.getTagName(); @@ -123,7 +124,7 @@ public class HttpBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Http> for (Element bindingSpec: XML.getChildren(child, "binding")) { String binding = XML.getValue(bindingSpec); - result.add(Binding.create(chainId, binding, logger)); + result.add(FilterBinding.create(chainId, UserBindingPattern.fromPattern(binding))); } } } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/processing/ProcessingChains.java b/config-model/src/main/java/com/yahoo/vespa/model/container/processing/ProcessingChains.java index 4fd79a4f335..f6b24bf9635 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/processing/ProcessingChains.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/processing/ProcessingChains.java @@ -2,6 +2,8 @@ package com.yahoo.vespa.model.container.processing; import com.yahoo.config.model.producer.AbstractConfigProducer; +import com.yahoo.vespa.model.container.component.BindingPattern; +import com.yahoo.vespa.model.container.component.SystemBindingPattern; import com.yahoo.vespa.model.container.component.chain.Chains; /** @@ -11,7 +13,7 @@ import com.yahoo.vespa.model.container.component.chain.Chains; */ public class ProcessingChains extends Chains<ProcessingChain> { - public static final String[] defaultBindings = new String[] {"http://*/processing/*"}; + public static final BindingPattern[] defaultBindings = new BindingPattern[]{SystemBindingPattern.fromHttpPath("/processing/*")}; public ProcessingChains(AbstractConfigProducer parent, String subId) { diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/GUIHandler.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/GUIHandler.java index 1e717f89819..f01bbcd3951 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/GUIHandler.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/GUIHandler.java @@ -14,7 +14,7 @@ public class GUIHandler extends Handler<AbstractConfigProducer<?>> { public static final String BUNDLE = "container-search-gui"; public static final String CLASS = "com.yahoo.search.query.gui.GUIHandler"; - public static final String BINDING = "*/querybuilder/*"; + public static final String BINDING_PATH = "/querybuilder/*"; public GUIHandler() { super(new ComponentModel(bundleSpec(CLASS, BUNDLE))); 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 41e092c7ea5..d11e0f9a891 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 @@ -29,7 +29,6 @@ import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.HostName; import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.NodeType; -import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.Zone; import com.yahoo.search.rendering.RendererRegistry; import com.yahoo.searchdefinition.derived.RankProfileList; @@ -57,9 +56,11 @@ import com.yahoo.vespa.model.container.ContainerModel; import com.yahoo.vespa.model.container.ContainerModelEvaluation; import com.yahoo.vespa.model.container.IdentityProvider; import com.yahoo.vespa.model.container.SecretStore; -import com.yahoo.vespa.model.container.component.Component; +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.UserBindingPattern; import com.yahoo.vespa.model.container.component.chain.Chain; import com.yahoo.vespa.model.container.component.chain.ProcessingHandler; import com.yahoo.vespa.model.container.docproc.ContainerDocproc; @@ -113,7 +114,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { private static final String ENVIRONMENT_VARIABLES_ELEMENT = "environment-variables"; static final String SEARCH_HANDLER_CLASS = com.yahoo.search.handler.SearchHandler.class.getName(); - static final String SEARCH_HANDLER_BINDING = "http://*/search/*"; + static final BindingPattern SEARCH_HANDLER_BINDING = SystemBindingPattern.fromHttpPath("/search/*"); public enum Networking { disable, enable } @@ -278,8 +279,10 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { String name = "status.html"; Optional<String> statusFile = Optional.ofNullable(System.getenv(HOSTED_VESPA_STATUS_FILE_SETTING)); cluster.addComponent( - new FileStatusHandlerComponent(name + "-status-handler", statusFile.orElse(HOSTED_VESPA_STATUS_FILE), - "http://*/" + name)); + new FileStatusHandlerComponent( + name + "-status-handler", + statusFile.orElse(HOSTED_VESPA_STATUS_FILE), + SystemBindingPattern.fromHttpPath("/" + name))); } else { cluster.addVipHandler(); } @@ -369,7 +372,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { AthenzDomain tenantDomain = deployState.getProperties().athenzDomain().orElse(null); if (tenantDomain == null) return; // tenant domain not present, cannot add access control. this should eventually be a failure. AccessControl accessControl = - new AccessControl.Builder(tenantDomain.value(), deployState.getDeployLogger()) + new AccessControl.Builder(tenantDomain.value()) .setHandlers(cluster) .readEnabled(false) .writeEnabled(false) @@ -795,8 +798,8 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { ProcessingHandler<SearchChains> searchHandler = new ProcessingHandler<>(cluster.getSearch().getChains(), "com.yahoo.search.handler.SearchHandler"); - String[] defaultBindings = {SEARCH_HANDLER_BINDING}; - for (String binding: serverBindings(searchElement, defaultBindings)) { + BindingPattern[] defaultBindings = {SEARCH_HANDLER_BINDING}; + for (BindingPattern binding: serverBindings(searchElement, defaultBindings)) { searchHandler.addServerBindings(binding); } @@ -805,12 +808,12 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { private void addGUIHandler(ApplicationContainerCluster cluster) { Handler<?> guiHandler = new GUIHandler(); - guiHandler.addServerBindings("http://"+GUIHandler.BINDING); + guiHandler.addServerBindings(SystemBindingPattern.fromHttpPath(GUIHandler.BINDING_PATH)); cluster.addComponent(guiHandler); } - private String[] serverBindings(Element searchElement, String... defaultBindings) { + private BindingPattern[] serverBindings(Element searchElement, BindingPattern... defaultBindings) { List<Element> bindings = XML.getChildren(searchElement, "binding"); if (bindings.isEmpty()) return defaultBindings; @@ -818,16 +821,16 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { return toBindingList(bindings); } - private String[] toBindingList(List<Element> bindingElements) { - List<String> result = new ArrayList<>(); + private BindingPattern[] toBindingList(List<Element> bindingElements) { + List<BindingPattern> result = new ArrayList<>(); for (Element element: bindingElements) { String text = element.getTextContent().trim(); if (!text.isEmpty()) - result.add(text); + result.add(UserBindingPattern.fromPattern(text)); } - return result.toArray(new String[result.size()]); + return result.toArray(BindingPattern[]::new); } private ContainerDocumentApi buildDocumentApi(ApplicationContainerCluster cluster, Element spec) { diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/DocumentApiOptionsBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/DocumentApiOptionsBuilder.java index ae74dbdb4a7..61464799812 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/DocumentApiOptionsBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/DocumentApiOptionsBuilder.java @@ -6,19 +6,17 @@ import com.yahoo.vespa.model.clients.ContainerDocumentApi; import org.w3c.dom.Element; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.logging.Logger; /** * @author Einar M R Rosenvinge - * @since 5.1.11 */ public class DocumentApiOptionsBuilder { private static final Logger log = Logger.getLogger(DocumentApiOptionsBuilder.class.getName()); - private static final String[] DEFAULT_BINDINGS = {"http://*/"}; + public static ContainerDocumentApi.Options build(Element spec) { return new ContainerDocumentApi.Options(getBindings(spec)); @@ -27,8 +25,7 @@ public class DocumentApiOptionsBuilder { private static List<String> getBindings(Element spec) { Collection<Element> bindingElems = XML.getChildren(spec, "binding"); if (bindingElems.isEmpty()) - return Arrays.asList(DEFAULT_BINDINGS); - + return List.of(); List<String> bindings = new ArrayList<>(); for (Element e :bindingElems) { String binding = getBinding(e); diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/UriBindingsValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/UriBindingsValidatorTest.java new file mode 100644 index 00000000000..cce88bc02f9 --- /dev/null +++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/UriBindingsValidatorTest.java @@ -0,0 +1,104 @@ +package com.yahoo.vespa.model.application.validation;// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +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.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.xml.sax.SAXException; + +import java.io.IOException; + +/** + * @author bjorncs + */ +public class UriBindingsValidatorTest { + + @Rule + public ExpectedException exceptionRule = ExpectedException.none(); + + @Test + public void fails_on_user_handler_binding_with_port() throws IOException, SAXException { + exceptionRule.expect(IllegalArgumentException.class); + exceptionRule.expectMessage("For binding 'http://*:4443/my-handler': binding with port is not allowed"); + runUriBindingValidator(true, createServicesXmlWithHandler("http://*:4443/my-handler")); + } + + @Test + public void fails_on_user_handler_binding_with_hostname() throws IOException, SAXException { + exceptionRule.expect(IllegalArgumentException.class); + exceptionRule.expectMessage("For binding 'http://myhostname/my-handler': only binding with wildcard ('*') for hostname is allowed"); + runUriBindingValidator(true, createServicesXmlWithHandler("http://myhostname/my-handler")); + } + + @Test + public void fails_on_user_handler_binding_with_non_http_scheme() throws IOException, SAXException { + exceptionRule.expect(IllegalArgumentException.class); + exceptionRule.expectMessage("For binding 'ftp://*/my-handler': only 'http' is allowed as scheme"); + runUriBindingValidator(true, createServicesXmlWithHandler("ftp://*/my-handler")); + } + + @Test + public void fails_on_invalid_filter_binding() throws IOException, SAXException { + exceptionRule.expect(IllegalArgumentException.class); + exceptionRule.expectMessage("For binding 'https://*:4443/my-request-filer-chain': binding with port is not allowed"); + runUriBindingValidator(true, createServicesXmlWithRequestFilterChain("https://*:4443/my-request-filer-chain")); + } + + @Test + public void allows_valid_user_binding() throws IOException, SAXException { + runUriBindingValidator(true, createServicesXmlWithHandler("http://*/my-handler")); + } + + @Test + public void only_restricts_user_bindings_on_hosted() throws IOException, SAXException { + runUriBindingValidator(false, createServicesXmlWithRequestFilterChain("https://*:4443/my-request-filer-chain")); + } + + private void runUriBindingValidator(boolean isHosted, String servicesXml) throws IOException, SAXException { + 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 UriBindingsValidator().validate(model, deployState); + } + + private static String createServicesXmlWithHandler(String handlerBinding) { + return String.join( + "\n", + "<services version='1.0'>", + " <container id='default' version='1.0'>", + " <handler id='custom.Handler'>", + " <binding>" + handlerBinding + "</binding>", + " </handler>", + " </container>", + "</services>"); + } + + private static String createServicesXmlWithRequestFilterChain(String filterBinding) { + return String.join( + "\n", + "<services version='1.0'>", + " <container version='1.0'>", + " <http>", + " <server port='8080' id='main' />", + " <filtering>", + " <request-chain id='myChain'>", + " <filter id='myFilter'/>", + " <binding>" + filterBinding + "</binding>", + " </request-chain>", + " </filtering>", + " </http>", + " </container>", + "</services>"); + } + +}
\ No newline at end of file diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/component/BindingPatternTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/component/BindingPatternTest.java new file mode 100644 index 00000000000..91a2b65c0e0 --- /dev/null +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/component/BindingPatternTest.java @@ -0,0 +1,53 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.model.container.component; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +/** + * @author bjorncs + */ +public class BindingPatternTest { + + @Test + public void parses_valid_bindings_correctly() { + assertBindingParses("http://host:1234/path"); + assertBindingParses("http://host/path"); + assertBindingParses("http://host/"); + assertBindingParses("*://*:*/*"); + assertBindingParses("http://*/*"); + assertBindingParses("https://*/my/path"); + assertBindingParses("https://*/path/*"); + assertBindingParses("https://host:*/path/*"); + assertBindingParses("https://host:1234/*"); + } + + @Test + public void getters_returns_correct_components() { + { + BindingPattern pattern = SystemBindingPattern.fromPattern("http://host:1234/path/*"); + assertEquals("http", pattern.scheme()); + assertEquals("host", pattern.host()); + assertEquals("1234", pattern.port().get()); + assertEquals("/path/*", pattern.path()); + } + { + BindingPattern pattern = SystemBindingPattern.fromPattern("https://*/path/v1/"); + assertEquals("https", pattern.scheme()); + assertEquals("*", pattern.host()); + assertFalse(pattern.port().isPresent()); + assertEquals("/path/v1/", pattern.path()); + } + } + + private static void assertBindingParses(String binding) { + BindingPattern pattern = SystemBindingPattern.fromPattern(binding); + String stringRepresentation = pattern.patternString(); + assertEquals( + "Expected string representation of parsed binding to match original binding string", + binding, stringRepresentation); + } + +}
\ No newline at end of file diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/http/FilterBindingsTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/http/FilterBindingsTest.java index 0f9de516a4b..4ca7590673d 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/http/FilterBindingsTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/http/FilterBindingsTest.java @@ -1,10 +1,12 @@ // Copyright 2017 Yahoo Holdings. 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.config.model.deploy.DeployState; import com.yahoo.config.model.builder.xml.test.DomBuilderTest; +import com.yahoo.config.model.deploy.DeployState; import com.yahoo.jdisc.http.ServerConfig; import com.yahoo.vespa.model.container.ContainerModel; +import com.yahoo.vespa.model.container.component.BindingPattern; +import com.yahoo.vespa.model.container.component.UserBindingPattern; import com.yahoo.vespa.model.container.component.chain.Chain; import com.yahoo.vespa.model.container.http.xml.HttpBuilder; import com.yahoo.vespa.model.container.xml.ContainerModelBuilder; @@ -21,7 +23,7 @@ import static org.junit.Assert.assertNotNull; */ public class FilterBindingsTest extends DomBuilderTest { - private static final String MY_CHAIN_BINDING = "http://*/my-chain-binding"; + private static final BindingPattern MY_CHAIN_BINDING = UserBindingPattern.fromHttpPath("/my-chain-binding"); private Http buildHttp(Element xml) { Http http = new HttpBuilder().build(root.getDeployState(), root, xml); @@ -42,13 +44,13 @@ public class FilterBindingsTest extends DomBuilderTest { "<http>", " <filtering>", " <request-chain id='my-request-chain'>", - " <binding>" + MY_CHAIN_BINDING + "</binding>", + " <binding>" + MY_CHAIN_BINDING.patternString() + "</binding>", " </request-chain>", " </filtering>", "</http>"); Http http = buildHttp(xml); - Binding binding = first(http.getBindings()); + FilterBinding binding = first(http.getBindings()); assertEquals("my-request-chain", binding.filterId().getName()); assertEquals(MY_CHAIN_BINDING, binding.binding()); @@ -62,13 +64,13 @@ public class FilterBindingsTest extends DomBuilderTest { "<http>", " <filtering>", " <response-chain id='my-response-chain'>", - " <binding>" + MY_CHAIN_BINDING + "</binding>", + " <binding>" + MY_CHAIN_BINDING.patternString() + "</binding>", " </response-chain>", " </filtering>", "</http>"); Http http = buildHttp(xml); - Binding binding = first(http.getBindings()); + FilterBinding binding = first(http.getBindings()); assertEquals("my-response-chain", binding.filterId().getName()); assertEquals(MY_CHAIN_BINDING, binding.binding()); @@ -83,7 +85,7 @@ public class FilterBindingsTest extends DomBuilderTest { " <http>", " <filtering>", " <request-chain id='my-request-chain'>", - " <binding>" + MY_CHAIN_BINDING + "</binding>", + " <binding>" + MY_CHAIN_BINDING.patternString() + "</binding>", " </request-chain>", " </filtering>", " <server id='server1' port='8000' />", @@ -96,13 +98,13 @@ public class FilterBindingsTest extends DomBuilderTest { final ServerConfig config = root.getConfig(ServerConfig.class, "container/http/jdisc-jetty/server1"); assertEquals(1, config.filter().size()); assertEquals("my-request-chain", config.filter(0).id()); - assertEquals(MY_CHAIN_BINDING, config.filter(0).binding()); + assertEquals(MY_CHAIN_BINDING.patternString(), config.filter(0).binding()); } { final ServerConfig config = root.getConfig(ServerConfig.class, "container/http/jdisc-jetty/server2"); assertEquals(1, config.filter().size()); assertEquals("my-request-chain", config.filter(0).id()); - assertEquals(MY_CHAIN_BINDING, config.filter(0).binding()); + assertEquals(MY_CHAIN_BINDING.patternString(), config.filter(0).binding()); } } diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java index 28e23ce3222..e054698d40b 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java @@ -1,19 +1,19 @@ // Copyright 2017 Yahoo Holdings. 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.google.common.collect.ImmutableSet; -import com.yahoo.collections.CollectionUtil; import com.yahoo.component.ComponentId; import com.yahoo.config.model.builder.xml.test.DomBuilderTest; import com.yahoo.config.model.deploy.DeployState; import com.yahoo.config.model.deploy.TestProperties; import com.yahoo.config.provision.AthenzDomain; -import com.yahoo.container.jdisc.state.StateHandler; import com.yahoo.vespa.model.container.ApplicationContainer; import com.yahoo.vespa.model.container.ContainerCluster; +import com.yahoo.vespa.model.container.component.BindingPattern; +import com.yahoo.vespa.model.container.component.SystemBindingPattern; +import com.yahoo.vespa.model.container.component.UserBindingPattern; import com.yahoo.vespa.model.container.http.AccessControl; +import com.yahoo.vespa.model.container.http.FilterBinding; import com.yahoo.vespa.model.container.http.Http; -import com.yahoo.vespa.model.container.http.Binding; import com.yahoo.vespa.model.container.http.xml.HttpBuilder; import com.yahoo.vespa.model.container.jersey.Jersey2Servlet; import org.junit.Test; @@ -41,18 +41,20 @@ import static org.junit.Assert.assertTrue; */ public class AccessControlTest extends ContainerModelBuilderTestBase { - private static final Set<String> REQUIRED_HANDLER_BINDINGS = ImmutableSet.of( - "/custom-handler/", - "/search/", - "/document/", - ContainerCluster.RESERVED_URI_PREFIX); - - private static final Set<String> FORBIDDEN_HANDLER_BINDINGS = ImmutableSet.of( - "/ApplicationStatus", - "/status.html", - "/statistics/", - StateHandler.STATE_API_ROOT, - ContainerCluster.ROOT_HANDLER_PATH); + private static final Set<BindingPattern> REQUIRED_HANDLER_BINDINGS = + Set.of( + UserBindingPattern.fromHttpPath("/custom-handler/*"), + SystemBindingPattern.fromHttpPath("/search/*"), + SystemBindingPattern.fromHttpPath("/document/v1/*"), + SystemBindingPattern.fromHttpPath("/reserved-for-internal-use/feedapi")); + + private static final Set<BindingPattern> FORBIDDEN_HANDLER_BINDINGS = + Set.of( + SystemBindingPattern.fromHttpPath("/ApplicationStatus"), + SystemBindingPattern.fromHttpPath("/status.html"), + SystemBindingPattern.fromHttpPath("/statistics/"), + SystemBindingPattern.fromHttpPath("/state/v1"), + SystemBindingPattern.fromHttpPath("/")); @Test public void access_control_filter_chain_is_set_up() { @@ -135,16 +137,15 @@ public class AccessControlTest extends ContainerModelBuilderTestBase { Http http = getHttp(clusterElem); - Set<String> foundRequiredBindings = REQUIRED_HANDLER_BINDINGS.stream() + Set<BindingPattern> foundRequiredBindings = REQUIRED_HANDLER_BINDINGS.stream() .filter(requiredBinding -> containsBinding(http.getBindings(), requiredBinding)) .collect(Collectors.toSet()); - Set<String> missingRequiredBindings = new HashSet<>(REQUIRED_HANDLER_BINDINGS); + Set<BindingPattern> missingRequiredBindings = new HashSet<>(REQUIRED_HANDLER_BINDINGS); missingRequiredBindings.removeAll(foundRequiredBindings); - assertTrue("Access control chain was not bound to: " + CollectionUtil.mkString(missingRequiredBindings, ", "), + assertTrue("Access control chain was not bound to: " + prettyString(missingRequiredBindings), missingRequiredBindings.isEmpty()); - FORBIDDEN_HANDLER_BINDINGS.forEach(forbiddenPath -> { - String forbiddenBinding = String.format("http://*%s", forbiddenPath); + FORBIDDEN_HANDLER_BINDINGS.forEach(forbiddenBinding -> { http.getBindings().forEach( binding -> assertNotEquals("Access control chain was bound to: " + binding.binding(), binding.binding(), forbiddenBinding)); }); @@ -152,14 +153,14 @@ public class AccessControlTest extends ContainerModelBuilderTestBase { @Test public void handler_can_be_excluded_by_excluding_one_of_its_bindings() { - final String notExcludedBinding = "http://*/custom-handler/*"; - final String excludedBinding = "http://*/excluded/*"; + BindingPattern notExcludedBinding = UserBindingPattern.fromHttpPath("/custom-handler/*"); + BindingPattern excludedBinding = SystemBindingPattern.fromHttpPath("/excluded/*"); Element clusterElem = DomBuilderTest.parse( "<container version='1.0'>", httpWithExcludedBinding(excludedBinding), " <handler id='custom.Handler'>", - " <binding>" + notExcludedBinding + "</binding>", - " <binding>" + excludedBinding + "</binding>", + " <binding>" + notExcludedBinding.patternString() + "</binding>", + " <binding>" + excludedBinding.patternString() + "</binding>", " </handler>", "</container>"); @@ -175,7 +176,6 @@ public class AccessControlTest extends ContainerModelBuilderTestBase { public void access_control_filter_chain_has_all_servlet_bindings() { final String servletPath = "servlet/path"; final String restApiPath = "api/v0"; - final Set<String> requiredBindings = ImmutableSet.of(servletPath, restApiPath); Element clusterElem = DomBuilderTest.parse( "<container version='1.0'>", " <servlet id='foo' class='bar' bundle='baz'>", @@ -191,19 +191,22 @@ public class AccessControlTest extends ContainerModelBuilderTestBase { Http http = getHttp(clusterElem); - Set<String> missingRequiredBindings = requiredBindings.stream() + Set<BindingPattern> requiredBindings = Set.of( + SystemBindingPattern.fromHttpPath("/" + servletPath), + SystemBindingPattern.fromHttpPath("/" + restApiPath + "/*")); + Set<BindingPattern> missingRequiredBindings = requiredBindings.stream() .filter(requiredBinding -> ! containsBinding(http.getBindings(), requiredBinding)) .collect(Collectors.toSet()); - assertTrue("Access control chain was not bound to: " + CollectionUtil.mkString(missingRequiredBindings, ", "), + assertTrue("Access control chain was not bound to: " + prettyString(missingRequiredBindings), missingRequiredBindings.isEmpty()); } @Test public void servlet_can_be_excluded_by_excluding_one_of_its_bindings() { - final String servletPath = "servlet/path"; - final String notExcludedBinding = "http://*:8081/" + servletPath; - final String excludedBinding = "http://*:8080/" + servletPath; + String servletPath = "servlet/path"; + BindingPattern notExcludedBinding = SystemBindingPattern.fromPattern("http://*:8081/" + servletPath); + BindingPattern excludedBinding = SystemBindingPattern.fromPattern("http://*:8080/" + servletPath); Element clusterElem = DomBuilderTest.parse( "<container version='1.0'>", httpWithExcludedBinding(excludedBinding), @@ -222,9 +225,9 @@ public class AccessControlTest extends ContainerModelBuilderTestBase { @Test public void rest_api_can_be_excluded_by_excluding_one_of_its_bindings() { - final String restApiPath = "api/v0"; - final String notExcludedBinding = "http://*:8081/" + restApiPath + Jersey2Servlet.BINDING_SUFFIX;; - final String excludedBinding = "http://*:8080/" + restApiPath + Jersey2Servlet.BINDING_SUFFIX;; + String restApiPath = "api/v0"; + BindingPattern notExcludedBinding = SystemBindingPattern.fromPattern("http://*:8081/" + restApiPath + Jersey2Servlet.BINDING_SUFFIX); + BindingPattern excludedBinding = SystemBindingPattern.fromPattern("http://*:8080/" + restApiPath + Jersey2Servlet.BINDING_SUFFIX);; Element clusterElem = DomBuilderTest.parse( "<container version='1.0'>", httpWithExcludedBinding(excludedBinding), @@ -290,14 +293,17 @@ public class AccessControlTest extends ContainerModelBuilderTestBase { assertThat(http.getFilterChains().hasChain(ComponentId.fromString("myChain")), is(true)); } + private static String prettyString(Set<BindingPattern> missingRequiredBindings) { + return missingRequiredBindings.stream().map(BindingPattern::patternString).collect(Collectors.joining(", ")); + } - private String httpWithExcludedBinding(String excludedBinding) { + private String httpWithExcludedBinding(BindingPattern excludedBinding) { return joinLines( " <http>", " <filtering>", " <access-control domain='foo'>", " <exclude>", - " <binding>" + excludedBinding + "</binding>", + " <binding>" + excludedBinding.patternString() + "</binding>", " </exclude>", " </access-control>", " </filtering>", @@ -312,9 +318,9 @@ public class AccessControlTest extends ContainerModelBuilderTestBase { return http; } - private boolean containsBinding(Collection<Binding> bindings, String binding) { - for (Binding b : bindings) { - if (b.binding().contains(binding)) + private boolean containsBinding(Collection<FilterBinding> bindings, BindingPattern binding) { + for (FilterBinding b : bindings) { + if (b.binding().equals(binding)) return true; } return false; diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerDocumentApiBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerDocumentApiBuilderTest.java index ac2e1b88c0b..73a68429b6d 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerDocumentApiBuilderTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerDocumentApiBuilderTest.java @@ -4,6 +4,8 @@ package com.yahoo.vespa.model.container.xml; import com.yahoo.config.model.builder.xml.test.DomBuilderTest; import com.yahoo.vespa.model.container.ContainerCluster; import com.yahoo.vespa.model.container.component.Handler; +import com.yahoo.vespa.model.container.component.SystemBindingPattern; +import com.yahoo.vespa.model.container.component.UserBindingPattern; import org.junit.Test; import org.w3c.dom.Element; @@ -40,24 +42,21 @@ public class ContainerDocumentApiBuilderTest extends ContainerModelBuilderTestBa "<container id='cluster1' version='1.0'>", " <document-api>", " <binding>http://*/document-api/</binding>", - " <binding>missing-trailing-slash</binding>", " </document-api>", nodesXml, "</container>"); createModel(root, elem); - verifyCustomBindings("com.yahoo.vespa.http.server.FeedHandler", ContainerCluster.RESERVED_URI_PREFIX + "/feedapi"); + verifyCustomBindings("com.yahoo.vespa.http.server.FeedHandler"); } - private void verifyCustomBindings(String id, String bindingSuffix) { + private void verifyCustomBindings(String id) { Handler<?> handler = getHandlers("cluster1").get(id); - assertThat(handler.getServerBindings(), hasItem("http://*/document-api/" + bindingSuffix)); - assertThat(handler.getServerBindings(), hasItem("http://*/document-api/" + bindingSuffix + "/")); - assertThat(handler.getServerBindings(), hasItem("missing-trailing-slash/" + bindingSuffix)); - assertThat(handler.getServerBindings(), hasItem("missing-trailing-slash/" + bindingSuffix + "/")); + assertThat(handler.getServerBindings(), hasItem(UserBindingPattern.fromHttpPath("/document-api/reserved-for-internal-use/feedapi"))); + assertThat(handler.getServerBindings(), hasItem(UserBindingPattern.fromHttpPath("/document-api/reserved-for-internal-use/feedapi/"))); - assertThat(handler.getServerBindings().size(), is(4)); + assertThat(handler.getServerBindings().size(), is(2)); } @Test @@ -76,8 +75,12 @@ public class ContainerDocumentApiBuilderTest extends ContainerModelBuilderTestBa assertThat(handlerMap.get("com.yahoo.container.jdisc.state.StateHandler"), not(nullValue())); assertThat(handlerMap.get("com.yahoo.vespa.http.server.FeedHandler"), not(nullValue())); - assertThat(handlerMap.get("com.yahoo.vespa.http.server.FeedHandler").getServerBindings().contains("http://*/" + ContainerCluster.RESERVED_URI_PREFIX + "/feedapi"), is(true)); - assertThat(handlerMap.get("com.yahoo.vespa.http.server.FeedHandler").getServerBindings().contains("http://*/" + ContainerCluster.RESERVED_URI_PREFIX + "/feedapi/"), is(true)); + assertThat(handlerMap.get("com.yahoo.vespa.http.server.FeedHandler").getServerBindings() + .contains(SystemBindingPattern.fromHttpPath("/reserved-for-internal-use/feedapi")), + is(true)); + assertThat(handlerMap.get("com.yahoo.vespa.http.server.FeedHandler").getServerBindings() + .contains(SystemBindingPattern.fromHttpPath("/reserved-for-internal-use/feedapi")), + is(true)); assertThat(handlerMap.get("com.yahoo.vespa.http.server.FeedHandler").getServerBindings().size(), equalTo(2)); } } diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java index fdd7ae57f0f..6114449c948 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java @@ -241,7 +241,7 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase { Element clusterElem = DomBuilderTest.parse( "<container id='default' version='1.0'>" + " <handler id='userRootHandler'>" + - " <binding>" + ROOT_HANDLER_BINDING + "</binding>" + + " <binding>" + ROOT_HANDLER_BINDING.patternString() + "</binding>" + " </handler>" + "</container>"); createModel(root, clusterElem); @@ -260,7 +260,7 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase { Element clusterElem = DomBuilderTest.parse( "<container id='default' version='1.0'>" + " <handler id='userHandler'>" + - " <binding>" + STATE_HANDLER_BINDING_1 + "</binding>" + + " <binding>" + STATE_HANDLER_BINDING_1.patternString() + "</binding>" + " </handler>" + "</container>"); try { @@ -277,9 +277,9 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase { createClusterWithJDiscHandler(); String discBindingsConfig = root.getConfig(JdiscBindingsConfig.class, "default").toString(); assertThat(discBindingsConfig, containsString("{discHandler}")); - assertThat(discBindingsConfig, containsString(".serverBindings[0] \"binding0\"")); - assertThat(discBindingsConfig, containsString(".serverBindings[1] \"binding1\"")); - assertThat(discBindingsConfig, containsString(".clientBindings[0] \"clientBinding\"")); + assertThat(discBindingsConfig, containsString(".serverBindings[0] \"http://*/binding0\"")); + assertThat(discBindingsConfig, containsString(".serverBindings[1] \"http://*/binding1\"")); + assertThat(discBindingsConfig, containsString(".clientBindings[0] \"http://*/clientBinding\"")); } @Test @@ -292,9 +292,9 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase { Element clusterElem = DomBuilderTest.parse( "<container id='default' version='1.0'>", " <handler id='discHandler'>", - " <binding>binding0</binding>", - " <binding>binding1</binding>", - " <clientBinding>clientBinding</clientBinding>", + " <binding>http://*/binding0</binding>", + " <binding>http://*/binding1</binding>", + " <clientBinding>http://*/clientBinding</clientBinding>", " </handler>", "</container>"); @@ -340,16 +340,16 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase { Element clusterElem = DomBuilderTest.parse( "<container id='default' version='1.0'>", " <processing>", - " <binding>binding0</binding>", - " <binding>binding1</binding>", + " <binding>http://*/binding0</binding>", + " <binding>http://*/binding1</binding>", " </processing>", "</container>"); createModel(root, clusterElem); String discBindingsConfig = root.getConfig(JdiscBindingsConfig.class, "default").toString(); - assertThat(discBindingsConfig, containsString(".serverBindings[0] \"binding0\"")); - assertThat(discBindingsConfig, containsString(".serverBindings[1] \"binding1\"")); + assertThat(discBindingsConfig, containsString(".serverBindings[0] \"http://*/binding0\"")); + assertThat(discBindingsConfig, containsString(".serverBindings[1] \"http://*/binding1\"")); assertThat(discBindingsConfig, not(containsString("/processing/*"))); } @@ -358,9 +358,9 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase { createModelWithClientProvider(); String discBindingsConfig = root.getConfig(JdiscBindingsConfig.class, "default").toString(); assertThat(discBindingsConfig, containsString("{discClient}")); - assertThat(discBindingsConfig, containsString(".clientBindings[0] \"binding0\"")); - assertThat(discBindingsConfig, containsString(".clientBindings[1] \"binding1\"")); - assertThat(discBindingsConfig, containsString(".serverBindings[0] \"serverBinding\"")); + assertThat(discBindingsConfig, containsString(".clientBindings[0] \"http://*/binding0\"")); + assertThat(discBindingsConfig, containsString(".clientBindings[1] \"http://*/binding1\"")); + assertThat(discBindingsConfig, containsString(".serverBindings[0] \"http://*/serverBinding\"")); } @Test @@ -373,9 +373,9 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase { Element clusterElem = DomBuilderTest.parse( "<container id='default' version='1.0'>" + " <client id='discClient'>" + - " <binding>binding0</binding>" + - " <binding>binding1</binding>" + - " <serverBinding>serverBinding</serverBinding>" + + " <binding>http://*/binding0</binding>" + + " <binding>http://*/binding1</binding>" + + " <serverBinding>http://*/serverBinding</serverBinding>" + " </client>" + "</container>" ); diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/SearchBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/SearchBuilderTest.java index b2f9c805be1..c8564c5a273 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/SearchBuilderTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/SearchBuilderTest.java @@ -20,6 +20,8 @@ import static com.yahoo.test.Matchers.hasItemWithMethod; import static com.yahoo.vespa.model.container.search.ContainerSearch.QUERY_PROFILE_REGISTRY_CLASS; import static com.yahoo.vespa.model.container.xml.ContainerModelBuilder.SEARCH_HANDLER_BINDING; import static com.yahoo.vespa.model.container.xml.ContainerModelBuilder.SEARCH_HANDLER_CLASS; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -48,7 +50,7 @@ public class SearchBuilderTest extends ContainerModelBuilderTestBase { createModel(root, clusterElem); String discBindingsConfig = root.getConfig(JdiscBindingsConfig.class, "default").toString(); - assertTrue(discBindingsConfig.contains(GUIHandler.BINDING)); + assertThat(discBindingsConfig, containsString(GUIHandler.BINDING_PATH)); ApplicationContainerCluster cluster = (ApplicationContainerCluster)root.getChildren().get("default"); @@ -66,8 +68,8 @@ public class SearchBuilderTest extends ContainerModelBuilderTestBase { Element clusterElem = DomBuilderTest.parse( "<container id='default' version='1.0'>", " <search>", - " <binding>binding0</binding>", - " <binding>binding1</binding>", + " <binding>http://*/binding0</binding>", + " <binding>http://*/binding1</binding>", " </search>", nodesXml, "</container>"); @@ -75,9 +77,9 @@ public class SearchBuilderTest extends ContainerModelBuilderTestBase { createModel(root, clusterElem); String discBindingsConfig = root.getConfig(JdiscBindingsConfig.class, "default").toString(); - assertTrue(discBindingsConfig.contains(".serverBindings[0] \"binding0\"")); - assertTrue(discBindingsConfig.contains(".serverBindings[1] \"binding1\"")); - assertFalse(discBindingsConfig.contains("/search/*")); + assertThat(discBindingsConfig, containsString(".serverBindings[0] \"http://*/binding0\"")); + assertThat(discBindingsConfig, containsString(".serverBindings[1] \"http://*/binding1\"")); + assertThat(discBindingsConfig, not(containsString("/search/*"))); } @Test @@ -103,7 +105,7 @@ public class SearchBuilderTest extends ContainerModelBuilderTestBase { "<container id='default' version='1.0'>", " <search />", " <handler id='" + myHandler + "'>", - " <binding>" + SEARCH_HANDLER_BINDING + "</binding>", + " <binding>" + SEARCH_HANDLER_BINDING.patternString() + "</binding>", " </handler>", nodesXml, "</container>"); @@ -111,7 +113,7 @@ public class SearchBuilderTest extends ContainerModelBuilderTestBase { createModel(root, clusterElem); var discBindingsConfig = root.getConfig(JdiscBindingsConfig.class, "default"); - assertEquals(SEARCH_HANDLER_BINDING, discBindingsConfig.handlers(myHandler).serverBindings(0)); + assertEquals(SEARCH_HANDLER_BINDING.patternString(), discBindingsConfig.handlers(myHandler).serverBindings(0)); assertNull(discBindingsConfig.handlers(SEARCH_HANDLER_CLASS)); } diff --git a/config/src/main/java/com/yahoo/vespa/config/benchmark/LoadTester.java b/config/src/main/java/com/yahoo/vespa/config/benchmark/LoadTester.java index 8a0f24bcd48..3a8d80e5ffe 100644 --- a/config/src/main/java/com/yahoo/vespa/config/benchmark/LoadTester.java +++ b/config/src/main/java/com/yahoo/vespa/config/benchmark/LoadTester.java @@ -43,11 +43,11 @@ import java.util.concurrent.ThreadLocalRandom; public class LoadTester { private static boolean debug = false; - private Transport transport = new Transport("rpc-client"); + private final Transport transport = new Transport("rpc-client"); protected Supervisor supervisor = new Supervisor(transport); private List<ConfigKey<?>> configs = new ArrayList<>(); private Map<ConfigDefinitionKey, Tuple2<String, String[]>> defs = new HashMap<>(); - private CompressionType compressionType = JRTConfigRequestFactory.getCompressionType(); + private final CompressionType compressionType = JRTConfigRequestFactory.getCompressionType(); /** * @param args command-line arguments @@ -149,7 +149,7 @@ public class LoadTester { return ret; } - private class Metrics { + private static class Metrics { long totBytes = 0; long totLatency = 0; @@ -214,9 +214,7 @@ public class LoadTester { Tuple2<String, String[]> defContent = defs.get(dKey); if (defContent == null && defs.size() > 0) { // Only complain if we actually did run with a def dir System.out.println("# No def found for " + dKey + ", not sending in request."); - }/* else { - System.out.println("# FOUND: "+dKey+" : "+ StringUtilities.implode(defContent, "\n")); - }*/ + } request = getRequest(ConfigKey.createFull(reqKey.getName(), reqKey.getConfigId(), reqKey.getNamespace(), defContent.first), defContent.second); if (debug) System.out.println("# Requesting: " + reqKey); long start = System.currentTimeMillis(); @@ -261,7 +259,7 @@ public class LoadTester { if (defContent == null) defContent = new String[0]; final long serverTimeout = 1000; return JRTClientConfigRequestV3.createWithParams(reqKey, DefContent.fromList(Arrays.asList(defContent)), - "unknown", "", 0, serverTimeout, Trace.createDummy(), + ConfigUtils.getCanonicalHostName(), "", 0, serverTimeout, Trace.createDummy(), compressionType, Optional.empty()); } @@ -269,4 +267,5 @@ public class LoadTester { return supervisor.connect(spec); } } + } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java index 03769d0a537..b38d90d470f 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java @@ -47,7 +47,6 @@ import java.time.Clock; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -317,8 +316,7 @@ public class SessionRepository { getRemoteSessions().forEach(this::sessionAdded); } - private synchronized void sessionsChanged() throws NumberFormatException { - List<Long> sessions = getSessionListFromDirectoryCache(directoryCache.getCurrentData()); + private synchronized void sessionsChanged(List<Long> sessions) throws NumberFormatException { checkForRemovedSessions(sessions); checkForAddedSessions(sessions); } @@ -418,14 +416,15 @@ public class SessionRepository { private void childEvent(CuratorFramework ignored, PathChildrenCacheEvent event) { zkWatcherExecutor.execute(() -> { log.log(Level.FINE, () -> "Got child event: " + event); + List<Long> sessions = getSessionListFromDirectoryCache(directoryCache.getCurrentData()); switch (event.getType()) { case CHILD_ADDED: - sessionsChanged(); - synchronizeOnNew(getSessionListFromDirectoryCache(Collections.singletonList(event.getData()))); + sessionsChanged(sessions); + synchronizeOnNew(sessions); break; case CHILD_REMOVED: case CONNECTION_RECONNECTED: - sessionsChanged(); + sessionsChanged(sessions); break; } }); diff --git a/default_build_settings.cmake b/default_build_settings.cmake index 5ba7494d1e1..69a69dc1860 100644 --- a/default_build_settings.cmake +++ b/default_build_settings.cmake @@ -43,7 +43,7 @@ function(setup_vespa_default_build_settings_darwin) else() set(DEFAULT_VESPA_LLVM_VERSION "10" PARENT_SCOPE) endif() - set(DEFAULT_CMAKE_PREFIX_PATH "${VESPA_DEPS}" "/usr/local/opt/bison" "/usr/local/opt/flex" "/usr/local/opt/openssl@1.1" "/usr/local/opt/openblas" PARENT_SCOPE) + set(DEFAULT_CMAKE_PREFIX_PATH "${VESPA_DEPS}" "/usr/local/opt/bison" "/usr/local/opt/flex" "/usr/local/opt/openssl@1.1" "/usr/local/opt/openblas" "/usr/local/opt/icu4c" PARENT_SCOPE) set(DEFAULT_EXTRA_LINK_DIRECTORY "${VESPA_DEPS}/lib" "/usr/local/opt/bison/lib" "/usr/local/opt/flex/lib" "/usr/local/opt/icu4c/lib" "/usr/local/opt/openssl@1.1/lib" "/usr/local/opt/openblas/lib") if(DEFINED DEFAULT_LLVM_LINK_DIRECTORY) list(APPEND DEFAULT_EXTRA_LINK_DIRECTORY "${DEFAULT_LLVM_LINK_DIRECTORY}") @@ -278,8 +278,8 @@ function(vespa_use_default_cxx_compiler) unset(DEFAULT_CMAKE_CXX_COMPILER) if(NOT DEFINED VESPA_COMPILER_VARIANT OR VESPA_COMPILER_VARIANT STREQUAL "gcc") if(APPLE) - set(DEFAULT_CMAKE_C_COMPILER "/usr/local/bin/gcc-9") - set(DEFAULT_CMAKE_CXX_COMPILER "/usr/local/bin/g++-9") + set(DEFAULT_CMAKE_C_COMPILER "/usr/local/bin/gcc-10") + set(DEFAULT_CMAKE_CXX_COMPILER "/usr/local/bin/g++-10") endif() elseif(VESPA_COMPILER_VARIANT STREQUAL "clang") if(APPLE) diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseTest.java index 44026f835dd..149510bdc97 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseTest.java @@ -149,7 +149,6 @@ public class CuratorDatabaseTest { this.task = task; } - @SuppressWarnings("deprecation") @Override public org.apache.curator.framework.api.transaction.CuratorTransaction and(org.apache.curator.framework.api.transaction.CuratorTransaction transaction) { task.run(); diff --git a/parent/pom.xml b/parent/pom.xml index 0cfe87c6488..a9998af3962 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -82,12 +82,6 @@ <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <version>${maven-bundle-plugin.version}</version> - <configuration> - <!-- TODO: remove when bundle-plugin understands java 10, https://issues.apache.org/jira/browse/FELIX-5879 --> - <instructions> - <_noee>true</_noee> - </instructions> - </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> @@ -763,14 +757,15 @@ zkfacade/src/main/java/org/apache/curator/**/package-info.java using something like find zkfacade/src/main/java/org/apache/curator -name package-info.java | \ - xargs perl -pi -e 's/major = [0-9]+, minor = [0-9]+, micro = [0-9]+/major = 4, minor = 3, micro = 0/g' + xargs perl -pi -e 's/major = [0-9]+, minor = [0-9]+, micro = [0-9]+/major = 2, minor = 9, micro = 1/g' --> - <curator.version>4.3.0</curator.version> + <curator.version>2.13.0</curator.version> <jna.version>4.5.2</jna.version> <commons.math3.version>3.6.1</commons.math3.version> <junit.version>5.6.2</junit.version> <maven-assembly-plugin.version>3.1.1</maven-assembly-plugin.version> - <maven-bundle-plugin.version>3.5.0</maven-bundle-plugin.version> + <!-- TODO: in order to upgrade above 4.1.0, we probably need to convert fat-model-deps to a jar artifact. --> + <maven-bundle-plugin.version>4.1.0</maven-bundle-plugin.version> <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version> <maven-dependency-plugin.version>3.1.1</maven-dependency-plugin.version> <maven-deploy-plugin.version>2.8.1</maven-deploy-plugin.version> diff --git a/searchcore/src/vespa/searchcore/proton/server/documentretriever.cpp b/searchcore/src/vespa/searchcore/proton/server/documentretriever.cpp index 031dd4f35e7..952c359b716 100644 --- a/searchcore/src/vespa/searchcore/proton/server/documentretriever.cpp +++ b/searchcore/src/vespa/searchcore/proton/server/documentretriever.cpp @@ -261,6 +261,7 @@ DocumentRetriever::getPartialDocument(search::DocumentIdT lid, const document::D case FieldSet::Type::DOCID: break; } + doc->setRepo(getDocumentTypeRepo()); } return doc; } diff --git a/searchlib/src/tests/common/location/geo_location_test.cpp b/searchlib/src/tests/common/location/geo_location_test.cpp index 31b844d0fc8..eec2411f2af 100644 --- a/searchlib/src/tests/common/location/geo_location_test.cpp +++ b/searchlib/src/tests/common/location/geo_location_test.cpp @@ -4,6 +4,7 @@ #include <vespa/searchlib/common/geo_location.h> #include <vespa/searchlib/common/geo_location_spec.h> #include <vespa/searchlib/common/geo_location_parser.h> +#include <vespa/searchlib/query/tree/location.h> #include <vespa/vespalib/gtest/gtest.h> using search::common::GeoLocation; @@ -16,19 +17,36 @@ using Aspect = search::common::GeoLocation::Aspect; constexpr int32_t plus_inf = std::numeric_limits<int32_t>::max(); constexpr int32_t minus_inf = std::numeric_limits<int32_t>::min(); -constexpr uint32_t u32_inf = std::numeric_limits<uint32_t>::max(); bool is_parseable(const char *str) { GeoLocationParser parser; return parser.parseOldFormat(str); } +bool is_parseable_new(const char *str, bool with_field = false) { + GeoLocationParser parser; + if (with_field) { + return parser.parseWithField(str); + } + return parser.parseNoField(str); +} + GeoLocation parse(const char *str) { GeoLocationParser parser; EXPECT_TRUE(parser.parseOldFormat(str)); return parser.getGeoLocation(); } +GeoLocation parse_new(const std::string &str, bool with_field = false) { + GeoLocationParser parser; + if (with_field) { + EXPECT_TRUE(parser.parseWithField(str)); + } else { + EXPECT_TRUE(parser.parseNoField(str)); + } + return parser.getGeoLocation(); +} + TEST(GeoLocationParserTest, malformed_bounding_boxes_are_not_parseable) { EXPECT_TRUE(is_parseable("[2,10,20,30,40]")); EXPECT_FALSE(is_parseable("[2,10,20,30,40][2,10,20,30,40]")); @@ -40,6 +58,15 @@ TEST(GeoLocationParserTest, malformed_bounding_boxes_are_not_parseable) { EXPECT_FALSE(is_parseable("[10,20,30,40]")); } +TEST(GeoLocationParserTest, new_bounding_box_formats) { + EXPECT_TRUE(is_parseable_new("{b:{x:[10,30],y:[20,40]}}")); + EXPECT_TRUE(is_parseable_new("{b:{}}")); + EXPECT_TRUE(is_parseable_new("{b:[]}")); + EXPECT_TRUE(is_parseable_new("{b:10,b:20}")); + EXPECT_TRUE(is_parseable_new("{b:[10, 20, 30, 40]}")); + EXPECT_FALSE(is_parseable_new("{b:{x:[10,30],y:[20,40]}")); +} + TEST(GeoLocationParserTest, malformed_circles_are_not_parseable) { EXPECT_TRUE(is_parseable("(2,10,20,5,0,0,0)")); EXPECT_FALSE(is_parseable("(2,10,20,5,0,0,0)(2,10,20,5,0,0,0)")); @@ -52,94 +79,147 @@ TEST(GeoLocationParserTest, malformed_circles_are_not_parseable) { EXPECT_FALSE(is_parseable("(10,20,5)")); } +TEST(GeoLocationParserTest, new_circle_formats) { + EXPECT_TRUE(is_parseable_new("{p:{x:10,y:20}}")); + EXPECT_TRUE(is_parseable_new("{p:{x:10,y:20},r:5}")); + EXPECT_TRUE(is_parseable_new("{p:{x:10, y:10}, r:5}")); + EXPECT_TRUE(is_parseable_new("{'p':{y:20,x:10},'r':5}")); + EXPECT_TRUE(is_parseable_new("{\n \"p\": { \"x\": 10, \"y\": 20},\n \"r\": 5\n}")); + // json demands colon: + EXPECT_FALSE(is_parseable_new("{p:{x:10,y:10},r=5}")); + // missing y -> 0 default: + EXPECT_TRUE(is_parseable_new("{p:{x:10},r:5}")); + // unused extra fields are ignored: + EXPECT_TRUE(is_parseable_new("{p:{x:10,y:10,z:10},r:5,c:1,d:17}")); +} + TEST(GeoLocationParserTest, bounding_boxes_can_be_parsed) { - auto loc = parse("[2,10,20,30,40]"); - EXPECT_EQ(false, loc.has_point); - EXPECT_EQ(true, loc.bounding_box.active()); - EXPECT_EQ(0u, loc.x_aspect.multiplier); - EXPECT_EQ(0, loc.point.x); - EXPECT_EQ(0, loc.point.y); - EXPECT_EQ(std::numeric_limits<uint32_t>::max(), loc.radius); - EXPECT_EQ(10, loc.bounding_box.x.low); - EXPECT_EQ(20, loc.bounding_box.y.low); - EXPECT_EQ(30, loc.bounding_box.x.high); - EXPECT_EQ(40, loc.bounding_box.y.high); + for (const auto & loc : { + parse("[2,10,20,30,40]"), + parse_new("{b:{x:[10,30],y:[20,40]}}") + }) { + EXPECT_EQ(false, loc.has_point); + EXPECT_EQ(true, loc.bounding_box.active()); + EXPECT_EQ(0u, loc.x_aspect.multiplier); + EXPECT_EQ(0, loc.point.x); + EXPECT_EQ(0, loc.point.y); + EXPECT_EQ(std::numeric_limits<uint32_t>::max(), loc.radius); + EXPECT_EQ(10, loc.bounding_box.x.low); + EXPECT_EQ(20, loc.bounding_box.y.low); + EXPECT_EQ(30, loc.bounding_box.x.high); + EXPECT_EQ(40, loc.bounding_box.y.high); + } } TEST(GeoLocationParserTest, circles_can_be_parsed) { - auto loc = parse("(2,10,20,5,0,0,0)"); - EXPECT_EQ(true, loc.has_point); - EXPECT_EQ(true, loc.bounding_box.active()); - EXPECT_EQ(0u, loc.x_aspect.multiplier); - EXPECT_EQ(10, loc.point.x); - EXPECT_EQ(20, loc.point.y); - EXPECT_EQ(5u, loc.radius); - EXPECT_EQ(5, loc.bounding_box.x.low); - EXPECT_EQ(15, loc.bounding_box.y.low); - EXPECT_EQ(15, loc.bounding_box.x.high); - EXPECT_EQ(25, loc.bounding_box.y.high); + for (const auto & loc : { + parse("(2,10,20,5,0,0,0)"), + parse_new("{p:{x:10,y:20},r:5}") + }) { + EXPECT_EQ(true, loc.has_point); + EXPECT_EQ(true, loc.bounding_box.active()); + EXPECT_EQ(0u, loc.x_aspect.multiplier); + EXPECT_EQ(10, loc.point.x); + EXPECT_EQ(20, loc.point.y); + EXPECT_EQ(5u, loc.radius); + EXPECT_EQ(5, loc.bounding_box.x.low); + EXPECT_EQ(15, loc.bounding_box.y.low); + EXPECT_EQ(15, loc.bounding_box.x.high); + EXPECT_EQ(25, loc.bounding_box.y.high); + } } TEST(GeoLocationParserTest, circles_can_have_aspect_ratio) { - auto loc = parse("(2,10,20,5,0,0,0,2147483648)"); - EXPECT_EQ(true, loc.has_point); - EXPECT_EQ(true, loc.bounding_box.active()); - EXPECT_EQ(2147483648u, loc.x_aspect.multiplier); - EXPECT_EQ(10, loc.point.x); - EXPECT_EQ(20, loc.point.y); - EXPECT_EQ(5u, loc.radius); - EXPECT_EQ(-1, loc.bounding_box.x.low); - EXPECT_EQ(15, loc.bounding_box.y.low); - EXPECT_EQ(21, loc.bounding_box.x.high); - EXPECT_EQ(25, loc.bounding_box.y.high); + for (const auto & loc : { + parse("(2,10,20,5,0,0,0,2147483648)"), + parse_new("{p:{x:10,y:20},r:5,a:2147483648}") + }) { + EXPECT_EQ(true, loc.has_point); + EXPECT_EQ(true, loc.bounding_box.active()); + EXPECT_EQ(2147483648u, loc.x_aspect.multiplier); + EXPECT_EQ(10, loc.point.x); + EXPECT_EQ(20, loc.point.y); + EXPECT_EQ(5u, loc.radius); + EXPECT_EQ(-1, loc.bounding_box.x.low); + EXPECT_EQ(15, loc.bounding_box.y.low); + EXPECT_EQ(21, loc.bounding_box.x.high); + EXPECT_EQ(25, loc.bounding_box.y.high); + } + auto loc2 = parse_new("{p:{x:10,y:10},a:3123456789}"); + EXPECT_EQ(3123456789, loc2.x_aspect.multiplier); } TEST(GeoLocationParserTest, bounding_box_can_be_specified_after_circle) { - auto loc = parse("(2,10,20,5,0,0,0)[2,10,20,30,40]"); - EXPECT_EQ(true, loc.has_point); - EXPECT_EQ(true, loc.bounding_box.active()); - EXPECT_EQ(0u, loc.x_aspect.multiplier); - EXPECT_EQ(10, loc.point.x); - EXPECT_EQ(20, loc.point.y); - EXPECT_EQ(5u, loc.radius); - EXPECT_EQ(10, loc.bounding_box.x.low); - EXPECT_EQ(20, loc.bounding_box.y.low); - EXPECT_EQ(15, loc.bounding_box.x.high); - EXPECT_EQ(25, loc.bounding_box.y.high); + for (const auto & loc : { + parse("(2,10,20,5,0,0,0)[2,10,20,30,40]"), + parse_new("{p:{x:10,y:20},r:5,b:{x:[10,30],y:[20,40]}}") + }) { + EXPECT_EQ(true, loc.has_point); + EXPECT_EQ(true, loc.bounding_box.active()); + EXPECT_EQ(0u, loc.x_aspect.multiplier); + EXPECT_EQ(10, loc.point.x); + EXPECT_EQ(20, loc.point.y); + EXPECT_EQ(5u, loc.radius); + EXPECT_EQ(10, loc.bounding_box.x.low); + EXPECT_EQ(20, loc.bounding_box.y.low); + EXPECT_EQ(15, loc.bounding_box.x.high); + EXPECT_EQ(25, loc.bounding_box.y.high); + } } TEST(GeoLocationParserTest, circles_can_be_specified_after_bounding_box) { - auto loc = parse("[2,10,20,30,40](2,10,20,5,0,0,0)"); + for (const auto & loc : { + parse("[2,10,20,30,40](2,10,20,5,0,0,0)"), + parse_new("{b:{x:[10,30],y:[20,40]},p:{x:10,y:20},r:5}") + }) { + EXPECT_EQ(true, loc.has_point); + EXPECT_EQ(true, loc.bounding_box.active()); + EXPECT_EQ(0u, loc.x_aspect.multiplier); + EXPECT_EQ(10, loc.point.x); + EXPECT_EQ(20, loc.point.y); + EXPECT_EQ(5u, loc.radius); + EXPECT_EQ(10, loc.bounding_box.x.low); + EXPECT_EQ(20, loc.bounding_box.y.low); + EXPECT_EQ(15, loc.bounding_box.x.high); + EXPECT_EQ(25, loc.bounding_box.y.high); + } + const auto &loc = parse_new("{a:12345,b:{x:[8,10],y:[8,10]},p:{x:10,y:10},r:3}"); EXPECT_EQ(true, loc.has_point); - EXPECT_EQ(true, loc.bounding_box.active()); - EXPECT_EQ(0u, loc.x_aspect.multiplier); EXPECT_EQ(10, loc.point.x); - EXPECT_EQ(20, loc.point.y); - EXPECT_EQ(5u, loc.radius); - EXPECT_EQ(10, loc.bounding_box.x.low); - EXPECT_EQ(20, loc.bounding_box.y.low); - EXPECT_EQ(15, loc.bounding_box.x.high); - EXPECT_EQ(25, loc.bounding_box.y.high); + EXPECT_EQ(10, loc.point.y); + EXPECT_EQ(12345u, loc.x_aspect.multiplier); } TEST(GeoLocationParserTest, santa_search_gives_non_wrapped_bounding_box) { - auto loc = parse("(2,122163600,89998536,290112,4,2000,0,109704)"); - EXPECT_GE(loc.bounding_box.x.high, loc.bounding_box.x.low); - EXPECT_GE(loc.bounding_box.y.high, loc.bounding_box.y.low); + for (const auto & loc : { + parse("(2,122163600,89998536,290112,4,2000,0,109704)"), + parse_new("{p:{x:122163600,y:89998536},r:290112,a:109704}") + }) { + EXPECT_GE(loc.bounding_box.x.high, loc.bounding_box.x.low); + EXPECT_GE(loc.bounding_box.y.high, loc.bounding_box.y.low); + } } TEST(GeoLocationParserTest, near_boundary_search_gives_non_wrapped_bounding_box) { - auto loc1 = parse("(2,2000000000,2000000000,3000000000,0,1,0)"); - EXPECT_GE(loc1.bounding_box.x.high, loc1.bounding_box.x.low); - EXPECT_GE(loc1.bounding_box.y.high, loc1.bounding_box.y.low); - EXPECT_EQ(std::numeric_limits<int32_t>::max(), loc1.bounding_box.y.high); - EXPECT_EQ(std::numeric_limits<int32_t>::max(), loc1.bounding_box.y.high); - - auto loc2 = parse("(2,-2000000000,-2000000000,3000000000,0,1,0)"); - EXPECT_GE(loc2.bounding_box.x.high, loc2.bounding_box.x.low); - EXPECT_GE(loc2.bounding_box.y.high, loc2.bounding_box.y.low); - EXPECT_EQ(std::numeric_limits<int32_t>::min(), loc2.bounding_box.x.low); - EXPECT_EQ(std::numeric_limits<int32_t>::min(), loc2.bounding_box.y.low); + for (const auto & loc1 : { + parse("(2,2000000000,2000000000,3000000000,0,1,0)"), + parse_new("{p:{x:2000000000,y:2000000000},r:3000000000}") + }) { + EXPECT_GE(loc1.bounding_box.x.high, loc1.bounding_box.x.low); + EXPECT_GE(loc1.bounding_box.y.high, loc1.bounding_box.y.low); + EXPECT_EQ(std::numeric_limits<int32_t>::max(), loc1.bounding_box.y.high); + EXPECT_EQ(std::numeric_limits<int32_t>::max(), loc1.bounding_box.y.high); + } + + for (const auto & loc2 : { + parse("(2,-2000000000,-2000000000,3000000000,0,1,0)"), + parse_new("{p:{x:-2000000000,y:-2000000000},r:3000000000}") + }) { + EXPECT_GE(loc2.bounding_box.x.high, loc2.bounding_box.x.low); + EXPECT_GE(loc2.bounding_box.y.high, loc2.bounding_box.y.low); + EXPECT_EQ(std::numeric_limits<int32_t>::min(), loc2.bounding_box.x.low); + EXPECT_EQ(std::numeric_limits<int32_t>::min(), loc2.bounding_box.y.low); + } } void check_box(const GeoLocation &location, Box expected) @@ -391,4 +471,67 @@ TEST(GeoLocationTest, box_point_radius_and_aspect) { EXPECT_EQ(location.bounding_box.y.high, 700); } +TEST(GeoLocationParserTest, can_parse_what_query_tree_produces) { + search::query::Point point_1{-17, 42}; + uint32_t distance = 12345; + uint32_t aspect_ratio = 67890; + search::query::Rectangle rectangle_1(-1, -2, 3, 4); + + search::query::Location loc_1(point_1); + std::string str_1 = loc_1.getJsonFormatString(); + auto result_1 = parse_new(str_1); + + EXPECT_EQ(true, result_1.has_point); + EXPECT_EQ(false, result_1.has_radius()); + EXPECT_EQ(false, result_1.x_aspect.active()); + EXPECT_EQ(false, result_1.bounding_box.active()); + EXPECT_EQ(-17, result_1.point.x); + EXPECT_EQ(42, result_1.point.y); + + search::query::Location loc_1b(point_1, distance, aspect_ratio); + std::string str_1b = loc_1b.getJsonFormatString(); + auto result_1b = parse_new(str_1b); + + EXPECT_EQ(true, result_1b.has_point); + EXPECT_EQ(true, result_1b.has_radius()); + EXPECT_EQ(true, result_1b.x_aspect.active()); + EXPECT_EQ(true, result_1b.bounding_box.active()); + EXPECT_EQ(-17, result_1b.point.x); + EXPECT_EQ(42, result_1b.point.y); + EXPECT_EQ(distance, result_1b.radius); + EXPECT_EQ(aspect_ratio, result_1b.x_aspect.multiplier); + EXPECT_EQ(42-distance, result_1b.bounding_box.y.low); + EXPECT_EQ(42+distance, result_1b.bounding_box.y.high); + + search::query::Location loc_2(rectangle_1); + std::string str_2 = loc_2.getJsonFormatString(); + auto result_2 = parse_new(str_2); + + EXPECT_EQ(false, result_2.has_point); + EXPECT_EQ(false, result_2.has_radius()); + EXPECT_EQ(false, result_2.x_aspect.active()); + EXPECT_EQ(true, result_2.bounding_box.active()); + EXPECT_EQ(-1, result_2.bounding_box.x.low); + EXPECT_EQ(-2, result_2.bounding_box.y.low); + EXPECT_EQ(3, result_2.bounding_box.x.high); + EXPECT_EQ(4, result_2.bounding_box.y.high); + + search::query::Location loc_3(rectangle_1, point_1, distance, aspect_ratio); + std::string str_3 = loc_3.getJsonFormatString(); + auto result_3 = parse_new(str_3); + + EXPECT_EQ(true, result_3.has_point); + EXPECT_EQ(true, result_3.has_radius()); + EXPECT_EQ(true, result_3.x_aspect.active()); + EXPECT_EQ(true, result_3.bounding_box.active()); + EXPECT_EQ(-17, result_3.point.x); + EXPECT_EQ(42, result_3.point.y); + EXPECT_EQ(distance, result_3.radius); + EXPECT_EQ(aspect_ratio, result_3.x_aspect.multiplier); + EXPECT_EQ(-1, result_3.bounding_box.x.low); + EXPECT_EQ(-2, result_3.bounding_box.y.low); + EXPECT_EQ(3, result_3.bounding_box.x.high); + EXPECT_EQ(4, result_3.bounding_box.y.high); +} + GTEST_MAIN_RUN_ALL_TESTS() diff --git a/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp b/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp index 032caaacba8..b9e4bf565ef 100644 --- a/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp +++ b/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp @@ -263,8 +263,18 @@ public: const common::Location &location() const { return _location; } SearchIterator::UP - createLeafSearch(const TermFieldMatchDataArray &, bool strict) const override + createLeafSearch(const TermFieldMatchDataArray &tfmda, bool strict) const override { + if (tfmda.size() == 1) { + // search in exactly one field + fef::TermFieldMatchData &tfmd = *tfmda[0]; + return search::common::create_location_iterator(tfmd, + _attribute.getNumDocs(), + strict, + _location); + } else { + LOG(debug, "wrong size tfmda: %zu (fallback to old location iterator)\n", tfmda.size()); + } return FastS_AllocLocationIterator(_attribute.getNumDocs(), strict, _location); } }; @@ -273,7 +283,8 @@ public: Blueprint::UP make_location_blueprint(const FieldSpec &field, const IAttributeVector &attribute, const Location &loc) { - LOG(debug, "make_location_blueprint(p[%d,%d], r[%u], aspect[%u], bb[[%d,%d],[%d,%d]])", + LOG(debug, "make_location_blueprint(fieldId[%u], p[%d,%d], r[%u], aspect[%u], bb[[%d,%d],[%d,%d]])", + field.getFieldId(), loc.point.x, loc.point.y, loc.radius, loc.x_aspect.multiplier, loc.bounding_box.x.low, loc.bounding_box.x.high, diff --git a/searchlib/src/vespa/searchlib/common/geo_location_parser.cpp b/searchlib/src/vespa/searchlib/common/geo_location_parser.cpp index 05c53348699..53792e56562 100644 --- a/searchlib/src/vespa/searchlib/common/geo_location_parser.cpp +++ b/searchlib/src/vespa/searchlib/common/geo_location_parser.cpp @@ -3,6 +3,11 @@ #include "geo_location_parser.h" #include <limits> #include <vespa/vespalib/stllike/asciistream.h> +#include <vespa/vespalib/data/slime/slime.h> +#include <vespa/vespalib/data/slime/json_format.h> + +#include <vespa/log/log.h> +LOG_SETUP(".searchlib.common.geo_location_parser"); namespace { @@ -39,7 +44,7 @@ GeoLocationParser::GeoLocationParser() _max_x(std::numeric_limits<int32_t>::max()), _min_y(std::numeric_limits<int32_t>::min()), _max_y(std::numeric_limits<int32_t>::max()), - _parseError(NULL) + _parseError(nullptr) {} bool @@ -62,7 +67,7 @@ GeoLocationParser::parseOldFormatWithField(const std::string &str) { auto sep = str.find(':'); if (sep == std::string::npos) { - _parseError = "Location string lacks field specification."; + _parseError = "Location string lacks field specification"; return false; } _field_name = str.substr(0, sep); @@ -179,9 +184,87 @@ GeoLocationParser::parseOldFormat(const std::string &locStr) return _valid; } +bool +GeoLocationParser::parseWithField(const std::string &str) +{ + auto sep = str.find(':'); + if (sep == std::string::npos) { + _parseError = "Location string lacks field specification"; + return false; + } + _field_name = str.substr(0, sep); + std::string only_loc = str.substr(sep + 1); + return parseNoField(only_loc); +} + +bool +GeoLocationParser::parseNoField(const std::string &str) +{ + if (str.empty()) { + _parseError = "Location string is empty"; + return false; + } + if (str[0] == '(' || str[0] == '[') { + return parseOldFormat(str); + } + if (str[0] != '{') { + _parseError = "Location string should start with '{'"; + return false; + } + return parseJsonFormat(str); +} + +bool +GeoLocationParser::parseJsonFormat(const std::string &str) +{ + vespalib::Slime slime; + size_t decoded = vespalib::slime::JsonFormat::decode(str, slime); + if (decoded == 0) { + LOG(warning, "bad location JSON: %s\n>> %s <<", + slime.get()["error_message"].asString().make_string().c_str(), + str.c_str()); + _parseError = "Failed decoding JSON format location"; + return false; + } + // fprintf(stderr, "parsed location JSON %s -> %s\n", str.c_str(), slime.toString().c_str()); + const auto &root = slime.get(); + const auto &point = root["p"]; + const auto &radius = root["r"]; + const auto &aspect = root["a"]; + const auto &bbox = root["b"]; + + if (point.valid()) { + _x = point["x"].asLong(); + _y = point["y"].asLong(); + _has_point = true; + } + if (radius.valid()) { + _radius = radius.asLong(); + } + if (aspect.valid()) { + _x_aspect = aspect.asLong(); + } + if (bbox.valid()) { + _min_x = bbox["x"][0].asLong(); + _max_x = bbox["x"][1].asLong(); + _min_y = bbox["y"][0].asLong(); + _max_y = bbox["y"][1].asLong(); + _has_bounding_box = true; + } + if (_has_point || _has_bounding_box) { + _valid = true; + } else { + _parseError = "Neither point nor bounding box found"; + } + return _valid; +} + GeoLocation GeoLocationParser::getGeoLocation() const { + if (! _valid) { + return GeoLocation(); + } GeoLocation::Aspect aspect(_x_aspect); if (_has_bounding_box) { GeoLocation::Range x_range{_min_x, _max_x}; diff --git a/searchlib/src/vespa/searchlib/common/geo_location_parser.h b/searchlib/src/vespa/searchlib/common/geo_location_parser.h index 8936a620d21..7d725f4a035 100644 --- a/searchlib/src/vespa/searchlib/common/geo_location_parser.h +++ b/searchlib/src/vespa/searchlib/common/geo_location_parser.h @@ -17,6 +17,9 @@ class GeoLocationParser public: GeoLocationParser(); + bool parseNoField(const std::string &locStr); + bool parseWithField(const std::string &locStr); + bool parseOldFormat(const std::string &locStr); bool parseOldFormatWithField(const std::string &str); @@ -42,6 +45,7 @@ private: const char *_parseError; bool correctDimensionalitySkip(const char * &p); + bool parseJsonFormat(const std::string &locStr); }; } // namespace diff --git a/searchlib/src/vespa/searchlib/common/locationiterators.cpp b/searchlib/src/vespa/searchlib/common/locationiterators.cpp index d90ed3b41f3..413930522c6 100644 --- a/searchlib/src/vespa/searchlib/common/locationiterators.cpp +++ b/searchlib/src/vespa/searchlib/common/locationiterators.cpp @@ -7,6 +7,108 @@ #include <vespa/log/log.h> LOG_SETUP(".searchlib.common.locationiterators"); +namespace search::common { + +class LocationIterator : public search::queryeval::SearchIterator +{ +private: + static constexpr double pi = 3.14159265358979323846; + // microdegrees -> degrees -> radians -> km (using Earth mean radius) + static constexpr double udeg_to_km = 1.0e-6 * (pi / 180.0) * 6371.0088; + search::fef::TermFieldMatchData & _tfmd; + const unsigned int _numDocs; + const bool _strict; + const Location & _location; + uint32_t _num_values; + std::vector<search::AttributeVector::largeint_t> _pos; + + void doSeek(uint32_t docId) override; + void doUnpack(uint32_t docId) override; +public: + LocationIterator(search::fef::TermFieldMatchData &tfmd, + unsigned int numDocs, + bool strict, + const Location & location); + ~LocationIterator() override; +}; + +LocationIterator::LocationIterator(search::fef::TermFieldMatchData &tfmd, + unsigned int numDocs, + bool strict, + const Location & location) + : SearchIterator(), + _tfmd(tfmd), + _numDocs(numDocs), + _strict(strict), + _location(location), + _num_values(0), + _pos() +{ + _pos.resize(1); //Need at least 1 entry as the singlevalue attributes does not honour given size. + LOG(debug, "created LocationIterator(numDocs=%u)\n", numDocs); +}; + + +LocationIterator::~LocationIterator() = default; + +void +LocationIterator::doSeek(uint32_t docId) +{ + while (__builtin_expect(docId < getEndId(), true)) { + if (__builtin_expect(docId >= _numDocs, false)) { + break; + } + _num_values = _location.getVec()->get(docId, &_pos[0], _pos.size()); + while (_num_values > _pos.size()) { + _pos.resize(_num_values); + _num_values = _location.getVec()->get(docId, &_pos[0], _pos.size()); + } + for (uint32_t i = 0; i < _num_values; i++) { + int64_t docxy(_pos[i]); + if (_location.inside_limit(docxy)) { + setDocId(docId); + return; + } + } + if (!_strict) { + return; + } + ++docId; + } + setAtEnd(); +} + +void +LocationIterator::doUnpack(uint32_t docId) +{ + uint64_t sqabsdist = std::numeric_limits<uint64_t>::max(); + int32_t docx = 0; + int32_t docy = 0; + // use _num_values from _pos fetched in doSeek() + for (uint32_t i = 0; i < _num_values; i++) { + int64_t docxy(_pos[i]); + vespalib::geo::ZCurve::decode(docxy, &docx, &docy); + uint64_t sqdist = _location.sq_distance_to({docx, docy}); + if (sqdist < sqabsdist) { + sqabsdist = sqdist; + } + } + double dist = std::sqrt(double(sqabsdist)); + double score = 1.0 / (1.0 + (udeg_to_km * dist)); + LOG(debug, "unpack LI(%u) score %f\n", docId, score); + LOG(debug, "distance: %f micro-degrees ~= %f km", dist, udeg_to_km * dist); + _tfmd.setRawScore(docId, score); +} + +std::unique_ptr<search::queryeval::SearchIterator> +create_location_iterator(search::fef::TermFieldMatchData &tfmd, unsigned int numDocs, + bool strict, const Location & location) +{ + return std::make_unique<LocationIterator>(tfmd, numDocs, strict, location); +} + +} // namespace + using namespace search::common; class FastS_2DZLocationIterator : public search::queryeval::SearchIterator diff --git a/searchlib/src/vespa/searchlib/common/locationiterators.h b/searchlib/src/vespa/searchlib/common/locationiterators.h index e345bcae4fe..d963ac2e479 100644 --- a/searchlib/src/vespa/searchlib/common/locationiterators.h +++ b/searchlib/src/vespa/searchlib/common/locationiterators.h @@ -4,9 +4,19 @@ #include <vespa/searchlib/queryeval/searchiterator.h> #include <vespa/searchlib/common/location.h> +#include <vespa/searchlib/fef/termfieldmatchdata.h> + +namespace search::common { + +std::unique_ptr<search::queryeval::SearchIterator> +create_location_iterator(search::fef::TermFieldMatchData &tfmd, + unsigned int numDocs, + bool strict, + const Location & location); + +} // namespace std::unique_ptr<search::queryeval::SearchIterator> FastS_AllocLocationIterator(unsigned int numDocs, bool strict, const search::common::Location & location); - diff --git a/searchlib/src/vespa/searchlib/fef/iqueryenvironment.h b/searchlib/src/vespa/searchlib/fef/iqueryenvironment.h index 7c6a84916f4..d51b29cbfe3 100644 --- a/searchlib/src/vespa/searchlib/fef/iqueryenvironment.h +++ b/searchlib/src/vespa/searchlib/fef/iqueryenvironment.h @@ -6,7 +6,7 @@ #include "objectstore.h" #include <vespa/searchcommon/attribute/iattributecontext.h> -namespace search::common { class GeoLocationSpec; } +namespace search::common { struct GeoLocationSpec; } namespace search::fef { diff --git a/searchlib/src/vespa/searchlib/query/tree/location.cpp b/searchlib/src/vespa/searchlib/query/tree/location.cpp index 6e678f9e682..44f0b82d304 100644 --- a/searchlib/src/vespa/searchlib/query/tree/location.cpp +++ b/searchlib/src/vespa/searchlib/query/tree/location.cpp @@ -33,12 +33,13 @@ Location::Location(const Rectangle &rect) bool Location::operator==(const Location &other) const { - auto me = getOldFormatString(); - auto it = other.getOldFormatString(); + auto me = getJsonFormatString(); + auto it = other.getJsonFormatString(); if (me == it) { return true; } else { // dump 'me' and 'it' here if unit tests fail + // fprintf(stderr, "me='%s', it='%s'\n", me.c_str(), it.c_str()); return false; } } @@ -69,8 +70,37 @@ Location::getOldFormatString() const return buf.str(); } +std::string +Location::getJsonFormatString() const +{ + // Only produce what search::common::GeoLocationParser can parse + vespalib::asciistream buf; + buf << "{"; + if (has_point) { + buf << "p:{x:" << point.x << ",y:" << point.y << "}"; + if (has_radius()) { + buf << "," << "r:" << radius; + } + if (x_aspect.active()) { + buf << "," << "a:" << x_aspect.multiplier; + } + } + if (bounding_box.active()) { + if (has_point) { + buf << ","; + } + buf << "b:{x:[" << bounding_box.x.low + << "," << bounding_box.x.high + << "],y:[" << bounding_box.y.low + << "," << bounding_box.y.high + << "]}" ; + } + buf << "}"; + return buf.str(); +} + vespalib::asciistream &operator<<(vespalib::asciistream &out, const Location &loc) { return out << loc.getOldFormatString(); } -} +} // namespace diff --git a/searchlib/src/vespa/searchlib/query/tree/location.h b/searchlib/src/vespa/searchlib/query/tree/location.h index 6b8090f45e1..143282e2958 100644 --- a/searchlib/src/vespa/searchlib/query/tree/location.h +++ b/searchlib/src/vespa/searchlib/query/tree/location.h @@ -22,6 +22,7 @@ public: bool operator==(const Location &other) const; std::string getOldFormatString() const; + std::string getJsonFormatString() const; }; vespalib::asciistream &operator<<(vespalib::asciistream &out, const Location &loc); diff --git a/storage/src/vespa/storage/distributor/distributor.h b/storage/src/vespa/storage/distributor/distributor.h index 84e195fdff2..fcc08030764 100644 --- a/storage/src/vespa/storage/distributor/distributor.h +++ b/storage/src/vespa/storage/distributor/distributor.h @@ -25,11 +25,11 @@ #include <unordered_map> namespace storage { + struct DoneInitializeHandler; + class HostInfo; +} -struct DoneInitializeHandler; -class HostInfo; - -namespace distributor { +namespace storage::distributor { class DistributorBucketSpaceRepo; class SimpleMaintenanceScanner; @@ -342,5 +342,4 @@ private: bool _must_send_updated_host_info; }; -} // distributor -} // storage +} diff --git a/storage/src/vespa/storage/distributor/operations/external/visitoroperation.cpp b/storage/src/vespa/storage/distributor/operations/external/visitoroperation.cpp index 70dd215cc1d..5a03e05d563 100644 --- a/storage/src/vespa/storage/distributor/operations/external/visitoroperation.cpp +++ b/storage/src/vespa/storage/distributor/operations/external/visitoroperation.cpp @@ -41,6 +41,8 @@ VisitorOperation::BucketInfo::toString() const return ost.str(); } +VisitorOperation::SuperBucketInfo::~SuperBucketInfo() = default; + VisitorOperation::VisitorOperation( DistributorComponent& owner, DistributorBucketSpace &bucketSpace, diff --git a/storage/src/vespa/storage/distributor/operations/external/visitoroperation.h b/storage/src/vespa/storage/distributor/operations/external/visitoroperation.h index fdfe60731f5..42b0bd56b9e 100644 --- a/storage/src/vespa/storage/distributor/operations/external/visitoroperation.h +++ b/storage/src/vespa/storage/distributor/operations/external/visitoroperation.h @@ -74,6 +74,7 @@ private: subBucketsCompletelyExpanded(false) { } + ~SuperBucketInfo(); }; diff --git a/storage/src/vespa/storage/persistence/fieldvisitor.h b/storage/src/vespa/storage/persistence/fieldvisitor.h index 99558fe6e9c..688874742a6 100644 --- a/storage/src/vespa/storage/persistence/fieldvisitor.h +++ b/storage/src/vespa/storage/persistence/fieldvisitor.h @@ -15,7 +15,7 @@ namespace storage { class FieldVisitor : public document::select::Visitor { private: - document::DocumentType _docType; + const document::DocumentType & _docType; document::Field::Set::Builder _fields; public: diff --git a/storage/src/vespa/storage/persistence/testandsethelper.cpp b/storage/src/vespa/storage/persistence/testandsethelper.cpp index ed396cd522e..9232abc5c8a 100644 --- a/storage/src/vespa/storage/persistence/testandsethelper.cpp +++ b/storage/src/vespa/storage/persistence/testandsethelper.cpp @@ -69,7 +69,7 @@ TestAndSetHelper::retrieveAndMatch(spi::Context & context) { auto docPtr = result.getDocumentPtr(); if (_docSelectionUp->contains(*docPtr) != document::select::Result::True) { return api::ReturnCode(api::ReturnCode::TEST_AND_SET_CONDITION_FAILED, - vespalib::make_string("Condition did not match document partition=%d, nodeIndex=%d bucket=%lx %s", + vespalib::make_string("Condition did not match document partition=%d, nodeIndex=%d bucket=%" PRIx64 " %s", _thread._env._partition, _thread._env._nodeIndex, _cmd.getBucketId().getRawId(), _cmd.hasBeenRemapped() ? "remapped" : "")); } @@ -81,7 +81,7 @@ TestAndSetHelper::retrieveAndMatch(spi::Context & context) { } return api::ReturnCode(api::ReturnCode::TEST_AND_SET_CONDITION_FAILED, - vespalib::make_string("Document does not exist partition=%d, nodeIndex=%d bucket=%lx %s", + vespalib::make_string("Document does not exist partition=%d, nodeIndex=%d bucket=%" PRIx64 " %s", _thread._env._partition, _thread._env._nodeIndex, _cmd.getBucketId().getRawId(), _cmd.hasBeenRemapped() ? "remapped" : "")); } diff --git a/storage/src/vespa/storage/storageserver/storagenodecontext.cpp b/storage/src/vespa/storage/storageserver/storagenodecontext.cpp index 75e1f12773f..49390d4e579 100644 --- a/storage/src/vespa/storage/storageserver/storagenodecontext.cpp +++ b/storage/src/vespa/storage/storageserver/storagenodecontext.cpp @@ -13,4 +13,6 @@ StorageNodeContext::StorageNodeContext(ComponentRegister::UP compReg, framework: _componentRegister->setThreadPool(_threadPool); } +StorageNodeContext::~StorageNodeContext() = default; + } // storage diff --git a/storage/src/vespa/storage/storageserver/storagenodecontext.h b/storage/src/vespa/storage/storageserver/storagenodecontext.h index eabca618bfb..163c02ef5af 100644 --- a/storage/src/vespa/storage/storageserver/storagenodecontext.h +++ b/storage/src/vespa/storage/storageserver/storagenodecontext.h @@ -40,6 +40,8 @@ struct StorageNodeContext { */ FastOS_ThreadPool& getThreadPool() { return _threadPool.getThreadPool(); } + ~StorageNodeContext(); + protected: // Initialization has been split in two as subclass needs to initialize // component register before sending it on. diff --git a/storageapi/src/vespa/storageapi/message/visitor.cpp b/storageapi/src/vespa/storageapi/message/visitor.cpp index faf58361276..d87f65a72cf 100644 --- a/storageapi/src/vespa/storageapi/message/visitor.cpp +++ b/storageapi/src/vespa/storageapi/message/visitor.cpp @@ -4,6 +4,7 @@ #include <vespa/document/fieldset/fieldsets.h> #include <vespa/vespalib/util/array.hpp> #include <climits> +#include <ostream> namespace storage::api { diff --git a/zkfacade/pom.xml b/zkfacade/pom.xml index c160d935788..de542b89b67 100644 --- a/zkfacade/pom.xml +++ b/zkfacade/pom.xml @@ -48,7 +48,6 @@ <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-test</artifactId> - <version>4.2.0</version> <scope>test</scope> <exclusions> <exclusion> diff --git a/zkfacade/src/main/java/com/yahoo/vespa/curator/Curator.java b/zkfacade/src/main/java/com/yahoo/vespa/curator/Curator.java index 51cb387b734..37b1fa1c9fb 100644 --- a/zkfacade/src/main/java/com/yahoo/vespa/curator/Curator.java +++ b/zkfacade/src/main/java/com/yahoo/vespa/curator/Curator.java @@ -279,7 +279,7 @@ public class Curator implements AutoCloseable { */ public void createAtomically(Path... paths) { try { - @SuppressWarnings("deprecation") CuratorTransaction transaction = framework().inTransaction(); + CuratorTransaction transaction = framework().inTransaction(); for (Path path : paths) { if ( ! exists(path)) { transaction = transaction.create().forPath(path.getAbsolute(), new byte[0]).and(); diff --git a/zkfacade/src/main/java/com/yahoo/vespa/curator/mock/MockCurator.java b/zkfacade/src/main/java/com/yahoo/vespa/curator/mock/MockCurator.java index 1f583ada7a1..3da7678c44e 100644 --- a/zkfacade/src/main/java/com/yahoo/vespa/curator/mock/MockCurator.java +++ b/zkfacade/src/main/java/com/yahoo/vespa/curator/mock/MockCurator.java @@ -12,40 +12,31 @@ import com.yahoo.vespa.curator.Curator; import com.yahoo.vespa.curator.recipes.CuratorLockException; import org.apache.curator.CuratorZookeeperClient; import org.apache.curator.framework.CuratorFramework; -import org.apache.curator.framework.WatcherRemoveCuratorFramework; import org.apache.curator.framework.api.ACLBackgroundPathAndBytesable; import org.apache.curator.framework.api.ACLCreateModeBackgroundPathAndBytesable; import org.apache.curator.framework.api.ACLCreateModePathAndBytesable; -import org.apache.curator.framework.api.ACLCreateModeStatBackgroundPathAndBytesable; import org.apache.curator.framework.api.ACLPathAndBytesable; -import org.apache.curator.framework.api.ACLableExistBuilderMain; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.BackgroundPathAndBytesable; import org.apache.curator.framework.api.BackgroundPathable; import org.apache.curator.framework.api.BackgroundVersionable; import org.apache.curator.framework.api.ChildrenDeletable; -import org.apache.curator.framework.api.CreateBackgroundModeStatACLable; +import org.apache.curator.framework.api.CreateBackgroundModeACLable; import org.apache.curator.framework.api.CreateBuilder; -import org.apache.curator.framework.api.CreateBuilder2; -import org.apache.curator.framework.api.CreateBuilderMain; -import org.apache.curator.framework.api.CreateProtectACLCreateModePathAndBytesable; import org.apache.curator.framework.api.CuratorListener; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.framework.api.DeleteBuilder; -import org.apache.curator.framework.api.DeleteBuilderMain; import org.apache.curator.framework.api.ErrorListenerPathAndBytesable; import org.apache.curator.framework.api.ErrorListenerPathable; import org.apache.curator.framework.api.ExistsBuilder; +import org.apache.curator.framework.api.ExistsBuilderMain; import org.apache.curator.framework.api.GetACLBuilder; import org.apache.curator.framework.api.GetChildrenBuilder; -import org.apache.curator.framework.api.GetConfigBuilder; import org.apache.curator.framework.api.GetDataBuilder; import org.apache.curator.framework.api.GetDataWatchBackgroundStatable; import org.apache.curator.framework.api.PathAndBytesable; import org.apache.curator.framework.api.Pathable; -import org.apache.curator.framework.api.ProtectACLCreateModeStatPathAndBytesable; -import org.apache.curator.framework.api.ReconfigBuilder; -import org.apache.curator.framework.api.RemoveWatchesBuilder; +import org.apache.curator.framework.api.ProtectACLCreateModePathAndBytesable; import org.apache.curator.framework.api.SetACLBuilder; import org.apache.curator.framework.api.SetDataBackgroundVersionable; import org.apache.curator.framework.api.SetDataBuilder; @@ -54,16 +45,13 @@ import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.framework.api.VersionPathAndBytesable; import org.apache.curator.framework.api.WatchPathable; import org.apache.curator.framework.api.Watchable; -import org.apache.curator.framework.api.transaction.CuratorMultiTransaction; import org.apache.curator.framework.api.transaction.CuratorTransaction; import org.apache.curator.framework.api.transaction.CuratorTransactionBridge; import org.apache.curator.framework.api.transaction.CuratorTransactionFinal; import org.apache.curator.framework.api.transaction.CuratorTransactionResult; import org.apache.curator.framework.api.transaction.TransactionCheckBuilder; import org.apache.curator.framework.api.transaction.TransactionCreateBuilder; -import org.apache.curator.framework.api.transaction.TransactionCreateBuilder2; import org.apache.curator.framework.api.transaction.TransactionDeleteBuilder; -import org.apache.curator.framework.api.transaction.TransactionOp; import org.apache.curator.framework.api.transaction.TransactionSetDataBuilder; import org.apache.curator.framework.imps.CuratorFrameworkState; import org.apache.curator.framework.listen.Listenable; @@ -76,8 +64,6 @@ import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; import org.apache.curator.framework.recipes.locks.InterProcessLock; import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex; -import org.apache.curator.framework.schema.SchemaSet; -import org.apache.curator.framework.state.ConnectionStateErrorPolicy; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.utils.EnsurePath; import org.apache.zookeeper.CreateMode; @@ -85,7 +71,6 @@ import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; -import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import java.nio.file.Paths; import java.time.Duration; @@ -97,7 +82,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; @@ -649,101 +633,64 @@ public class MockCurator extends Curator { // ----- file system methods above. ----- // ----- There's nothing to see unless you are interested in an illustration of ----- // ----- the folly of fluent API's or, more generally, mankind. ----- - private abstract static class MockProtectACLCreateModeStatPathAndBytesable<String> - implements ProtectACLCreateModeStatPathAndBytesable<String> { - public BackgroundPathAndBytesable<String> withACL(List<ACL> list) { - throw new UnsupportedOperationException("Not implemented in MockCurator"); - } + private abstract class MockBackgroundACLPathAndBytesableBuilder<T> implements PathAndBytesable<T>, ProtectACLCreateModePathAndBytesable<T> { - public BackgroundPathAndBytesable<String> withACL(List<ACL> list, boolean b) { + public BackgroundPathAndBytesable<T> withACL(List<ACL> list) { throw new UnsupportedOperationException("Not implemented in MockCurator"); } - public ProtectACLCreateModeStatPathAndBytesable<String> withMode(CreateMode createMode) { + public ACLBackgroundPathAndBytesable<T> withMode(CreateMode createMode) { throw new UnsupportedOperationException("Not implemented in MockCurator"); } @Override - public ACLCreateModeBackgroundPathAndBytesable<java.lang.String> withProtection() { - return null; - } - - @Override - public ErrorListenerPathAndBytesable<String> inBackground() { - return null; - } - - @Override - public ErrorListenerPathAndBytesable<String> inBackground(Object o) { - return null; - } - - @Override - public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback) { - return null; - } - - @Override - public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback, Object o) { - return null; - } - - @Override - public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback, Executor executor) { - return null; + public ACLCreateModeBackgroundPathAndBytesable<String> withProtection() { + throw new UnsupportedOperationException("Not implemented in MockCurator"); } - @Override - public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback, Object o, Executor executor) { - return null; + public T forPath(String s, byte[] bytes) throws Exception { + throw new UnsupportedOperationException("Not implemented in MockCurator"); } - @Override - public ACLBackgroundPathAndBytesable<String> storingStatIn(Stat stat) { - return null; + public T forPath(String s) throws Exception { + throw new UnsupportedOperationException("Not implemented in MockCurator"); } } - private class MockCreateBuilder implements CreateBuilder { + private class MockCreateBuilder extends MockBackgroundACLPathAndBytesableBuilder<String> implements CreateBuilder { private boolean createParents = false; private CreateMode createMode = CreateMode.PERSISTENT; @Override - public ProtectACLCreateModeStatPathAndBytesable<String> creatingParentsIfNeeded() { + public ProtectACLCreateModePathAndBytesable<String> creatingParentsIfNeeded() { createParents = true; - return new MockProtectACLCreateModeStatPathAndBytesable<>() { - - @Override - public String forPath(String s, byte[] bytes) throws Exception { - return createNode(s, bytes, createParents, createMode, fileSystem.root(), listeners); - } - - @Override - public String forPath(String s) throws Exception { - return createNode(s, new byte[0], createParents, createMode, fileSystem.root(), listeners); - } - - }; + return this; } @Override - public ProtectACLCreateModeStatPathAndBytesable<String> creatingParentContainersIfNeeded() { - return new MockProtectACLCreateModeStatPathAndBytesable<>() { + public ACLCreateModeBackgroundPathAndBytesable<String> withProtection() { + // Protection against the server crashing after creating the file but before returning to the client. + // Not relevant for an in-memory mock, obviously + return this; + } - @Override - public String forPath(String s, byte[] bytes) throws Exception { - return createNode(s, bytes, createParents, createMode, fileSystem.root(), listeners); - } + public ACLBackgroundPathAndBytesable<String> withMode(CreateMode createMode) { + this.createMode = createMode; + return this; + } - @Override - public String forPath(String s) throws Exception { - return createNode(s, new byte[0], createParents, createMode, fileSystem.root(), listeners); - } + @Override + public CreateBackgroundModeACLable compressed() { + throw new UnsupportedOperationException("Not implemented in MockCurator"); + } - }; + @Override + public ProtectACLCreateModePathAndBytesable<String> creatingParentContainersIfNeeded() { + // TODO: Add proper support for container nodes, see https://issues.apache.org/jira/browse/ZOOKEEPER-2163. + return creatingParentsIfNeeded(); } @Override @@ -752,11 +699,6 @@ public class MockCurator extends Curator { throw new UnsupportedOperationException("Not implemented in MockCurator"); } - @Override - public ACLCreateModeStatBackgroundPathAndBytesable<String> withProtection() { - return null; - } - public String forPath(String s) throws Exception { return createNode(s, new byte[0], createParents, createMode, fileSystem.root(), listeners); } @@ -794,50 +736,9 @@ public class MockCurator extends Curator { public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback, Object o, Executor executor) { throw new UnsupportedOperationException("Not implemented in MockCurator"); } - - @Override - public CreateBuilderMain withTtl(long l) { - return null; - } - - @Override - public CreateBuilder2 orSetData() { - return null; - } - - @Override - public CreateBuilder2 orSetData(int i) { - return null; - } - - @Override - public CreateBackgroundModeStatACLable compressed() { - return null; - } - - @Override - public CreateProtectACLCreateModePathAndBytesable<String> storingStatIn(Stat stat) { - return null; - } - - @Override - public BackgroundPathAndBytesable<String> withACL(List<ACL> list) { - return null; - } - - @Override - public ACLBackgroundPathAndBytesable<String> withMode(CreateMode createMode) { - this.createMode = createMode; - return this; - } - - @Override - public BackgroundPathAndBytesable<String> withACL(List<ACL> list, boolean b) { - return null; - } } - private static class MockBackgroundPathableBuilder<T> implements BackgroundPathable<T>, Watchable<BackgroundPathable<T>> { + private class MockBackgroundPathableBuilder<T> implements BackgroundPathable<T>, Watchable<BackgroundPathable<T>> { @Override public ErrorListenerPathable<T> inBackground() { @@ -920,12 +821,7 @@ public class MockCurator extends Curator { } @Override - public ACLableExistBuilderMain creatingParentsIfNeeded() { - throw new UnsupportedOperationException("Not implemented in MockCurator"); - } - - @Override - public ACLableExistBuilderMain creatingParentContainersIfNeeded() { + public ExistsBuilderMain creatingParentContainersIfNeeded() { throw new UnsupportedOperationException("Not implemented in MockCurator"); } } @@ -955,10 +851,6 @@ public class MockCurator extends Curator { return null; } - @Override - public DeleteBuilderMain quietly() { - return this; - } } private class MockGetDataBuilder extends MockBackgroundPathableBuilder<byte[]> implements GetDataBuilder { @@ -968,48 +860,18 @@ public class MockCurator extends Curator { throw new UnsupportedOperationException("Not implemented in MockCurator"); } - public byte[] forPath(String path) throws Exception { - return getData(path, fileSystem.root()); - } - @Override - public ErrorListenerPathable<byte[]> inBackground() { - throw new UnsupportedOperationException("Not implemented in MockCurator"); - } - - @Override - public ErrorListenerPathable<byte[]> inBackground(Object o) { - throw new UnsupportedOperationException("Not implemented in MockCurator"); - } - - @Override - public ErrorListenerPathable<byte[]> inBackground(BackgroundCallback backgroundCallback) { - throw new UnsupportedOperationException("Not implemented in MockCurator"); - } - - @Override - public ErrorListenerPathable<byte[]> inBackground(BackgroundCallback backgroundCallback, Object o) { - throw new UnsupportedOperationException("Not implemented in MockCurator"); - } - - @Override - public ErrorListenerPathable<byte[]> inBackground(BackgroundCallback backgroundCallback, Executor executor) { + public WatchPathable<byte[]> storingStatIn(Stat stat) { throw new UnsupportedOperationException("Not implemented in MockCurator"); } - @Override - public ErrorListenerPathable<byte[]> inBackground(BackgroundCallback backgroundCallback, Object o, Executor executor) { - throw new UnsupportedOperationException("Not implemented in MockCurator"); + public byte[] forPath(String path) throws Exception { + return getData(path, fileSystem.root()); } - @Override - public WatchPathable<byte[]> storingStatIn(Stat stat) { - throw new UnsupportedOperationException("Not implemented in MockCurator"); - } } - // extends MockBackgroundACLPathAndBytesableBuilder<Stat> - private class MockSetDataBuilder implements SetDataBuilder { + private class MockSetDataBuilder extends MockBackgroundACLPathAndBytesableBuilder<Stat> implements SetDataBuilder { @Override public SetDataBackgroundVersionable compressed() { @@ -1028,11 +890,6 @@ public class MockCurator extends Curator { } @Override - public Stat forPath(String s) throws Exception { - return null; - } - - @Override public ErrorListenerPathAndBytesable<Stat> inBackground() { throw new UnsupportedOperationException("Not implemented in MockCurator"); } @@ -1134,6 +991,11 @@ public class MockCurator extends Curator { private CreateMode createMode = CreateMode.PERSISTENT; @Override + public PathAndBytesable<CuratorTransactionBridge> withACL(List<ACL> list) { + throw new UnsupportedOperationException("Not implemented in MockCurator"); + } + + @Override public ACLCreateModePathAndBytesable<CuratorTransactionBridge> compressed() { throw new UnsupportedOperationException("Not implemented in MockCurator"); } @@ -1156,20 +1018,6 @@ public class MockCurator extends Curator { return new MockCuratorTransactionBridge(); } - @Override - public TransactionCreateBuilder2 withTtl(long l) { - return this; - } - - @Override - public Object withACL(List list, boolean b) { - return this; - } - - @Override - public Object withACL(List list) { - return this; - } } private class MockTransactionDeleteBuilder implements TransactionDeleteBuilder { @@ -1307,31 +1155,11 @@ public class MockCurator extends Curator { } @Override - public ReconfigBuilder reconfig() { - throw new UnsupportedOperationException("Not implemented in MockCurator"); - } - - @Override - public GetConfigBuilder getConfig() { - throw new UnsupportedOperationException("Not implemented in MockCurator"); - } - - @Override public CuratorTransaction inTransaction() { return new MockCuratorTransactionFinal(); } @Override - public CuratorMultiTransaction transaction() { - throw new UnsupportedOperationException("Not implemented in MockCurator"); - } - - @Override - public TransactionOp transactionOp() { - throw new UnsupportedOperationException("Not implemented in MockCurator"); - } - - @Override @Deprecated public void sync(String path, Object backgroundContextObject) { throw new UnsupportedOperationException("Not implemented in MockCurator"); @@ -1400,241 +1228,10 @@ public class MockCurator extends Curator { } @Override - public WatcherRemoveCuratorFramework newWatcherRemoveCuratorFramework() { - return new WatcherRemoveCuratorFramework() { - @Override - public void removeWatchers() { - - } - - @Override - public void start() { - - } - - @Override - public void close() { - - } - - @Override - public CuratorFrameworkState getState() { - return null; - } - - @Override - public boolean isStarted() { - return false; - } - - @Override - public CreateBuilder create() { - return null; - } - - @Override - public DeleteBuilder delete() { - return null; - } - - @Override - public ExistsBuilder checkExists() { - return null; - } - - @Override - public GetDataBuilder getData() { - return null; - } - - @Override - public SetDataBuilder setData() { - return null; - } - - @Override - public GetChildrenBuilder getChildren() { - return null; - } - - @Override - public GetACLBuilder getACL() { - return null; - } - - @Override - public SetACLBuilder setACL() { - return null; - } - - @Override - public ReconfigBuilder reconfig() { - return null; - } - - @Override - public GetConfigBuilder getConfig() { - return null; - } - - @Override - public CuratorTransaction inTransaction() { - return null; - } - - @Override - public CuratorMultiTransaction transaction() { - return null; - } - - @Override - public TransactionOp transactionOp() { - return null; - } - - @Override - public void sync(String s, Object o) { - - } - - @Override - public void createContainers(String s) throws Exception { - - } - - @Override - public SyncBuilder sync() { - return null; - } - - @Override - public RemoveWatchesBuilder watches() { - return null; - } - - @Override - public Listenable<ConnectionStateListener> getConnectionStateListenable() { - return null; - } - - @Override - public Listenable<CuratorListener> getCuratorListenable() { - return null; - } - - @Override - public Listenable<UnhandledErrorListener> getUnhandledErrorListenable() { - return null; - } - - @Override - public CuratorFramework nonNamespaceView() { - return null; - } - - @Override - public CuratorFramework usingNamespace(String s) { - return null; - } - - @Override - public String getNamespace() { - return null; - } - - @Override - public CuratorZookeeperClient getZookeeperClient() { - return null; - } - - @Override - public EnsurePath newNamespaceAwareEnsurePath(String s) { - return null; - } - - @Override - public void clearWatcherReferences(Watcher watcher) { - - } - - @Override - public boolean blockUntilConnected(int i, TimeUnit timeUnit) throws InterruptedException { - return false; - } - - @Override - public void blockUntilConnected() throws InterruptedException { - - } - - @Override - public WatcherRemoveCuratorFramework newWatcherRemoveCuratorFramework() { - return null; - } - - @Override - public ConnectionStateErrorPolicy getConnectionStateErrorPolicy() { - return null; - } - - @Override - public QuorumVerifier getCurrentConfig() { - return null; - } - - @Override - public SchemaSet getSchemaSet() { - return null; - } - - @Override - public boolean isZk34CompatibilityMode() { - return false; - } - - @Override - public CompletableFuture<Void> runSafe(Runnable runnable) { - return null; - } - }; - - } - - @Override - public ConnectionStateErrorPolicy getConnectionStateErrorPolicy() { - throw new UnsupportedOperationException("Not implemented in MockCurator"); - } - - @Override - public QuorumVerifier getCurrentConfig() { - throw new UnsupportedOperationException("Not implemented in MockCurator"); - } - - @Override - public SchemaSet getSchemaSet() { - throw new UnsupportedOperationException("Not implemented in MockCurator"); - } - - @Override - public boolean isZk34CompatibilityMode() { - return false; - } - - @Override - public CompletableFuture<Void> runSafe(Runnable runnable) { - throw new UnsupportedOperationException("Not implemented in MockCurator"); - } - - @Override public SyncBuilder sync() { throw new UnsupportedOperationException("Not implemented in MockCurator"); } - - @Override - public RemoveWatchesBuilder watches() { - throw new UnsupportedOperationException("Not implemented in MockCurator"); - } - + } } diff --git a/zkfacade/src/main/java/org/apache/curator/framework/api/package-info.java b/zkfacade/src/main/java/org/apache/curator/framework/api/package-info.java index e3da4ab3efa..be9e84013c1 100644 --- a/zkfacade/src/main/java/org/apache/curator/framework/api/package-info.java +++ b/zkfacade/src/main/java/org/apache/curator/framework/api/package-info.java @@ -1,5 +1,5 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -@ExportPackage(version = @Version(major = 4, minor = 3, micro = 0)) +@ExportPackage(version = @Version(major = 2, minor = 13, micro = 0)) package org.apache.curator.framework.api; import com.yahoo.osgi.annotation.ExportPackage; import com.yahoo.osgi.annotation.Version; diff --git a/zkfacade/src/main/java/org/apache/curator/framework/api/transaction/package-info.java b/zkfacade/src/main/java/org/apache/curator/framework/api/transaction/package-info.java index 94f8b12894e..be3ece0357b 100644 --- a/zkfacade/src/main/java/org/apache/curator/framework/api/transaction/package-info.java +++ b/zkfacade/src/main/java/org/apache/curator/framework/api/transaction/package-info.java @@ -1,5 +1,5 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -@ExportPackage(version = @Version(major = 4, minor = 3, micro = 0)) +@ExportPackage(version = @Version(major = 2, minor = 13, micro = 0)) package org.apache.curator.framework.api.transaction; import com.yahoo.osgi.annotation.ExportPackage; import com.yahoo.osgi.annotation.Version; diff --git a/zkfacade/src/main/java/org/apache/curator/framework/listen/package-info.java b/zkfacade/src/main/java/org/apache/curator/framework/listen/package-info.java index 71ee8ccfff0..79c67cedf75 100644 --- a/zkfacade/src/main/java/org/apache/curator/framework/listen/package-info.java +++ b/zkfacade/src/main/java/org/apache/curator/framework/listen/package-info.java @@ -1,5 +1,5 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -@ExportPackage(version = @Version(major = 4, minor = 3, micro = 0)) +@ExportPackage(version = @Version(major = 2, minor = 13, micro = 0)) package org.apache.curator.framework.listen; import com.yahoo.osgi.annotation.ExportPackage; import com.yahoo.osgi.annotation.Version; diff --git a/zkfacade/src/main/java/org/apache/curator/framework/package-info.java b/zkfacade/src/main/java/org/apache/curator/framework/package-info.java index 2999456bc9d..3e3b8433556 100644 --- a/zkfacade/src/main/java/org/apache/curator/framework/package-info.java +++ b/zkfacade/src/main/java/org/apache/curator/framework/package-info.java @@ -1,5 +1,5 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -@ExportPackage(version = @Version(major = 4, minor = 3, micro = 0)) +@ExportPackage(version = @Version(major = 2, minor = 13, micro = 0)) package org.apache.curator.framework; import com.yahoo.osgi.annotation.ExportPackage; import com.yahoo.osgi.annotation.Version; diff --git a/zkfacade/src/main/java/org/apache/curator/framework/recipes/atomic/package-info.java b/zkfacade/src/main/java/org/apache/curator/framework/recipes/atomic/package-info.java index dd1dd7a1899..a607d5dcda5 100644 --- a/zkfacade/src/main/java/org/apache/curator/framework/recipes/atomic/package-info.java +++ b/zkfacade/src/main/java/org/apache/curator/framework/recipes/atomic/package-info.java @@ -1,5 +1,5 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -@ExportPackage(version = @Version(major = 4, minor = 3, micro = 0)) +@ExportPackage(version = @Version(major = 2, minor = 13, micro = 0)) package org.apache.curator.framework.recipes.atomic; import com.yahoo.osgi.annotation.ExportPackage; import com.yahoo.osgi.annotation.Version; diff --git a/zkfacade/src/main/java/org/apache/curator/framework/recipes/barriers/package-info.java b/zkfacade/src/main/java/org/apache/curator/framework/recipes/barriers/package-info.java index 4e2aea367de..2db4beef75f 100644 --- a/zkfacade/src/main/java/org/apache/curator/framework/recipes/barriers/package-info.java +++ b/zkfacade/src/main/java/org/apache/curator/framework/recipes/barriers/package-info.java @@ -1,5 +1,5 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -@ExportPackage(version = @Version(major = 4, minor = 3, micro = 0)) +@ExportPackage(version = @Version(major = 2, minor = 13, micro = 0)) package org.apache.curator.framework.recipes.barriers; import com.yahoo.osgi.annotation.ExportPackage; import com.yahoo.osgi.annotation.Version; diff --git a/zkfacade/src/main/java/org/apache/curator/framework/recipes/cache/package-info.java b/zkfacade/src/main/java/org/apache/curator/framework/recipes/cache/package-info.java index ad6913d6381..0465bbf2039 100644 --- a/zkfacade/src/main/java/org/apache/curator/framework/recipes/cache/package-info.java +++ b/zkfacade/src/main/java/org/apache/curator/framework/recipes/cache/package-info.java @@ -1,5 +1,5 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -@ExportPackage(version = @Version(major = 4, minor = 3, micro = 0)) +@ExportPackage(version = @Version(major = 2, minor = 13, micro = 0)) package org.apache.curator.framework.recipes.cache; import com.yahoo.osgi.annotation.ExportPackage; import com.yahoo.osgi.annotation.Version; diff --git a/zkfacade/src/main/java/org/apache/curator/framework/recipes/locks/package-info.java b/zkfacade/src/main/java/org/apache/curator/framework/recipes/locks/package-info.java index 4307c09e30a..63b067bcffc 100644 --- a/zkfacade/src/main/java/org/apache/curator/framework/recipes/locks/package-info.java +++ b/zkfacade/src/main/java/org/apache/curator/framework/recipes/locks/package-info.java @@ -1,5 +1,5 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -@ExportPackage(version = @Version(major = 4, minor = 3, micro = 0)) +@ExportPackage(version = @Version(major = 2, minor = 13, micro = 0)) package org.apache.curator.framework.recipes.locks; import com.yahoo.osgi.annotation.ExportPackage; import com.yahoo.osgi.annotation.Version; diff --git a/zkfacade/src/main/java/org/apache/curator/framework/state/package-info.java b/zkfacade/src/main/java/org/apache/curator/framework/state/package-info.java index 4a10e20318d..eec4f00ddb4 100644 --- a/zkfacade/src/main/java/org/apache/curator/framework/state/package-info.java +++ b/zkfacade/src/main/java/org/apache/curator/framework/state/package-info.java @@ -1,5 +1,5 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -@ExportPackage(version = @Version(major = 4, minor = 3, micro = 0)) +@ExportPackage(version = @Version(major = 2, minor = 13, micro = 0)) package org.apache.curator.framework.state; import com.yahoo.osgi.annotation.ExportPackage; import com.yahoo.osgi.annotation.Version; diff --git a/zkfacade/src/main/java/org/apache/curator/package-info.java b/zkfacade/src/main/java/org/apache/curator/package-info.java index 232a5fd46f3..120aa4558d2 100644 --- a/zkfacade/src/main/java/org/apache/curator/package-info.java +++ b/zkfacade/src/main/java/org/apache/curator/package-info.java @@ -1,5 +1,5 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -@ExportPackage(version = @Version(major = 4, minor = 3, micro = 0)) +@ExportPackage(version = @Version(major = 2, minor = 13, micro = 0)) package org.apache.curator; import com.yahoo.osgi.annotation.ExportPackage; import com.yahoo.osgi.annotation.Version; diff --git a/zkfacade/src/main/java/org/apache/curator/retry/package-info.java b/zkfacade/src/main/java/org/apache/curator/retry/package-info.java index f45a0d927a5..98130481c4c 100644 --- a/zkfacade/src/main/java/org/apache/curator/retry/package-info.java +++ b/zkfacade/src/main/java/org/apache/curator/retry/package-info.java @@ -1,5 +1,5 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -@ExportPackage(version = @Version(major = 4, minor = 3, micro = 0)) +@ExportPackage(version = @Version(major = 2, minor = 13, micro = 0)) package org.apache.curator.retry; import com.yahoo.osgi.annotation.ExportPackage; import com.yahoo.osgi.annotation.Version; |