diff options
16 files changed, 409 insertions, 100 deletions
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 9cc83bb4275..08f4e2fa12f 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,12 +14,11 @@ 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.xml.PlatformBundles; import java.util.Set; import java.util.TreeSet; -import static com.yahoo.vespa.defaults.Defaults.getDefaults; - /** * Container implementation for cluster-controllers */ @@ -55,11 +54,12 @@ public class ClusterControllerContainer extends Container implements } addComponent(new AccessLogComponent(AccessLogComponent.AccessLogType.jsonAccessLog, "controller", isHosted)); - addFileBundle("lib/jars/clustercontroller-apps-jar-with-dependencies.jar"); - addFileBundle("lib/jars/clustercontroller-apputil-jar-with-dependencies.jar"); - addFileBundle("lib/jars/clustercontroller-core-jar-with-dependencies.jar"); - addFileBundle("lib/jars/clustercontroller-utils-jar-with-dependencies.jar"); - addFileBundle("lib/jars/zookeeper-server-jar-with-dependencies.jar"); + // TODO: Why are bundles added here instead of in the cluster? + addFileBundle("clustercontroller-apps"); + addFileBundle("clustercontroller-apputil"); + addFileBundle("clustercontroller-core"); + addFileBundle("clustercontroller-utils"); + addFileBundle("zookeeper-server"); } @Override @@ -82,8 +82,8 @@ public class ClusterControllerContainer extends Container implements super.addHandler(h); } - private void addFileBundle(String bundlePath) { - bundles.add("file:" + getDefaults().underVespaHome(bundlePath)); + private void addFileBundle(String bundleName) { + bundles.add(PlatformBundles.absoluteBundlePath(bundleName).toString()); } private ComponentModel createComponentModel(String id, String className, ComponentSpecification bundle) { @@ -103,7 +103,7 @@ public class ClusterControllerContainer extends Container implements @Override public void getConfig(PlatformBundlesConfig.Builder builder) { - bundles.forEach(builder::bundles); + bundles.forEach(builder::bundlePaths); } @Override 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 e2d0868cb9d..240157fb7aa 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 @@ -69,8 +69,6 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; -import static com.yahoo.container.core.BundleLoaderProperties.DISK_BUNDLE_PREFIX; - /** * Parent class for all container cluster types. * @@ -474,12 +472,9 @@ public abstract class ContainerCluster<CONTAINER extends Container> @Override public void getConfig(PlatformBundlesConfig.Builder builder) { - platformBundles.stream() .map(ContainerCluster::toFileReferenceString) - .forEach(builder::bundles); - } - - private static String toFileReferenceString(Path path) { - return DISK_BUNDLE_PREFIX + path.toString(); + platformBundles.stream() + .map(Path::toString) + .forEach(builder::bundlePaths); } @Override diff --git a/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerClusterTest.java index 754f4b66070..34f06519ac9 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerClusterTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerClusterTest.java @@ -58,8 +58,8 @@ public class MetricsProxyContainerClusterTest { var builder = new PlatformBundlesConfig.Builder(); model.getConfig(builder, CLUSTER_CONFIG_ID); PlatformBundlesConfig config = builder.build(); - assertEquals(1, config.bundles().size()); - assertThat(config.bundles(0).value(), endsWith(METRICS_PROXY_BUNDLE_FILE.toString())); + assertEquals(1, config.bundlePaths().size()); + assertThat(config.bundlePaths(0), endsWith(METRICS_PROXY_BUNDLE_FILE.toString())); } @Test diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java index 07b6cd72bdb..97359b392a5 100755 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java @@ -4,7 +4,6 @@ package com.yahoo.vespa.model.container; import com.yahoo.cloud.config.ClusterInfoConfig; import com.yahoo.cloud.config.ConfigserverConfig; import com.yahoo.cloud.config.RoutingProviderConfig; -import com.yahoo.config.FileReference; import com.yahoo.config.application.api.DeployLogger; import com.yahoo.config.model.deploy.DeployState; import com.yahoo.config.model.deploy.TestProperties; @@ -294,7 +293,7 @@ public class ContainerClusterTest { ApplicationContainerCluster cluster = new ApplicationContainerCluster(root, "container0", "container1", state); var bundleBuilder = new PlatformBundlesConfig.Builder(); cluster.getConfig(bundleBuilder); - List<String> installedBundles = bundleBuilder.build().bundles().stream().map(FileReference::value).collect(Collectors.toList()); + List<String> installedBundles = bundleBuilder.build().bundlePaths(); assertEquals(expectedBundleNames.size(), installedBundles.size()); assertThat(installedBundles, containsInAnyOrder( diff --git a/container-core/src/main/java/com/yahoo/container/core/BundleLoaderProperties.java b/container-core/src/main/java/com/yahoo/container/core/BundleLoaderProperties.java deleted file mode 100644 index ee12c7d4c9f..00000000000 --- a/container-core/src/main/java/com/yahoo/container/core/BundleLoaderProperties.java +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.core; - -/** - * @author gjoranv - */ -public interface BundleLoaderProperties { - - // TODO: This should be removed. The prefix is used to separate the bundles in BundlesConfig - // into those that are transferred with filedistribution and those that are preinstalled - // on disk. Instead, the model should have put them in two different configs. I.e. create a new - // config 'preinstalled-bundles.def'. - String DISK_BUNDLE_PREFIX = "file:"; - -} diff --git a/container-core/src/main/java/com/yahoo/container/core/config/DiskBundleInstaller.java b/container-core/src/main/java/com/yahoo/container/core/config/DiskBundleInstaller.java index 3edabe9f861..a4cce2e38db 100644 --- a/container-core/src/main/java/com/yahoo/container/core/config/DiskBundleInstaller.java +++ b/container-core/src/main/java/com/yahoo/container/core/config/DiskBundleInstaller.java @@ -1,28 +1,21 @@ // Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.container.core.config; -import com.yahoo.config.FileReference; import com.yahoo.osgi.Osgi; import org.osgi.framework.Bundle; import java.io.File; import java.util.List; -import static com.yahoo.container.core.BundleLoaderProperties.DISK_BUNDLE_PREFIX; - /** * @author gjoranv */ -public class DiskBundleInstaller implements BundleInstaller { - - @Override - public List<Bundle> installBundles(FileReference reference, Osgi osgi) { - assert(reference.value().startsWith(DISK_BUNDLE_PREFIX)); - String referenceFileName = reference.value().substring(DISK_BUNDLE_PREFIX.length()); +public class DiskBundleInstaller { - File file = new File(referenceFileName); + public List<Bundle> installBundles(String bundlePath, Osgi osgi) { + File file = new File(bundlePath); if ( ! file.exists()) { - throw new IllegalArgumentException("Reference '" + reference.value() + "' not found on disk."); + throw new IllegalArgumentException("Bundle file '" + bundlePath + "' not found on disk."); } return osgi.install(file.getAbsolutePath()); diff --git a/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java b/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java index 80c39bef2cb..a58dff13f09 100644 --- a/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java +++ b/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java @@ -135,9 +135,9 @@ public class HandlersConfigurerDi { } @Override - public void installPlatformBundles(Collection<FileReference> bundles) { + public void installPlatformBundles(Collection<String> bundlePaths) { log.fine("Installing platform bundles."); - platformBundleLoader.useBundles(new ArrayList<>(bundles)); + platformBundleLoader.useBundles(new ArrayList<>(bundlePaths)); } @Override diff --git a/container-core/src/main/java/com/yahoo/container/core/config/PlatformBundleLoader.java b/container-core/src/main/java/com/yahoo/container/core/config/PlatformBundleLoader.java index f922bc0cb25..0ab89e223f6 100644 --- a/container-core/src/main/java/com/yahoo/container/core/config/PlatformBundleLoader.java +++ b/container-core/src/main/java/com/yahoo/container/core/config/PlatformBundleLoader.java @@ -1,6 +1,5 @@ package com.yahoo.container.core.config; -import com.yahoo.config.FileReference; import com.yahoo.osgi.Osgi; import org.osgi.framework.Bundle; @@ -10,8 +9,13 @@ import java.util.Set; import java.util.logging.Logger; /** - * Installs all platform bundles, using the {@link DiskBundleInstaller}. + * Used to install the bundles that are added as platform bundles by the config-model. + * * All platform bundles reside on disk, and they are never uninstalled. + * Platform bundles are allowed to pre-install other bundles on disk via the + * X-JDisc-Preinstall-Bundle manifest header. + * + * Attempts to install additional bundles, after the first call, should be a NOP. * * @author gjoranv */ @@ -21,33 +25,47 @@ public class PlatformBundleLoader { private final Osgi osgi; private final DiskBundleInstaller installer; + private Set<Bundle> installedBundles; + private boolean hasLoadedBundles = false; + public PlatformBundleLoader(Osgi osgi) { + this(osgi, new DiskBundleInstaller()); + } + + PlatformBundleLoader(Osgi osgi, DiskBundleInstaller installer) { this.osgi = osgi; - installer = new DiskBundleInstaller(); + this.installer = installer; } - public void useBundles(List<FileReference> fileReferences) { - Set<Bundle> installedBundles = install(fileReferences); + public void useBundles(List<String> bundlePaths) { + if (hasLoadedBundles) { + log.fine(() -> "Platform bundles have already been installed." + + "\nInstalled bundles: " + installedBundles + + "\nGiven files: " + bundlePaths); + return; + } + installedBundles = install(bundlePaths); BundleStarter.startBundles(installedBundles); + hasLoadedBundles = true; } - private Set<Bundle> install(List<FileReference> bundlesToInstall) { - var installedBundles = new LinkedHashSet<Bundle>(); - for (FileReference reference : bundlesToInstall) { + private Set<Bundle> install(List<String> bundlesToInstall) { + var allInstalled = new LinkedHashSet<Bundle>(); + for (String bundlePath : bundlesToInstall) { try { - installedBundles.addAll(installBundleFromDisk(reference)); + allInstalled.addAll(installBundleFromDisk(bundlePath)); } catch(Exception e) { - throw new RuntimeException("Could not install bundle '" + reference + "'", e); + throw new RuntimeException("Could not install bundle '" + bundlePath + "'", e); } } - return installedBundles; + return allInstalled; } - private List<Bundle> installBundleFromDisk(FileReference reference) { - log.info("Installing bundle from disk with reference '" + reference.value() + "'"); - List<Bundle> bundles = installer.installBundles(reference, osgi); - log.fine("Installed " + bundles.size() + " bundles for file reference " + reference); + private List<Bundle> installBundleFromDisk(String bundlePath) { + log.info("Installing bundle from disk: " + bundlePath); + List<Bundle> bundles = installer.installBundles(bundlePath, osgi); + log.fine("Installed " + bundles.size() + " bundles for file " + bundlePath); return bundles; } diff --git a/container-core/src/test/java/com/yahoo/container/core/config/PlatformBundleLoaderTest.java b/container-core/src/test/java/com/yahoo/container/core/config/PlatformBundleLoaderTest.java new file mode 100644 index 00000000000..931b0c547fd --- /dev/null +++ b/container-core/src/test/java/com/yahoo/container/core/config/PlatformBundleLoaderTest.java @@ -0,0 +1,64 @@ +package com.yahoo.container.core.config; + +import com.yahoo.osgi.Osgi; +import org.junit.Before; +import org.junit.Test; +import org.osgi.framework.Bundle; + +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @author gjoranv + */ +public class PlatformBundleLoaderTest { + + private static final String BUNDLE_1_REF = "bundle-1"; + private static final Bundle BUNDLE_1 = new TestBundle(BUNDLE_1_REF); + private static final String BUNDLE_2_REF = "bundle-2"; + private static final Bundle BUNDLE_2 = new TestBundle(BUNDLE_2_REF); + + private PlatformBundleLoader bundleLoader; + private TestOsgi osgi; + + @Before + public void setup() { + osgi = new TestOsgi(testBundles()); + bundleLoader = new PlatformBundleLoader(osgi, new TestBundleInstaller()); + } + + @Test + public void bundles_are_installed_and_started() { + bundleLoader.useBundles(List.of(BUNDLE_1_REF)); + assertEquals(1, osgi.getInstalledBundles().size()); + + // The bundle is installed and started + TestBundle installedBundle = (TestBundle)osgi.getInstalledBundles().get(0); + assertEquals(BUNDLE_1.getSymbolicName(), installedBundle.getSymbolicName()); + assertTrue(installedBundle.started); + } + + @Test + public void bundles_cannot_be_added_by_later_calls() { + bundleLoader.useBundles(List.of(BUNDLE_1_REF)); + bundleLoader.useBundles(List.of(BUNDLE_2_REF)); // Should be a NOP + + assertEquals(1, osgi.getInstalledBundles().size()); + assertEquals(BUNDLE_1.getSymbolicName(), osgi.getInstalledBundles().get(0).getSymbolicName()); + } + + private static Map<String, Bundle> testBundles() { + return Map.of(BUNDLE_1_REF, BUNDLE_1, + BUNDLE_2_REF, BUNDLE_2); + } + + static class TestBundleInstaller extends DiskBundleInstaller { + @Override + public List<Bundle> installBundles(String bundlePath, Osgi osgi) { + return osgi.install(bundlePath); + } + } +} diff --git a/container-di/src/main/java/com/yahoo/container/di/Container.java b/container-di/src/main/java/com/yahoo/container/di/Container.java index 672cef22010..af580767a17 100644 --- a/container-di/src/main/java/com/yahoo/container/di/Container.java +++ b/container-di/src/main/java/com/yahoo/container/di/Container.java @@ -4,11 +4,11 @@ package com.yahoo.container.di; import com.google.inject.Injector; import com.yahoo.config.ConfigInstance; import com.yahoo.config.ConfigurationRuntimeException; -import com.yahoo.config.FileReference; import com.yahoo.config.subscription.ConfigInterruptedException; import com.yahoo.container.ComponentsConfig; import com.yahoo.container.bundle.BundleInstantiationSpecification; import com.yahoo.container.di.ConfigRetriever.BootstrapConfigs; +import com.yahoo.container.di.ConfigRetriever.ComponentsConfigs; import com.yahoo.container.di.ConfigRetriever.ConfigSnapshot; import com.yahoo.container.di.componentgraph.core.ComponentGraph; import com.yahoo.container.di.componentgraph.core.ComponentNode; @@ -50,7 +50,7 @@ public class Container { private final Osgi osgi; private final ConfigRetriever configurer; - private List<FileReference> platformBundles; // Used to verify that platform bundles don't change. + private List<String> platformBundles; // Used to verify that platform bundles don't change. private long previousConfigGeneration = -1L; private long leastGeneration = -1L; @@ -106,7 +106,7 @@ public class Container { log.log(FINE, "Got new bootstrap generation\n" + configGenerationsString()); if (graph.generation() == 0) { - platformBundles = getConfig(platformBundlesConfigKey, snapshot.configs()).bundles(); + platformBundles = getConfig(platformBundlesConfigKey, snapshot.configs()).bundlePaths(); osgi.installPlatformBundles(platformBundles); } else { throwIfPlatformBundlesChanged(snapshot); @@ -118,7 +118,7 @@ public class Container { // Continues loop - } else if (snapshot instanceof ConfigRetriever.ComponentsConfigs) { + } else if (snapshot instanceof ComponentsConfigs) { break; } } @@ -140,7 +140,7 @@ public class Container { } private void throwIfPlatformBundlesChanged(ConfigSnapshot snapshot) { - var checkPlatformBundles = getConfig(platformBundlesConfigKey, snapshot.configs()).bundles(); + var checkPlatformBundles = getConfig(platformBundlesConfigKey, snapshot.configs()).bundlePaths(); if (! checkPlatformBundles.equals(platformBundles)) throw new RuntimeException("Platform bundles are not allowed to change!\nOld: " + platformBundles + "\nNew: " + checkPlatformBundles); } diff --git a/container-di/src/main/java/com/yahoo/container/di/Osgi.java b/container-di/src/main/java/com/yahoo/container/di/Osgi.java index c9ca256b5e0..940986e2f38 100644 --- a/container-di/src/main/java/com/yahoo/container/di/Osgi.java +++ b/container-di/src/main/java/com/yahoo/container/di/Osgi.java @@ -25,8 +25,8 @@ public interface Osgi { return new BundleClasses(new MockBundle(), Collections.emptySet()); } - default void installPlatformBundles(Collection<FileReference> bundles) { - System.out.println("installPlatformBundles " + bundles.stream().map(Object::toString).collect(Collectors.joining(", "))); + default void installPlatformBundles(Collection<String> bundlePaths) { + System.out.println("installPlatformBundles " + bundlePaths); } /** diff --git a/container-di/src/main/resources/configdefinitions/platform-bundles.def b/container-di/src/main/resources/configdefinitions/platform-bundles.def index 9b72bf6831e..a30a846b565 100644 --- a/container-di/src/main/resources/configdefinitions/platform-bundles.def +++ b/container-di/src/main/resources/configdefinitions/platform-bundles.def @@ -1,5 +1,5 @@ # Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package=com.yahoo.container.di.config -# References to platform bundles to install. -bundles[] file +# Paths to platform bundles to install. +bundlePaths[] string diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirer.java index 3f8cc58540d..2afbb0e6476 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirer.java @@ -3,7 +3,6 @@ package com.yahoo.vespa.hosted.provision.maintenance; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; -import com.yahoo.config.provision.Flavor; import com.yahoo.config.provision.NodeType; import com.yahoo.config.provision.Zone; import com.yahoo.jdisc.Metric; @@ -23,24 +22,21 @@ import java.util.stream.Collectors; /** * This moves expired failed nodes: - * <ul> - * <li>To parked: If the node has known hardware failure, Docker hosts are moved to parked only when all of their - * children are already in parked - * <li>To dirty: If the node has failed less than 5 times OR the environment is dev, test or perf. - * Those environments have no protection against users running bogus applications, so - * we cannot use the node failure count to conclude the node has a failure. - * <li>Otherwise the node will remain in failed - * </ul> + * + * - To parked: If the node has known hardware failure, Docker hosts are moved to parked only when all of their + * children are already in parked. + * - To dirty: If the node is a host and has failed less than 5 times, or always if the node is a child. + * - Otherwise the node will remain in failed. + * * Failed content nodes are given a long expiry time to enable us to manually moved them back to * active to recover data in cases where the node was failed accidentally. - * <p> + * * Failed container (Vespa, not Docker) nodes are expired early as there's no data to potentially recover. - * </p> - * <p> + * * The purpose of the automatic recycling to dirty + fail count is that nodes which were moved * to failed due to some undetected hardware failure will end up being failed again. * When that has happened enough they will not be recycled. - * <p> + * * Nodes with detected hardware issues will not be recycled. * * @author bratseth @@ -125,8 +121,7 @@ public class FailedExpirer extends NodeRepositoryMaintainer { /** Returns whether the current node fail count should be used as an indicator of hardware issue */ private boolean failCountIndicatesHardwareIssue(Node node) { - if (node.flavor().getType() == Flavor.Type.DOCKER_CONTAINER) return false; - return (zone.environment() == Environment.prod || zone.environment() == Environment.staging) && - node.status().failCount() >= maxAllowedFailures; + return node.type().isHost() && node.status().failCount() >= maxAllowedFailures; } + } diff --git a/searchlib/src/tests/common/location/geo_location_test.cpp b/searchlib/src/tests/common/location/geo_location_test.cpp index 8093ea61697..31b844d0fc8 100644 --- a/searchlib/src/tests/common/location/geo_location_test.cpp +++ b/searchlib/src/tests/common/location/geo_location_test.cpp @@ -9,6 +9,15 @@ using search::common::GeoLocation; using search::common::GeoLocationParser; +using Box = search::common::GeoLocation::Box; +using Point = search::common::GeoLocation::Point; +using Range = search::common::GeoLocation::Range; +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); @@ -20,7 +29,7 @@ GeoLocation parse(const char *str) { return parser.getGeoLocation(); } -TEST(GeoLocationTest, malformed_bounding_boxes_are_not_parseable) { +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]")); EXPECT_FALSE(is_parseable("[1,10,20,30,40]")); @@ -31,7 +40,7 @@ TEST(GeoLocationTest, malformed_bounding_boxes_are_not_parseable) { EXPECT_FALSE(is_parseable("[10,20,30,40]")); } -TEST(GeoLocationTest, malformed_circles_are_not_parseable) { +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)")); EXPECT_FALSE(is_parseable("(1,10,20,5,0,0,0)")); @@ -43,7 +52,7 @@ TEST(GeoLocationTest, malformed_circles_are_not_parseable) { EXPECT_FALSE(is_parseable("(10,20,5)")); } -TEST(GeoLocationTest, bounding_boxes_can_be_parsed) { +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()); @@ -57,7 +66,7 @@ TEST(GeoLocationTest, bounding_boxes_can_be_parsed) { EXPECT_EQ(40, loc.bounding_box.y.high); } -TEST(GeoLocationTest, circles_can_be_parsed) { +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()); @@ -71,7 +80,7 @@ TEST(GeoLocationTest, circles_can_be_parsed) { EXPECT_EQ(25, loc.bounding_box.y.high); } -TEST(GeoLocationTest, circles_can_have_aspect_ratio) { +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()); @@ -85,7 +94,7 @@ TEST(GeoLocationTest, circles_can_have_aspect_ratio) { EXPECT_EQ(25, loc.bounding_box.y.high); } -TEST(GeoLocationTest, bounding_box_can_be_specified_after_circle) { +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()); @@ -99,7 +108,7 @@ TEST(GeoLocationTest, bounding_box_can_be_specified_after_circle) { EXPECT_EQ(25, loc.bounding_box.y.high); } -TEST(GeoLocationTest, circles_can_be_specified_after_bounding_box) { +TEST(GeoLocationParserTest, circles_can_be_specified_after_bounding_box) { auto loc = parse("[2,10,20,30,40](2,10,20,5,0,0,0)"); EXPECT_EQ(true, loc.has_point); EXPECT_EQ(true, loc.bounding_box.active()); @@ -113,13 +122,13 @@ TEST(GeoLocationTest, circles_can_be_specified_after_bounding_box) { EXPECT_EQ(25, loc.bounding_box.y.high); } -TEST(GeoLocationTest, santa_search_gives_non_wrapped_bounding_box) { +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); } -TEST(GeoLocationTest, near_boundary_search_gives_non_wrapped_bounding_box) { +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); @@ -133,4 +142,253 @@ TEST(GeoLocationTest, near_boundary_search_gives_non_wrapped_bounding_box) { EXPECT_EQ(std::numeric_limits<int32_t>::min(), loc2.bounding_box.y.low); } +void check_box(const GeoLocation &location, Box expected) +{ + int32_t lx = expected.x.low; + int32_t hx = expected.x.high; + int32_t ly = expected.y.low; + int32_t hy = expected.y.high; + EXPECT_TRUE(location.inside_limit(Point{lx,ly})); + EXPECT_TRUE(location.inside_limit(Point{lx,hy})); + EXPECT_TRUE(location.inside_limit(Point{hx,ly})); + EXPECT_TRUE(location.inside_limit(Point{hx,hy})); + + EXPECT_FALSE(location.inside_limit(Point{lx,ly-1})); + EXPECT_FALSE(location.inside_limit(Point{lx,hy+1})); + EXPECT_FALSE(location.inside_limit(Point{lx-1,ly})); + EXPECT_FALSE(location.inside_limit(Point{lx-1,hy})); + EXPECT_FALSE(location.inside_limit(Point{hx,ly-1})); + EXPECT_FALSE(location.inside_limit(Point{hx,hy+1})); + EXPECT_FALSE(location.inside_limit(Point{hx+1,ly})); + EXPECT_FALSE(location.inside_limit(Point{hx+1,hy})); + + EXPECT_FALSE(location.inside_limit(Point{plus_inf,plus_inf})); + EXPECT_FALSE(location.inside_limit(Point{minus_inf,minus_inf})); +} + +TEST(GeoLocationTest, invalid_location) { + GeoLocation invalid; + EXPECT_FALSE(invalid.valid()); + EXPECT_FALSE(invalid.has_radius()); + EXPECT_FALSE(invalid.can_limit()); + EXPECT_FALSE(invalid.has_point); + EXPECT_FALSE(invalid.bounding_box.active()); + EXPECT_FALSE(invalid.x_aspect.active()); + + EXPECT_EQ(invalid.sq_distance_to(Point{0,0}), 0); + EXPECT_EQ(invalid.sq_distance_to(Point{999999,999999}), 0); + EXPECT_EQ(invalid.sq_distance_to(Point{-999999,-999999}), 0); + EXPECT_EQ(invalid.sq_distance_to(Point{plus_inf,plus_inf}), 0); + EXPECT_EQ(invalid.sq_distance_to(Point{minus_inf,minus_inf}), 0); + + EXPECT_TRUE(invalid.inside_limit(Point{0,0})); + EXPECT_TRUE(invalid.inside_limit(Point{999999,999999})); + EXPECT_TRUE(invalid.inside_limit(Point{-999999,-999999})); + EXPECT_TRUE(invalid.inside_limit(Point{plus_inf,plus_inf})); + EXPECT_TRUE(invalid.inside_limit(Point{minus_inf,minus_inf})); +} + +TEST(GeoLocationTest, point_location) { + GeoLocation location(Point{300,-400}); + EXPECT_TRUE(location.valid()); + EXPECT_FALSE(location.has_radius()); + EXPECT_FALSE(location.can_limit()); + EXPECT_TRUE(location.has_point); + EXPECT_FALSE(location.bounding_box.active()); + EXPECT_FALSE(location.x_aspect.active()); + + EXPECT_EQ(location.sq_distance_to(Point{0,0}), 500*500); + EXPECT_EQ(location.sq_distance_to(Point{300,-400}), 0); + EXPECT_EQ(location.sq_distance_to(Point{300,400}), 640000); + + EXPECT_TRUE(location.inside_limit(Point{0,0})); + EXPECT_TRUE(location.inside_limit(Point{999999,999999})); + EXPECT_TRUE(location.inside_limit(Point{-999999,-999999})); + EXPECT_TRUE(location.inside_limit(Point{plus_inf,plus_inf})); + EXPECT_TRUE(location.inside_limit(Point{minus_inf,minus_inf})); +} + +TEST(GeoLocationTest, point_and_radius) { + GeoLocation location(Point{300,-400}, 500); + EXPECT_TRUE(location.valid()); + EXPECT_TRUE(location.has_radius()); + EXPECT_TRUE(location.can_limit()); + EXPECT_TRUE(location.has_point); + EXPECT_TRUE(location.bounding_box.active()); + EXPECT_FALSE(location.x_aspect.active()); + + EXPECT_EQ(location.radius, 500); + + EXPECT_EQ(location.sq_distance_to(Point{0,0}), 500*500); + EXPECT_EQ(location.sq_distance_to(Point{300,-400}), 0); + EXPECT_EQ(location.sq_distance_to(Point{300,400}), 640000); + + EXPECT_TRUE(location.inside_limit(Point{0,0})); + EXPECT_TRUE(location.inside_limit(Point{-200,-400})); + EXPECT_TRUE(location.inside_limit(Point{800,-400})); + EXPECT_TRUE(location.inside_limit(Point{300,-400})); + EXPECT_TRUE(location.inside_limit(Point{300,100})); + EXPECT_TRUE(location.inside_limit(Point{300,-900})); + + check_box(location, Box{Range{0,600},{-800,0}}); +} + +TEST(GeoLocationTest, point_and_aspect) { + GeoLocation location(Point{600,400}, Aspect{0.5}); + // same: GeoLocation location(Point{600,400}, Aspect{1ul << 31}); + EXPECT_TRUE(location.valid()); + EXPECT_FALSE(location.has_radius()); + EXPECT_FALSE(location.can_limit()); + EXPECT_TRUE(location.has_point); + EXPECT_FALSE(location.bounding_box.active()); + EXPECT_TRUE(location.x_aspect.active()); + EXPECT_EQ(location.x_aspect.multiplier, 1ul << 31); + + EXPECT_EQ(location.sq_distance_to(Point{0,0}), 500*500); + EXPECT_EQ(location.sq_distance_to(Point{600,400}), 0); + EXPECT_EQ(location.sq_distance_to(Point{1200,800}), 500*500); + + EXPECT_TRUE(location.inside_limit(Point{0,0})); + EXPECT_TRUE(location.inside_limit(Point{999999,999999})); + EXPECT_TRUE(location.inside_limit(Point{-999999,-999999})); + EXPECT_TRUE(location.inside_limit(Point{plus_inf,plus_inf})); + EXPECT_TRUE(location.inside_limit(Point{minus_inf,minus_inf})); +} + +TEST(GeoLocationTest, point_radius_and_aspect) { + GeoLocation location(Point{1200,400}, 500, Aspect{0.25}); + EXPECT_TRUE(location.valid()); + EXPECT_TRUE(location.has_radius()); + EXPECT_TRUE(location.can_limit()); + EXPECT_TRUE(location.has_point); + EXPECT_TRUE(location.bounding_box.active()); + EXPECT_TRUE(location.x_aspect.active()); + EXPECT_EQ(location.x_aspect.multiplier, 1ul << 30); + + EXPECT_EQ(location.radius, 500); + + EXPECT_EQ(location.sq_distance_to(Point{0,0}), 500*500); + EXPECT_EQ(location.sq_distance_to(Point{1200,400}), 0); + EXPECT_EQ(location.sq_distance_to(Point{1240,400}), 100); + + EXPECT_TRUE(location.inside_limit(Point{1200,400})); + EXPECT_TRUE(location.inside_limit(Point{0,0})); + EXPECT_TRUE(location.inside_limit(Point{2400,0})); + EXPECT_TRUE(location.inside_limit(Point{2400,800})); + EXPECT_TRUE(location.inside_limit(Point{0,800})); + // note: must be 4 outside since 3*0.25 may be truncated to 0 + EXPECT_FALSE(location.inside_limit(Point{-4,0})); + EXPECT_FALSE(location.inside_limit(Point{-4,800})); + EXPECT_FALSE(location.inside_limit(Point{2404,0})); + EXPECT_FALSE(location.inside_limit(Point{2404,800})); + EXPECT_FALSE(location.inside_limit(Point{2400,-1})); + EXPECT_FALSE(location.inside_limit(Point{2400,801})); + EXPECT_FALSE(location.inside_limit(Point{0,-1})); + EXPECT_FALSE(location.inside_limit(Point{0,801})); + EXPECT_FALSE(location.inside_limit(Point{plus_inf,plus_inf})); + EXPECT_FALSE(location.inside_limit(Point{minus_inf,minus_inf})); +} + +TEST(GeoLocationTest, box_location) { + Box mybox{Range{300,350},Range{400,450}}; + GeoLocation location(mybox); + EXPECT_TRUE(location.valid()); + EXPECT_FALSE(location.has_radius()); + EXPECT_TRUE(location.can_limit()); + EXPECT_FALSE(location.has_point); + EXPECT_TRUE(location.bounding_box.active()); + EXPECT_FALSE(location.x_aspect.active()); + + // currently does not measure distance outside box: + EXPECT_EQ(location.sq_distance_to(Point{0,0}), 0); + EXPECT_EQ(location.sq_distance_to(Point{300,400}), 0); + EXPECT_EQ(location.sq_distance_to(Point{350,450}), 0); + EXPECT_EQ(location.sq_distance_to(Point{450,550}), 0); + + EXPECT_TRUE(location.inside_limit(Point{333,444})); + EXPECT_FALSE(location.inside_limit(Point{0,0})); + check_box(location, mybox); +} + +TEST(GeoLocationTest, box_and_point) { + Box mybox{Range{287,343},Range{366,401}}; + GeoLocation location(mybox, Point{300,400}); + EXPECT_TRUE(location.valid()); + EXPECT_FALSE(location.has_radius()); + EXPECT_TRUE(location.can_limit()); + EXPECT_TRUE(location.has_point); + EXPECT_TRUE(location.bounding_box.active()); + EXPECT_FALSE(location.x_aspect.active()); + + EXPECT_EQ(location.sq_distance_to(Point{0,0}), 500*500); + EXPECT_EQ(location.sq_distance_to(Point{300,400}), 0); + EXPECT_EQ(location.sq_distance_to(Point{300,423}), 23*23); + + check_box(location, mybox); +} + +TEST(GeoLocationTest, box_point_and_aspect) { + Box mybox{Range{-1000,350},Range{-1000,600}}; + GeoLocation location(mybox, Point{600,400}, Aspect{0.5}); + EXPECT_TRUE(location.valid()); + EXPECT_FALSE(location.has_radius()); + EXPECT_TRUE(location.can_limit()); + EXPECT_TRUE(location.has_point); + EXPECT_TRUE(location.bounding_box.active()); + EXPECT_TRUE(location.x_aspect.active()); + + EXPECT_EQ(location.sq_distance_to(Point{0,0}), 500*500); + EXPECT_EQ(location.sq_distance_to(Point{600,400}), 0); + EXPECT_EQ(location.sq_distance_to(Point{600,407}), 7*7); + EXPECT_EQ(location.sq_distance_to(Point{614,400}), 7*7); + + check_box(location, mybox); +} + +TEST(GeoLocationTest, box_point_and_radius) { + Box mybox{Range{-1000,350},Range{-1000,600}}; + GeoLocation location(mybox, Point{300,400}, 500); + EXPECT_TRUE(location.valid()); + EXPECT_TRUE(location.has_radius()); + EXPECT_TRUE(location.can_limit()); + EXPECT_TRUE(location.has_point); + EXPECT_TRUE(location.bounding_box.active()); + EXPECT_FALSE(location.x_aspect.active()); + + EXPECT_EQ(location.radius, 500); + + EXPECT_EQ(location.sq_distance_to(Point{0,0}), 500*500); + EXPECT_EQ(location.sq_distance_to(Point{300,400}), 0); + EXPECT_EQ(location.sq_distance_to(Point{300,423}), 23*23); + + EXPECT_EQ(location.bounding_box.x.low, -200); + EXPECT_EQ(location.bounding_box.y.low, -100); + EXPECT_EQ(location.bounding_box.x.high, 350); + EXPECT_EQ(location.bounding_box.y.high, 600); +} + +TEST(GeoLocationTest, box_point_radius_and_aspect) { + Box mybox{Range{-1000,650},Range{-1000,700}}; + GeoLocation location(mybox, Point{600,400}, 500, Aspect{0.5}); + EXPECT_TRUE(location.valid()); + EXPECT_TRUE(location.has_radius()); + EXPECT_TRUE(location.can_limit()); + EXPECT_TRUE(location.has_point); + EXPECT_TRUE(location.bounding_box.active()); + EXPECT_TRUE(location.x_aspect.active()); + + EXPECT_EQ(location.radius, 500); + + EXPECT_EQ(location.sq_distance_to(Point{0,0}), 500*500); + EXPECT_EQ(location.sq_distance_to(Point{600,400}), 0); + EXPECT_EQ(location.sq_distance_to(Point{600,407}), 7*7); + EXPECT_EQ(location.sq_distance_to(Point{614,400}), 7*7); + + EXPECT_GE(location.bounding_box.x.low, -402); + EXPECT_LE(location.bounding_box.x.low, -400); + EXPECT_EQ(location.bounding_box.y.low, -100); + EXPECT_EQ(location.bounding_box.x.high, 650); + EXPECT_EQ(location.bounding_box.y.high, 700); +} + GTEST_MAIN_RUN_ALL_TESTS() diff --git a/searchlib/src/vespa/searchlib/common/geo_location.h b/searchlib/src/vespa/searchlib/common/geo_location.h index 5d04a09142a..261951caf3e 100644 --- a/searchlib/src/vespa/searchlib/common/geo_location.h +++ b/searchlib/src/vespa/searchlib/common/geo_location.h @@ -28,6 +28,8 @@ struct GeoLocation uint32_t multiplier; Aspect() : multiplier(0) {} Aspect(uint32_t multiplier_in) : multiplier(multiplier_in) {} + // for unit tests: + Aspect(double multiplier_in) : multiplier(multiplier_in*4294967296.0) {} bool active() const { return multiplier != 0; } }; struct Range { diff --git a/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneSubscriberTest.java b/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneSubscriberTest.java index 9ba1a56083a..8ca20fdf7cd 100644 --- a/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneSubscriberTest.java +++ b/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneSubscriberTest.java @@ -48,7 +48,7 @@ public class StandaloneSubscriberTest { ApplicationBundlesConfig applicationBundlesConfig = (ApplicationBundlesConfig) config.get(applicationBundlesKey); ComponentsConfig componentsConfig = (ComponentsConfig) config.get(componentsKey); - assertThat(platformBundlesConfig.bundles().size(), is(0)); + assertThat(platformBundlesConfig.bundlePaths().size(), is(0)); assertThat(applicationBundlesConfig.bundles().size(), is(0)); assertThat(componentsConfig.components().size(), greaterThan(10)); return null; |