From 45265ed02ff07b7d3a33122f3a43bca5a40fffab Mon Sep 17 00:00:00 2001 From: gjoranv Date: Wed, 16 Nov 2022 23:07:31 +0100 Subject: Add test for enclave nodes, and necessary supporting infrastructure --- .../noderepository/RealNodeRepositoryTest.java | 3 +- .../provision/node/filter/CloudAccountFilter.java | 29 +++++++++ .../provision/testutils/ContainerConfig.java | 71 ++++++++++++---------- .../provision/testutils/MockNodeRepository.java | 45 +++++++++----- .../provision/restapi/LoadBalancersV1ApiTest.java | 3 +- .../provision/restapi/NodesV2ApiEnclaveTest.java | 36 +++++++++++ .../hosted/provision/restapi/NodesV2ApiTest.java | 3 +- .../hosted/provision/restapi/RestApiTester.java | 5 +- .../provision/restapi/responses/docker-node2.json | 3 +- .../provision/restapi/responses/enclave-nodes.json | 6 ++ .../hosted/provision/restapi/responses/node3.json | 3 +- 11 files changed, 154 insertions(+), 53 deletions(-) create mode 100644 node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/filter/CloudAccountFilter.java create mode 100644 node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiEnclaveTest.java create mode 100644 node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/enclave-nodes.json diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepositoryTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepositoryTest.java index 1ad8a4326e7..661d3e6073e 100644 --- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepositoryTest.java +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepositoryTest.java @@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.node.admin.configserver.noderepository; import com.yahoo.application.Networking; import com.yahoo.application.container.JDisc; +import com.yahoo.config.provision.CloudAccount; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.NodeType; @@ -60,7 +61,7 @@ public class RealNodeRepositoryTest { for (int i = 0; i < 3; i++) { try { int port = findRandomOpenPort(); - container = JDisc.fromServicesXml(ContainerConfig.servicesXmlV2(port), Networking.enable); + container = JDisc.fromServicesXml(ContainerConfig.servicesXmlV2(port, CloudAccount.empty), Networking.enable); ConfigServerApi configServerApi = ConfigServerApiImpl.createForTesting( List.of(URI.create("http://127.0.0.1:" + port))); waitForJdiscContainerToServe(configServerApi); diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/filter/CloudAccountFilter.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/filter/CloudAccountFilter.java new file mode 100644 index 00000000000..c7a4f50ab7c --- /dev/null +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/filter/CloudAccountFilter.java @@ -0,0 +1,29 @@ +package com.yahoo.vespa.hosted.provision.node.filter; + +import com.yahoo.config.provision.CloudAccount; +import com.yahoo.vespa.hosted.provision.Node; + +import java.util.Collection; +import java.util.Objects; +import java.util.function.Predicate; + +/** + * Filters nodes based on their cloud account. + * + * @author gjoranv + */ +public class CloudAccountFilter { + + private CloudAccountFilter() { } + + /** Creates a node filter which removes the nodes from the given cloud accounts */ + public static Predicate from(Collection unwantedAccounts, boolean enabled) { + Objects.requireNonNull(unwantedAccounts, "unwantedAccounts cannot be null"); + return node -> { + if (unwantedAccounts.isEmpty()) return true; + if (! enabled) return true; + return ! unwantedAccounts.contains(node.cloudAccount()); + }; + } + +} diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java index 024f071abb1..f0f85b6523f 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java @@ -1,6 +1,8 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.provision.testutils; +import com.yahoo.config.provision.CloudAccount; + /** * For running NodeRepository API with some mocked data. * This is used by both NodeAdmin and NodeRepository tests. @@ -9,38 +11,43 @@ package com.yahoo.vespa.hosted.provision.testutils; */ public class ContainerConfig { - public static String servicesXmlV2(int port) { - return "\n" + - " \n" + - " 20\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " http://*/nodes/v2*\n" + - " \n" + - " \n" + - " http://*/loadbalancers/v1*\n" + - " \n" + - " \n" + - " \n" + - " \n" + - ""; + public static String servicesXmlV2(int port, CloudAccount cloudAccount) { + return """ + + + 20 + + + %s + + + + + + + + + + + + + + + + + + + + http://*/nodes/v2* + + + http://*/loadbalancers/v1* + + + + + + """.formatted(cloudAccount.value(), port); } } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java index dccfa830a8d..a19e48cfa6b 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java @@ -7,6 +7,7 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ApplicationName; import com.yahoo.config.provision.ApplicationTransaction; import com.yahoo.config.provision.Capacity; +import com.yahoo.config.provision.CloudAccount; import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.DockerImage; @@ -34,6 +35,7 @@ import com.yahoo.vespa.hosted.provision.node.Status; import com.yahoo.vespa.hosted.provision.provisioning.EmptyProvisionServiceProvider; import com.yahoo.vespa.hosted.provision.provisioning.NodeRepositoryProvisioner; +import javax.inject.Inject; import java.time.Clock; import java.time.Instant; import java.time.ZoneId; @@ -56,20 +58,23 @@ import static com.yahoo.config.provision.NodeResources.StorageType.remote; * Instantiated by DI. */ public class MockNodeRepository extends NodeRepository { + public static final CloudAccount tenantAccount = CloudAccount.from("777888999000"); private final NodeFlavors flavors; + private final CloudAccount defaultCloudAccount; /** * Constructor * * @param flavors flavors to have in node repo */ - public MockNodeRepository(MockCurator curator, NodeFlavors flavors) { + @Inject + public MockNodeRepository(MockCurator curator, NodeFlavors flavors, Zone zone) { super(flavors, new EmptyProvisionServiceProvider(), curator, Clock.fixed(Instant.ofEpochMilli(123), ZoneId.of("Z")), - Zone.defaultZone(), + zone, new MockNameResolver().mockAnyLookup(), DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa"), Optional.empty(), @@ -79,6 +84,7 @@ public class MockNodeRepository extends NodeRepository { true, 0, 1000); this.flavors = flavors; + defaultCloudAccount = zone.cloud().account(); curator.setZooKeeperEnsembleConnectionSpec("cfg1:1234,cfg2:1234,cfg3:1234"); populate(); @@ -89,14 +95,19 @@ public class MockNodeRepository extends NodeRepository { List nodes = new ArrayList<>(); // Regular nodes - nodes.add(Node.create("node1", ipConfig(1), "host1.yahoo.com", resources(2, 8, 50, 1, fast, local), NodeType.tenant).build()); - nodes.add(Node.create("node2", ipConfig(2), "host2.yahoo.com", resources(2, 8, 50, 1, fast, local), NodeType.tenant).build()); - nodes.add(Node.create("node3", ipConfig(3), "host3.yahoo.com", resources(0.5, 48, 500, 1, fast, local), NodeType.tenant).build()); + nodes.add(Node.create("node1", ipConfig(1), "host1.yahoo.com", resources(2, 8, 50, 1, fast, local), NodeType.tenant) + .cloudAccount(defaultCloudAccount).build()); + nodes.add(Node.create("node2", ipConfig(2), "host2.yahoo.com", resources(2, 8, 50, 1, fast, local), NodeType.tenant) + .cloudAccount(defaultCloudAccount).build()); + // Emulate node in tenant account + nodes.add(Node.create("node3", ipConfig(3), "host3.yahoo.com", resources(0.5, 48, 500, 1, fast, local), NodeType.tenant) + .cloudAccount(tenantAccount).build()); Node node4 = Node.create("node4", ipConfig(4), "host4.yahoo.com", resources(1, 4, 100, 1, fast, local), NodeType.tenant) .parentHostname("dockerhost1.yahoo.com") .status(Status.initial() .withVespaVersion(new Version("6.41.0")) .withContainerImage(DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa:6.41.0"))) + .cloudAccount(defaultCloudAccount) .build(); nodes.add(node4); @@ -105,12 +116,15 @@ public class MockNodeRepository extends NodeRepository { .status(Status.initial() .withVespaVersion(new Version("1.2.3")) .withContainerImage(DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa:1.2.3"))) + .cloudAccount(defaultCloudAccount) .build(); nodes.add(node5); - nodes.add(Node.create("node6", ipConfig(6), "host6.yahoo.com", resources(2, 8, 50, 1, fast, local), NodeType.tenant).build()); - Node node7 = Node.create("node7", ipConfig(7), "host7.yahoo.com", resources(2, 8, 50, 1, fast, local), NodeType.tenant).build(); + nodes.add(Node.create("node6", ipConfig(6), "host6.yahoo.com", resources(2, 8, 50, 1, fast, local), NodeType.tenant) + .cloudAccount(defaultCloudAccount).build()); + Node node7 = Node.create("node7", ipConfig(7), "host7.yahoo.com", resources(2, 8, 50, 1, fast, local), NodeType.tenant) + .cloudAccount(defaultCloudAccount).build(); nodes.add(node7); // 8, 9, 11 and 12 are added by web service calls @@ -119,26 +133,29 @@ public class MockNodeRepository extends NodeRepository { .status(Status.initial() .withVespaVersion(Version.fromString("5.104.142")) .withContainerImage(DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa:5.104.142"))) + .cloudAccount(defaultCloudAccount) .build(); nodes.add(node10); Node node55 = Node.create("node55", ipConfig(55), "host55.yahoo.com", resources(2, 8, 50, 1, fast, local), NodeType.tenant) - .status(Status.initial().withWantToRetire(true, true, false)).build(); + .status(Status.initial().withWantToRetire(true, true, false)) + .cloudAccount(defaultCloudAccount).build(); nodes.add(node55); /* Setup docker hosts (two of these will be reserved for spares */ nodes.add(Node.create("dockerhost1", ipConfig(100, 1, 3), "dockerhost1.yahoo.com", - flavors.getFlavorOrThrow("large"), NodeType.host).build()); + flavors.getFlavorOrThrow("large"), NodeType.host).cloudAccount(defaultCloudAccount).build()); + // Emulate host in tenant account nodes.add(Node.create("dockerhost2", ipConfig(101, 1, 3), "dockerhost2.yahoo.com", - flavors.getFlavorOrThrow("large"), NodeType.host).build()); + flavors.getFlavorOrThrow("large"), NodeType.host).cloudAccount(tenantAccount).build()); nodes.add(Node.create("dockerhost3", ipConfig(102, 1, 3), "dockerhost3.yahoo.com", - flavors.getFlavorOrThrow("large"), NodeType.host).build()); + flavors.getFlavorOrThrow("large"), NodeType.host).cloudAccount(defaultCloudAccount).build()); nodes.add(Node.create("dockerhost4", ipConfig(103, 1, 3), "dockerhost4.yahoo.com", - flavors.getFlavorOrThrow("large"), NodeType.host).build()); + flavors.getFlavorOrThrow("large"), NodeType.host).cloudAccount(defaultCloudAccount).build()); nodes.add(Node.create("dockerhost5", ipConfig(104, 1, 3), "dockerhost5.yahoo.com", - flavors.getFlavorOrThrow("large"), NodeType.host).build()); + flavors.getFlavorOrThrow("large"), NodeType.host).cloudAccount(defaultCloudAccount).build()); nodes.add(Node.create("dockerhost6", ipConfig(105, 1, 3), "dockerhost6.yahoo.com", - flavors.getFlavorOrThrow("arm64"), NodeType.host).build()); + flavors.getFlavorOrThrow("arm64"), NodeType.host).cloudAccount(defaultCloudAccount).build()); // Config servers nodes.add(Node.create("cfg1", ipConfig(201), "cfg1.yahoo.com", flavors.getFlavorOrThrow("default"), NodeType.config).build()); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiTest.java index 340dfb46dc3..3c20f6ddb09 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiTest.java @@ -2,6 +2,7 @@ package com.yahoo.vespa.hosted.provision.restapi; import com.yahoo.application.container.handler.Request; +import com.yahoo.config.provision.CloudAccount; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -12,7 +13,7 @@ public class LoadBalancersV1ApiTest { @Before public void createTester() { - tester = new RestApiTester(); + tester = new RestApiTester(CloudAccount.empty); } @After diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiEnclaveTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiEnclaveTest.java new file mode 100644 index 00000000000..07cec08649a --- /dev/null +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiEnclaveTest.java @@ -0,0 +1,36 @@ +package com.yahoo.vespa.hosted.provision.restapi; + +import com.yahoo.application.container.handler.Request; +import com.yahoo.config.provision.CloudAccount; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +/** + * Test of the node repository REST API, for enclaves. The setup in this class is duplicated from NodesV2ApiTest, + * but it is inconvenient to reuse that class because we need to emulate an internal cloud account. + * + * @author gjoranv + */ +public class NodesV2ApiEnclaveTest { + + private RestApiTester tester; + + @Before + public void createTester() { + tester = new RestApiTester(CloudAccount.from("111222333444")); + } + + @After + public void closeTester() { + tester.close(); + } + + @Test + public void returns_only_enclave_nodes_when_enclave_param_is_set() throws IOException { + tester.assertFile(new Request("http://localhost:8080/nodes/v2/node/?recursive=true&enclave=true"), "enclave-nodes.json"); + + } +} diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java index 58a810bbff8..ca324bf3c66 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.provision.restapi; import com.yahoo.application.container.handler.Request; import com.yahoo.application.container.handler.Response; import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.CloudAccount; import com.yahoo.config.provision.NodeType; import com.yahoo.config.provision.TenantName; import com.yahoo.text.Utf8; @@ -40,7 +41,7 @@ public class NodesV2ApiTest { @Before public void createTester() { - tester = new RestApiTester(); + tester = new RestApiTester(CloudAccount.empty); } @After diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/RestApiTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/RestApiTester.java index a17fd4f5dda..e424b04aeaf 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/RestApiTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/RestApiTester.java @@ -5,6 +5,7 @@ import com.yahoo.application.Networking; import com.yahoo.application.container.JDisc; import com.yahoo.application.container.handler.Request; import com.yahoo.application.container.handler.Response; +import com.yahoo.config.provision.CloudAccount; import com.yahoo.io.IOUtils; import com.yahoo.vespa.hosted.provision.testutils.ContainerConfig; import org.junit.ComparisonFailure; @@ -24,8 +25,8 @@ public class RestApiTester { private final JDisc container; - public RestApiTester() { - container = JDisc.fromServicesXml(ContainerConfig.servicesXmlV2(0), Networking.disable); + public RestApiTester(CloudAccount defaultCloudAccount) { + container = JDisc.fromServicesXml(ContainerConfig.servicesXmlV2(0, defaultCloudAccount), Networking.disable); } public void close() { diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node2.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node2.json index b829fb2b4b4..bf12fc18b03 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node2.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node2.json @@ -97,5 +97,6 @@ "::101:2", "::101:3", "::101:4" - ] + ], + "cloudAccount": "777888999000" } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/enclave-nodes.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/enclave-nodes.json new file mode 100644 index 00000000000..5c728d77920 --- /dev/null +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/enclave-nodes.json @@ -0,0 +1,6 @@ +{ + "nodes": [ + @include(node3.json), + @include(docker-node2.json) + ] +} diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node3.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node3.json index 2e3181053ad..1c560c2f95b 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node3.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node3.json @@ -49,5 +49,6 @@ "127.0.3.1", "::3:1" ], - "additionalIpAddresses": [] + "additionalIpAddresses": [], + "cloudAccount": "777888999000" } -- cgit v1.2.3