diff options
25 files changed, 439 insertions, 197 deletions
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImpl.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImpl.java index 485de99082b..260e2da7c59 100644 --- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImpl.java +++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImpl.java @@ -21,6 +21,7 @@ import java.util.Random; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; +import java.util.stream.Stream; class CreateContainerCommandImpl implements Docker.CreateContainerCommand { private final DockerClient docker; @@ -32,13 +33,14 @@ class CreateContainerCommandImpl implements Docker.CreateContainerCommand { private final List<String> environmentAssignments = new ArrayList<>(); private final List<String> volumeBindSpecs = new ArrayList<>(); private final List<Ulimit> ulimits = new ArrayList<>(); + private final Set<Capability> addCapabilities = new HashSet<>(); + private final Set<Capability> dropCapabilities = new HashSet<>(); private Optional<String> networkMode = Optional.empty(); private Optional<String> ipv4Address = Optional.empty(); private Optional<String> ipv6Address = Optional.empty(); private Optional<String[]> entrypoint = Optional.empty(); - private Set<Capability> addCapabilities = new HashSet<>(); - private Set<Capability> dropCapabilities = new HashSet<>(); + private boolean privileged = false; CreateContainerCommandImpl(DockerClient docker, DockerImage dockerImage, @@ -60,8 +62,7 @@ class CreateContainerCommandImpl implements Docker.CreateContainerCommand { } public Docker.CreateContainerCommand withManagedBy(String manager) { - labels.put(DockerImpl.LABEL_NAME_MANAGEDBY, manager); - return this; + return withLabel(DockerImpl.LABEL_NAME_MANAGEDBY, manager); } @Override @@ -77,6 +78,12 @@ class CreateContainerCommandImpl implements Docker.CreateContainerCommand { } @Override + public Docker.CreateContainerCommand withPrivileged(boolean privileged) { + this.privileged = privileged; + return this; + } + + @Override public Docker.CreateContainerCommand withUlimit(String name, int softLimit, int hardLimit) { ulimits.add(new Ulimit(name, softLimit, hardLimit)); return this; @@ -84,6 +91,7 @@ class CreateContainerCommandImpl implements Docker.CreateContainerCommand { @Override public Docker.CreateContainerCommand withEntrypoint(String... entrypoint) { + if (entrypoint.length < 1) throw new IllegalArgumentException("Entrypoint must contain at least 1 element"); this.entrypoint = Optional.of(entrypoint); return this; } @@ -142,7 +150,8 @@ class CreateContainerCommandImpl implements Docker.CreateContainerCommand { .withBinds(volumeBinds) .withUlimits(ulimits) .withCapAdd(new ArrayList<>(addCapabilities)) - .withCapDrop(new ArrayList<>(dropCapabilities)); + .withCapDrop(new ArrayList<>(dropCapabilities)) + .withPrivileged(privileged); networkMode .filter(mode -> ! mode.toLowerCase().equals("host")) @@ -156,15 +165,19 @@ class CreateContainerCommandImpl implements Docker.CreateContainerCommand { return containerCmd; } - /** Maps ("--env", {"A", "B", "C"}) to "--env A --env B --env C ". */ + /** Maps ("--env", {"A", "B", "C"}) to "--env A --env B --env C" */ private String toRepeatedOption(String option, List<String> optionValues) { - StringBuilder builder = new StringBuilder(); - optionValues.forEach(optionValue -> builder.append(option).append(" ").append(optionValue).append(" ")); - return builder.toString(); + return optionValues.stream() + .map(optionValue -> option + " " + optionValue) + .collect(Collectors.joining(" ")); + } + + private String toOptionalOption(String option, Optional<String> value) { + return value.map(o -> option + " " + o).orElse(""); } - private String toOptionalOption(String option, Optional<?> value) { - return value.isPresent() ? option + " " + value.get() + " " : ""; + private String toFlagOption(String option, boolean value) { + return value ? option : ""; } /** Make toString() print the equivalent arguments to 'docker run' */ @@ -175,24 +188,31 @@ class CreateContainerCommandImpl implements Docker.CreateContainerCommand { List<String> ulimitList = ulimits.stream() .map(ulimit -> ulimit.getName() + "=" + ulimit.getSoft() + ":" + ulimit.getHard()) .collect(Collectors.toList()); - List<String> addCapabilitiesList = addCapabilities.stream().map(Enum<Capability>::toString).collect(Collectors.toList()); - List<String> dropCapabilitiesList = dropCapabilities.stream().map(Enum<Capability>::toString).collect(Collectors.toList()); - - return "--name " + containerName.asString() + " " - + "--hostname " + hostName + " " - + "--cpu-shares " + containerResources.cpuShares + " " - + "--memory " + containerResources.memoryBytes + " " - + toRepeatedOption("--label", labelList) - + toRepeatedOption("--ulimit", ulimitList) - + toRepeatedOption("--env", environmentAssignments) - + toRepeatedOption("--volume", volumeBindSpecs) - + toRepeatedOption("--cap-add", addCapabilitiesList) - + toRepeatedOption("--cap-drop", dropCapabilitiesList) - + toOptionalOption("--net", networkMode) - + toOptionalOption("--ip", ipv4Address) - + toOptionalOption("--ip6", ipv6Address) - + toOptionalOption("--entrypoint", entrypoint) - + dockerImage.asString(); + List<String> addCapabilitiesList = addCapabilities.stream().map(Enum<Capability>::toString).sorted().collect(Collectors.toList()); + List<String> dropCapabilitiesList = dropCapabilities.stream().map(Enum<Capability>::toString).sorted().collect(Collectors.toList()); + Optional<String> entrypointExecuteable = entrypoint.map(args -> args[0]); + String entrypointArgs = entrypoint.map(Stream::of).orElseGet(Stream::empty) + .skip(1) + .collect(Collectors.joining(" ")); + + return String.join(" ", + "--name " + containerName.asString(), + "--hostname " + hostName, + "--cpu-shares " + containerResources.cpuShares, + "--memory " + containerResources.memoryBytes, + toRepeatedOption("--label", labelList), + toRepeatedOption("--ulimit", ulimitList), + toRepeatedOption("--env", environmentAssignments), + toRepeatedOption("--volume", volumeBindSpecs), + toRepeatedOption("--cap-add", addCapabilitiesList), + toRepeatedOption("--cap-drop", dropCapabilitiesList), + toOptionalOption("--net", networkMode), + toOptionalOption("--ip", ipv4Address), + toOptionalOption("--ip6", ipv6Address), + toOptionalOption("--entrypoint", entrypointExecuteable), + toFlagOption("--privileged", privileged), + dockerImage.asString(), + entrypointArgs); } /** diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/Docker.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/Docker.java index 2039d0adfc9..36fc1446bea 100644 --- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/Docker.java +++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/Docker.java @@ -27,6 +27,7 @@ public interface Docker { CreateContainerCommand withManagedBy(String manager); CreateContainerCommand withAddCapability(String capabilityName); CreateContainerCommand withDropCapability(String capabilityName); + CreateContainerCommand withPrivileged(boolean privileged); void create(); } diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImpl.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImpl.java index f6588512e2d..805b1e69d45 100644 --- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImpl.java +++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImpl.java @@ -200,7 +200,8 @@ public class DockerImpl implements Docker { @Override public CreateContainerCommand createContainerCommand(DockerImage image, ContainerResources containerResources, ContainerName name, String hostName) { - return new CreateContainerCommandImpl(dockerClient, image, containerResources, name, hostName); + return new CreateContainerCommandImpl(dockerClient, image, containerResources, name, hostName) + .withPrivileged(config.runContainersInPrivileged()); } @Override diff --git a/docker-api/src/main/resources/configdefinitions/docker.def b/docker-api/src/main/resources/configdefinitions/docker.def index 83fee05dff6..7be8d85e0a9 100644 --- a/docker-api/src/main/resources/configdefinitions/docker.def +++ b/docker-api/src/main/resources/configdefinitions/docker.def @@ -13,3 +13,5 @@ isRunningLocally bool default = false imageGCMinTimeToLiveMinutes int default = 45 networkNATed bool default = false + +runContainersInPrivileged bool default = false diff --git a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImplTest.java b/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImplTest.java index aa455cfc0f2..0d8701ac43c 100644 --- a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImplTest.java +++ b/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImplTest.java @@ -4,6 +4,8 @@ package com.yahoo.vespa.hosted.dockerapi; import org.junit.Test; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.Optional; import java.util.stream.Stream; @@ -12,6 +14,52 @@ import static org.junit.Assert.assertEquals; public class CreateContainerCommandImplTest { @Test + public void testToString() throws UnknownHostException { + DockerImage dockerImage = new DockerImage("docker.registry.domain.tld/my/image:1.2.3"); + ContainerResources containerResources = new ContainerResources(100, 1024); + String hostname = "docker-1.region.domain.tld"; + ContainerName containerName = ContainerName.fromHostname(hostname); + + Docker.CreateContainerCommand createContainerCommand = new CreateContainerCommandImpl( + null, dockerImage, containerResources, containerName, hostname) + .withLabel("my-label", "test-label") + .withUlimit("nofile", 1, 2) + .withUlimit("nproc", 10, 20) + .withEnvironment("env1", "val1") + .withEnvironment("env2", "val2") + .withVolume("vol1", "/host/vol1") + .withAddCapability("SYS_PTRACE") + .withAddCapability("SYS_ADMIN") + .withDropCapability("NET_ADMIN") + .withNetworkMode("bridge") + .withIpAddress(InetAddress.getByName("10.0.0.1")) + .withIpAddress(InetAddress.getByName("::1")) + .withEntrypoint("/path/to/program", "arg1", "arg2") + .withPrivileged(true); + + assertEquals("--name docker-1 " + + "--hostname docker-1.region.domain.tld " + + "--cpu-shares 100 " + + "--memory 1024 " + + "--label my-label=test-label " + + "--ulimit nofile=1:2 " + + "--ulimit nproc=10:20 " + + "--env env1=val1 " + + "--env env2=val2 " + + "--volume vol1:/host/vol1 " + + "--cap-add SYS_ADMIN " + + "--cap-add SYS_PTRACE " + + "--cap-drop NET_ADMIN " + + "--net bridge " + + "--ip 10.0.0.1 " + + "--ip6 0:0:0:0:0:0:0:1 " + + "--entrypoint /path/to/program " + + "--privileged docker.registry.domain.tld/my/image:1.2.3 " + + "arg1 " + + "arg2", createContainerCommand.toString()); + } + + @Test public void generateMacAddressTest() { String[][] addresses = { {"test123.host.yahoo.com", null, "abcd:1234::1", "ee:ae:a9:de:ad:c2"}, diff --git a/node-admin/src/main/application/services.xml b/node-admin/src/main/application/services.xml index f2f9e46c5b8..afe2888c5ef 100644 --- a/node-admin/src/main/application/services.xml +++ b/node-admin/src/main/application/services.xml @@ -13,6 +13,7 @@ <config name="vespa.hosted.dockerapi.docker"> <uri>unix:///var/run/docker.sock</uri> + <runContainersInPrivileged>true</runContainersInPrivileged> </config> <preprocess:include file="variant.xml" required="false"/> diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/DockerAdminComponent.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/DockerAdminComponent.java index 89f52a39fbb..a93d77d07bd 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/DockerAdminComponent.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/DockerAdminComponent.java @@ -18,6 +18,7 @@ import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdaterImpl; import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgent; import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentImpl; import com.yahoo.vespa.hosted.node.admin.provider.NodeAdminStateUpdater; +import com.yahoo.vespa.hosted.node.admin.task.util.network.IPAddressesImpl; import java.time.Clock; import java.time.Duration; @@ -93,7 +94,8 @@ public class DockerAdminComponent implements AdminComponent { DockerOperations dockerOperations = new DockerOperationsImpl( docker, environment.get(), - processExecuter); + processExecuter, + new IPAddressesImpl()); StorageMaintainer storageMaintainer = new StorageMaintainer( dockerOperations, diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/GetAclResponse.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/GetAclResponse.java index b7762cf6aa9..7000170ca4c 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/GetAclResponse.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/GetAclResponse.java @@ -29,6 +29,7 @@ public class GetAclResponse { this.trustedNetworks = trustedNetworks == null ? Collections.emptyList() : trustedNetworks; } + @JsonIgnoreProperties(ignoreUnknown = true) public static class Node { @JsonProperty("hostname") @@ -49,6 +50,7 @@ public class GetAclResponse { } } + @JsonIgnoreProperties(ignoreUnknown = true) public static class Network { @JsonProperty("network") diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/GetNodesResponse.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/GetNodesResponse.java index 783ce89ad5c..d1eda3c2d45 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/GetNodesResponse.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/GetNodesResponse.java @@ -119,7 +119,7 @@ public class GetNodesResponse { + " }"; } - + @JsonIgnoreProperties(ignoreUnknown = true) public static class Owner { public final String tenant; public final String application; @@ -143,6 +143,7 @@ public class GetNodesResponse { } } + @JsonIgnoreProperties(ignoreUnknown = true) public static class Membership { public final String clusterType; public final String clusterId; diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/UpdateNodeAttributesRequestBody.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/UpdateNodeAttributesRequestBody.java index 28605bc3a8d..305d7d386de 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/UpdateNodeAttributesRequestBody.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/UpdateNodeAttributesRequestBody.java @@ -1,6 +1,7 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.node.admin.configserver.noderepository.bindings; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAttributes; @@ -16,6 +17,7 @@ import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAttributes; * @author bakksjo */ @JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) public class UpdateNodeAttributesRequestBody { public Long currentRestartGeneration; public Long currentRebootGeneration; diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java index 1b5507eb534..b9cea1cd5da 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java @@ -18,9 +18,11 @@ import com.yahoo.vespa.hosted.dockerapi.ProcessResult; import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec; import com.yahoo.vespa.hosted.node.admin.component.Environment; import com.yahoo.vespa.hosted.node.admin.maintenance.acl.iptables.NATCommand; +import com.yahoo.vespa.hosted.node.admin.task.util.network.IPAddresses; import com.yahoo.vespa.hosted.node.admin.util.PrefixLogger; import java.io.IOException; +import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.URI; @@ -43,7 +45,8 @@ public class DockerOperationsImpl implements DockerOperations { private static final String MANAGER_NAME = "node-admin"; - private static final String LOCAL_IPV6_PREFIX = "fd00::"; + private static final String IPV6_NPT_PREFIX = "fd00::"; + private static final String IPV4_NPT_PREFIX = "172.17.0.0"; private static final String DOCKER_CUSTOM_BRIDGE_NETWORK_NAME = "vespa-bridge"; private final Docker docker; @@ -51,11 +54,13 @@ public class DockerOperationsImpl implements DockerOperations { private final ProcessExecuter processExecuter; private final String nodeProgram; private Map<Path, Boolean> directoriesToMount; + private final IPAddresses retriever; - public DockerOperationsImpl(Docker docker, Environment environment, ProcessExecuter processExecuter) { + public DockerOperationsImpl(Docker docker, Environment environment, ProcessExecuter processExecuter, IPAddresses retriever) { this.docker = docker; this.environment = environment; this.processExecuter = processExecuter; + this.retriever = retriever; this.nodeProgram = environment.pathInNodeUnderVespaHome("bin/vespa-nodectl").toString(); this.directoriesToMount = getDirectoriesToMount(environment); @@ -91,10 +96,21 @@ public class DockerOperationsImpl implements DockerOperations { command.withNetworkMode(DockerImpl.DOCKER_CUSTOM_MACVLAN_NETWORK_NAME); command.withVolume("/etc/hosts", "/etc/hosts"); // TODO This is probably not necessary - review later } else { - command.withIpAddress(NetworkPrefixTranslator.translate( - nodeInetAddress, - InetAddress.getByName(LOCAL_IPV6_PREFIX), - 64)); + // IPv6 - Assume always valid + Inet6Address ipV6Address = this.retriever.getIPv6Address(nodeSpec.hostname).orElseThrow( + () -> new RuntimeException("Unable to find a valid IPv6 address. Missing an AAAA DNS entry?")); + InetAddress ipV6Prefix = InetAddress.getByName(IPV6_NPT_PREFIX); + InetAddress ipV6Local = IPAddresses.prefixTranslate(ipV6Address, ipV6Prefix, 8); + command.withIpAddress(ipV6Local); + + // IPv4 - Only present for some containers + Optional<Inet4Address> ipV4Address = this.retriever.getIPv4Address(nodeSpec.hostname); + if (ipV4Address.isPresent()) { + InetAddress ipV4Prefix = InetAddress.getByName(IPV4_NPT_PREFIX); + InetAddress ipV4Local = IPAddresses.prefixTranslate(ipV4Address.get(), ipV4Prefix, 2); + command.withIpAddress(ipV4Local); + } + command.withNetworkMode(DOCKER_CUSTOM_BRIDGE_NETWORK_NAME); } diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/NetworkPrefixTranslator.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/NetworkPrefixTranslator.java deleted file mode 100644 index a52dedb90e5..00000000000 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/NetworkPrefixTranslator.java +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -/** - * @author smorgrav - */ -package com.yahoo.vespa.hosted.node.admin.docker; - -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.UnknownHostException; - -class NetworkPrefixTranslator { - - /** - * For NPTed networks we want to find the private address from a public. - * - * @param address The original address to translate - * @param prefix The prefix address - * @param subnetSize in bits - e.g a /64 subnet equals 64 bits - * @return The translated address - */ - static Inet6Address translate(InetAddress address, InetAddress prefix, int subnetSize) { - - byte[] originalAddress = address.getAddress(); - byte[] prefixAddress = prefix.getAddress(); - byte[] translatedAddress = new byte[16]; - - for (int i = 0; i < 16; i++) { - translatedAddress[i] = i < subnetSize / 8 ? prefixAddress[i] : originalAddress[i]; - } - - try { - return (Inet6Address) InetAddress.getByAddress(address.getHostName(), translatedAddress); - } catch (UnknownHostException e) { - throw new RuntimeException(e); - } - } -} diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/network/IPAddresses.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/network/IPAddresses.java new file mode 100644 index 00000000000..a49c4e1fbf6 --- /dev/null +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/network/IPAddresses.java @@ -0,0 +1,130 @@ +package com.yahoo.vespa.hosted.node.admin.task.util.network; + +import com.google.common.net.InetAddresses; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * IP addresses - IP utilities to retrieve and manipulate addresses for docker host and docker containers in a + * multi-home environment. + * + * The assumption is that DNS is the source of truth for which address are assigned to the host and which + * that belongs to the containers. Only one address should be assigned to each. + * + * The behavior with respect to site-local addresses are distinct for IPv4 and IPv6. For IPv4 we choose + * the site-local address (assume the public is a NAT address not assigned to the host interface (the typical aws setup)). + * + * For IPv6 we disregard any site-local addresses (these are normally not in DNS anyway). + * + * This class also provides some utilities for prefix translation. + * + * @author smorgrav + */ +public interface IPAddresses { + + InetAddress[] getAddresses(String hostname); + + default List<String> getAddresses(String hostname, IPVersion ipVersion) { + return Stream.of(getAddresses(hostname)) + .filter(inetAddress -> isOfType(inetAddress, ipVersion)) + .map(InetAddresses::toAddrString) + .collect(Collectors.toList()); + } + + /** + * Get the IPv6 address for the host if any. + * + * @throws RuntimeException if multiple addresses are found + * @return An optional string representation of the IP address (RFC 5952 compact format) + */ + default Optional<Inet6Address> getIPv6Address(String hostname) { + List<InetAddress> ipv6addresses = Stream.of(getAddresses(hostname)) + .filter(inetAddress -> inetAddress instanceof Inet6Address) + .filter(inetAddress -> !inetAddress.isLinkLocalAddress()) + .filter(inetAddress -> !inetAddress.isSiteLocalAddress()) + .collect(Collectors.toList()); + + if (ipv6addresses.isEmpty()) { + return Optional.empty(); + } + + if (ipv6addresses.size() > 1) { + String addresses = + ipv6addresses.stream().map(InetAddresses::toAddrString).collect(Collectors.joining(",")); + throw new RuntimeException( + String.format( + "Multiple IPv6 addresses found: %s. Perhaps a missing DNS entry or multiple AAAA records in DNS?", + addresses)); + } + + return Optional.of((Inet6Address)ipv6addresses.get(0)); + } + + /** + * Get the IPv4 address for the host if any. + * + * @throws RuntimeException if multiple site-local addresses are found + * @return An optional string representation of the IP address + */ + default Optional<Inet4Address> getIPv4Address(String hostname) { + InetAddress[] allAddresses = getAddresses(hostname); + + List<InetAddress> ipv4Addresses = Stream.of(allAddresses) + .filter(inetAddress -> inetAddress instanceof Inet4Address) + .collect(Collectors.toList()); + + if (ipv4Addresses.size() == 1) return Optional.of((Inet4Address)ipv4Addresses.get(0)); + + if (ipv4Addresses.isEmpty()) { + Optional.empty(); + } + + List<InetAddress> siteLocalIPv4Addresses = Stream.of(allAddresses) + .filter(inetAddress -> inetAddress instanceof Inet4Address) + .filter(InetAddress::isSiteLocalAddress) + .collect(Collectors.toList()); + + if (siteLocalIPv4Addresses.size() == 1) return Optional.of((Inet4Address)siteLocalIPv4Addresses.get(0)); + + String addresses = + ipv4Addresses.stream().map(InetAddresses::toAddrString).collect(Collectors.joining(",")); + throw new RuntimeException( + String.format( + "Multiple IPv4 addresses found: %s. Perhaps a missing DNS entry or multiple A records in DNS?", + addresses)); + } + + static boolean isOfType(InetAddress address, IPVersion ipVersion) { + if (ipVersion.equals(IPVersion.IPv4) && address instanceof Inet4Address) return true; + if (ipVersion.equals(IPVersion.IPv6) && address instanceof Inet6Address) return true; + return false; + } + + /** + * For NPTed networks we want to find the private address from a public. + * + * @param address The original address to translate + * @param prefix The prefix address + * @param subnetSizeInBytes in bits - e.g a /64 subnet equals 8 bytes + * @return The translated address + */ + static InetAddress prefixTranslate(InetAddress address, InetAddress prefix, int subnetSizeInBytes) { + return prefixTranslate(address.getAddress(), prefix.getAddress(), subnetSizeInBytes); + } + + static InetAddress prefixTranslate(byte[] address, byte[] prefix, int nofBytes) { + System.arraycopy(prefix, 0, address, 0, nofBytes); + try { + return InetAddress.getByAddress(address); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + } +} diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/network/IPAddressesImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/network/IPAddressesImpl.java new file mode 100644 index 00000000000..9ae7181abe9 --- /dev/null +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/network/IPAddressesImpl.java @@ -0,0 +1,19 @@ +package com.yahoo.vespa.hosted.node.admin.task.util.network; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * @author smorgrav + */ +public class IPAddressesImpl implements IPAddresses { + + @Override + public InetAddress[] getAddresses(String hostname) { + try { + return InetAddress.getAllByName(hostname); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + } +} diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/network/IPVersion.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/network/IPVersion.java new file mode 100644 index 00000000000..f03f1a57f81 --- /dev/null +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/network/IPVersion.java @@ -0,0 +1,22 @@ +package com.yahoo.vespa.hosted.node.admin.task.util.network; + +/** + * Strong type IPv4 and IPv6 with the respective iptables executable. + * + * @author smorgrav + */ +enum IPVersion { + + IPv6("ip6tables"), + IPv4("iptables"); + + IPVersion(String exec) { + this.exec = exec; + } + + private String exec; + + String exec() { + return exec; + } +} diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImplTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImplTest.java index e2db9743412..a07bd4e0e91 100644 --- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImplTest.java +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImplTest.java @@ -9,6 +9,7 @@ import com.yahoo.vespa.hosted.dockerapi.Docker; import com.yahoo.vespa.hosted.dockerapi.DockerImage; import com.yahoo.vespa.hosted.dockerapi.ProcessResult; import com.yahoo.vespa.hosted.node.admin.component.Environment; +import com.yahoo.vespa.hosted.node.admin.task.util.network.IPAddressesMock; import org.junit.Test; import org.mockito.InOrder; @@ -34,7 +35,9 @@ public class DockerOperationsImplTest { .build(); private final Docker docker = mock(Docker.class); private final ProcessExecuter processExecuter = mock(ProcessExecuter.class); - private final DockerOperationsImpl dockerOperations = new DockerOperationsImpl(docker, environment, processExecuter); + private final IPAddressesMock addressesMock = new IPAddressesMock(); + private final DockerOperationsImpl dockerOperations + = new DockerOperationsImpl(docker, environment, processExecuter, addressesMock); @Test public void processResultFromNodeProgramWhenSuccess() throws Exception { diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/NetworkPrefixTranslatorTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/NetworkPrefixTranslatorTest.java deleted file mode 100644 index 96afe685a61..00000000000 --- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/NetworkPrefixTranslatorTest.java +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -/** - * @author smorgrav - */ -package com.yahoo.vespa.hosted.node.admin.docker; - -import org.junit.Assert; -import org.junit.Test; - -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.UnknownHostException; - -public class NetworkPrefixTranslatorTest { - - @Test - public void translator_with_valid_parameters() throws UnknownHostException { - - // Test simplest possible address - Inet6Address original = (Inet6Address)InetAddress.getByName("2001:db8::1"); - Inet6Address prefix = (Inet6Address)InetAddress.getByName("fd00::"); - Inet6Address translated = NetworkPrefixTranslator.translate(original, prefix, 64); - Assert.assertEquals("fd00:0:0:0:0:0:0:1", translated.getHostAddress()); - - - // Test an actual aws address we use - original = (Inet6Address)InetAddress.getByName("2600:1f16:f34:5300:ccc6:1703:b7c2:369d"); - translated = NetworkPrefixTranslator.translate(original, prefix, 64); - Assert.assertEquals("fd00:0:0:0:ccc6:1703:b7c2:369d", translated.getHostAddress()); - - // Test different subnet size - translated = NetworkPrefixTranslator.translate(original, prefix, 48); - Assert.assertEquals("fd00:0:0:5300:ccc6:1703:b7c2:369d", translated.getHostAddress()); - } -} diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerMock.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerMock.java index 6c9df440826..c183a7ba306 100644 --- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerMock.java +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerMock.java @@ -221,6 +221,11 @@ public class DockerMock implements Docker { } @Override + public CreateContainerCommand withPrivileged(boolean privileged) { + return this; + } + + @Override public void create() { } diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerTester.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerTester.java index 2968c1737a8..ba176305173 100644 --- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerTester.java +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerTester.java @@ -15,6 +15,7 @@ import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdaterImpl; import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgent; import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentImpl; import com.yahoo.vespa.hosted.node.admin.component.Environment; +import com.yahoo.vespa.hosted.node.admin.task.util.network.IPAddressesImpl; import com.yahoo.vespa.hosted.node.admin.util.InetAddressResolver; import com.yahoo.vespa.hosted.node.admin.component.PathResolver; @@ -65,7 +66,7 @@ public class DockerTester implements AutoCloseable { .pathResolver(new PathResolver(pathToVespaHome, Paths.get("/tmp"), Paths.get("/tmp"))) .build(); Clock clock = Clock.systemUTC(); - DockerOperations dockerOperations = new DockerOperationsImpl(dockerMock, environment, null); + DockerOperations dockerOperations = new DockerOperationsImpl(dockerMock, environment, null, new IPAddressesImpl()); StorageMaintainerMock storageMaintainer = new StorageMaintainerMock(dockerOperations, null, environment, callOrderVerifier, clock); AclMaintainer aclMaintainer = mock(AclMaintainer.class); diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/network/IPAddressesMock.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/network/IPAddressesMock.java new file mode 100644 index 00000000000..c799ee5eaca --- /dev/null +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/network/IPAddressesMock.java @@ -0,0 +1,33 @@ +package com.yahoo.vespa.hosted.node.admin.task.util.network; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author smorgrav + */ +public class IPAddressesMock implements IPAddresses { + + Map<String, List<InetAddress>> otherAddresses = new HashMap<>(); + + IPAddressesMock addAddress(String hostname, String ip) { + List<InetAddress> addresses = otherAddresses.getOrDefault(hostname, new ArrayList<>()); + try { + addresses.add(InetAddress.getByName(ip)); + } catch (UnknownHostException e) { + e.printStackTrace(); + } + otherAddresses.put(hostname, addresses); + return this; + } + + @Override + public InetAddress[] getAddresses(String hostname) { + List<InetAddress> addresses = otherAddresses.get(hostname); + return addresses.toArray(new InetAddress[addresses.size()]); + } +} diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/network/IPAddressesTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/network/IPAddressesTest.java new file mode 100644 index 00000000000..71f02364afa --- /dev/null +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/network/IPAddressesTest.java @@ -0,0 +1,79 @@ +package com.yahoo.vespa.hosted.node.admin.task.util.network; + +import org.junit.Assert; +import org.junit.Test; + +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * @author smorgrav + */ +public class IPAddressesTest { + + private final IPAddressesMock mock = new IPAddressesMock(); + + @Test + public void choose_sitelocal_ipv4_over_public() { + mock.addAddress("localhost", "38.3.4.2") + .addAddress("localhost", "10.0.2.2") + .addAddress("localhost", "fe80::1") + .addAddress("localhost", "2001::1"); + + Assert.assertTrue(equals("10.0.2.2", mock.getIPv4Address("localhost").get())); + } + + @Test + public void choose_ipv6_public_over_local() { + mock.addAddress("localhost", "38.3.4.2") + .addAddress("localhost", "10.0.2.2") + .addAddress("localhost", "fe80::1") + .addAddress("localhost", "2001::1"); + + Assert.assertTrue(equals("2001::1", mock.getIPv6Address("localhost").get())); + } + + @Test(expected = RuntimeException.class) + public void throws_when_multiple_ipv6_addresses() { + mock.addAddress("localhost", "2001::1") + .addAddress("localhost", "2001::2"); + mock.getIPv6Address("localhost"); + } + + @Test(expected = RuntimeException.class) + public void throws_when_multiple_private_ipv4_addresses() { + mock.addAddress("localhost", "38.3.4.2") + .addAddress("localhost", "10.0.2.2") + .addAddress("localhost", "10.0.2.3"); + mock.getIPv4Address("localhost"); + } + + @Test + public void translator_with_valid_parameters() throws UnknownHostException { + + // Test simplest possible address + Inet6Address original = (Inet6Address) InetAddress.getByName("2001:db8::1"); + Inet6Address prefix = (Inet6Address) InetAddress.getByName("fd00::"); + InetAddress translated = IPAddresses.prefixTranslate(original, prefix, 8); + Assert.assertEquals("fd00:0:0:0:0:0:0:1", translated.getHostAddress()); + + + // Test an actual aws address we use + original = (Inet6Address) InetAddress.getByName("2600:1f16:f34:5300:ccc6:1703:b7c2:369d"); + translated = IPAddresses.prefixTranslate(original, prefix, 8); + Assert.assertEquals("fd00:0:0:0:ccc6:1703:b7c2:369d", translated.getHostAddress()); + + // Test different subnet size + translated = IPAddresses.prefixTranslate(original, prefix, 6); + Assert.assertEquals("fd00:0:0:5300:ccc6:1703:b7c2:369d", translated.getHostAddress()); + } + + boolean equals(String a, InetAddress b) { + try { + return InetAddress.getByName(a).equals(b); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + } +} diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeAclResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeAclResponse.java index 65b727ad0dd..2a4f37151de 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeAclResponse.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeAclResponse.java @@ -7,13 +7,12 @@ import com.yahoo.slime.Cursor; import com.yahoo.slime.Slime; import com.yahoo.vespa.config.SlimeUtils; import com.yahoo.vespa.hosted.provision.Node; -import com.yahoo.vespa.hosted.provision.NodeRepository; import com.yahoo.vespa.hosted.provision.node.NodeAcl; +import com.yahoo.vespa.hosted.provision.NodeRepository; import java.io.File; import java.io.IOException; import java.io.OutputStream; -import java.util.Set; /** * @author mpolden @@ -37,35 +36,36 @@ public class NodeAclResponse extends HttpResponse { toSlime(hostname, root); } + private static String baseName(String path) { + return new File(path).getName(); + } + private void toSlime(String hostname, Cursor object) { Node node = nodeRepository.getNode(hostname) .orElseGet(() -> nodeRepository.getConfigNode(hostname) .orElseThrow(() -> new NotFoundException("No node with hostname '" + hostname + "'"))); Cursor trustedNodesArray = object.setArray("trustedNodes"); - nodeRepository.getNodeAcls(node, aclsForChildren).forEach(nodeAcl -> toSlime(nodeAcl, trustedNodesArray)); + nodeRepository.getNodeAcls(node, aclsForChildren).forEach(nodeAcl -> toTrustedNodeSlime(nodeAcl, trustedNodesArray)); Cursor trustedNetworksArray = object.setArray("trustedNetworks"); - nodeRepository.getNodeAcls(node, aclsForChildren).forEach(nodeAcl -> toSlime(nodeAcl.trustedNetworks(), - nodeAcl.node(), - trustedNetworksArray)); + nodeRepository.getNodeAcls(node, aclsForChildren).forEach(nodeAcl -> toTrustedNetworkSlime(nodeAcl, trustedNetworksArray)); } - private void toSlime(NodeAcl nodeAcl, Cursor array) { + private void toTrustedNodeSlime(NodeAcl nodeAcl, Cursor array) { nodeAcl.trustedNodes().forEach(node -> node.ipAddresses().forEach(ipAddress -> { Cursor object = array.addObject(); object.setString("hostname", node.hostname()); - object.setString("type", node.type().name()); object.setString("ipAddress", ipAddress); object.setString("trustedBy", nodeAcl.node().hostname()); })); } - private void toSlime(Set<String> trustedNetworks, Node trustedBy, Cursor array) { - trustedNetworks.forEach(network -> { + private void toTrustedNetworkSlime(NodeAcl nodeAcl, Cursor array) { + nodeAcl.trustedNetworks().forEach(network -> { Cursor object = array.addObject(); object.setString("network", network); - object.setString("trustedBy", trustedBy.hostname()); + object.setString("trustedBy", nodeAcl.node().hostname()); }); } @@ -78,8 +78,4 @@ public class NodeAclResponse extends HttpResponse { public String getContentType() { return "application/json"; } - - private static String baseName(String path) { - return new File(path).getName(); - } } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/acl-config-server.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/acl-config-server.json index 775d33a3a19..af925e9f94e 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/acl-config-server.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/acl-config-server.json @@ -2,193 +2,161 @@ "trustedNodes": [ { "hostname": "cfg1", - "type": "config", "ipAddress": "(ignore)", "trustedBy": "cfg1" }, { "hostname": "cfg2", - "type": "config", "ipAddress": "(ignore)", "trustedBy": "cfg1" }, { "hostname": "cfg3", - "type": "config", "ipAddress": "(ignore)", "trustedBy": "cfg1" }, { "hostname": "dockerhost1.yahoo.com", - "type": "host", "ipAddress": "::1", "trustedBy": "cfg1" }, { "hostname": "dockerhost1.yahoo.com", - "type": "host", "ipAddress": "127.0.0.1", "trustedBy": "cfg1" }, { "hostname": "dockerhost2.yahoo.com", - "type": "host", "ipAddress": "::1", "trustedBy": "cfg1" }, { "hostname": "dockerhost2.yahoo.com", - "type": "host", "ipAddress": "127.0.0.1", "trustedBy": "cfg1" }, { "hostname": "dockerhost3.yahoo.com", - "type": "host", "ipAddress": "::1", "trustedBy": "cfg1" }, { "hostname": "dockerhost3.yahoo.com", - "type": "host", "ipAddress": "127.0.0.1", "trustedBy": "cfg1" }, { "hostname": "dockerhost4.yahoo.com", - "type": "host", "ipAddress": "::1", "trustedBy": "cfg1" }, { "hostname": "dockerhost4.yahoo.com", - "type": "host", "ipAddress": "127.0.0.1", "trustedBy": "cfg1" }, { "hostname": "dockerhost5.yahoo.com", - "type": "host", "ipAddress": "::1", "trustedBy": "cfg1" }, { "hostname": "dockerhost5.yahoo.com", - "type": "host", "ipAddress": "127.0.0.1", "trustedBy": "cfg1" }, { "hostname": "host1.yahoo.com", - "type": "tenant", "ipAddress": "::1", "trustedBy": "cfg1" }, { "hostname": "host1.yahoo.com", - "type": "tenant", "ipAddress": "127.0.0.1", "trustedBy": "cfg1" }, { "hostname": "host10.yahoo.com", - "type": "tenant", "ipAddress": "::1", "trustedBy": "cfg1" }, { "hostname": "host10.yahoo.com", - "type": "tenant", "ipAddress": "127.0.0.1", "trustedBy": "cfg1" }, { "hostname": "host2.yahoo.com", - "type": "tenant", "ipAddress": "::1", "trustedBy": "cfg1" }, { "hostname": "host2.yahoo.com", - "type": "tenant", "ipAddress": "127.0.0.1", "trustedBy": "cfg1" }, { "hostname": "host3.yahoo.com", - "type": "tenant", "ipAddress": "::1", "trustedBy": "cfg1" }, { "hostname": "host3.yahoo.com", - "type": "tenant", "ipAddress": "127.0.0.1", "trustedBy": "cfg1" }, { "hostname": "host4.yahoo.com", - "type": "tenant", "ipAddress": "::1", "trustedBy": "cfg1" }, { "hostname": "host4.yahoo.com", - "type": "tenant", "ipAddress": "127.0.0.1", "trustedBy": "cfg1" }, { "hostname": "host5.yahoo.com", - "type": "tenant", "ipAddress": "::1", "trustedBy": "cfg1" }, { "hostname": "host5.yahoo.com", - "type": "tenant", "ipAddress": "127.0.0.1", "trustedBy": "cfg1" }, { "hostname": "host55.yahoo.com", - "type": "tenant", "ipAddress": "::1", "trustedBy": "cfg1" }, { "hostname": "host55.yahoo.com", - "type": "tenant", "ipAddress": "127.0.0.1", "trustedBy": "cfg1" }, { "hostname": "host6.yahoo.com", - "type": "tenant", "ipAddress": "::1", "trustedBy": "cfg1" }, { "hostname": "host6.yahoo.com", - "type": "tenant", "ipAddress": "127.0.0.1", "trustedBy": "cfg1" }, { "hostname": "host7.yahoo.com", - "type": "tenant", "ipAddress": "::1", "trustedBy": "cfg1" }, { "hostname": "host7.yahoo.com", - "type": "tenant", "ipAddress": "127.0.0.1", "trustedBy": "cfg1" }, { "hostname": "test-container-1", - "type": "tenant", "ipAddress": "::2", "trustedBy": "cfg1" } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/acl-docker-host.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/acl-docker-host.json index f13730ba066..195edac7cce 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/acl-docker-host.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/acl-docker-host.json @@ -2,79 +2,66 @@ "trustedNodes": [ { "hostname": "cfg1", - "type": "config", "ipAddress": "(ignore)", "trustedBy": "dockerhost1.yahoo.com" }, { "hostname": "cfg2", - "type": "config", "ipAddress": "(ignore)", "trustedBy": "dockerhost1.yahoo.com" }, { "hostname": "cfg3", - "type": "config", "ipAddress": "(ignore)", "trustedBy": "dockerhost1.yahoo.com" }, { "hostname": "dockerhost1.yahoo.com", - "type": "host", "ipAddress": "::1", "trustedBy": "dockerhost1.yahoo.com" }, { "hostname": "dockerhost1.yahoo.com", - "type": "host", "ipAddress": "127.0.0.1", "trustedBy": "dockerhost1.yahoo.com" }, { "hostname": "dockerhost2.yahoo.com", - "type": "host", "ipAddress": "::1", "trustedBy": "dockerhost1.yahoo.com" }, { "hostname": "dockerhost2.yahoo.com", - "type": "host", "ipAddress": "127.0.0.1", "trustedBy": "dockerhost1.yahoo.com" }, { "hostname": "dockerhost3.yahoo.com", - "type": "host", "ipAddress": "::1", "trustedBy": "dockerhost1.yahoo.com" }, { "hostname": "dockerhost3.yahoo.com", - "type": "host", "ipAddress": "127.0.0.1", "trustedBy": "dockerhost1.yahoo.com" }, { "hostname": "dockerhost4.yahoo.com", - "type": "host", "ipAddress": "::1", "trustedBy": "dockerhost1.yahoo.com" }, { "hostname": "dockerhost4.yahoo.com", - "type": "host", "ipAddress": "127.0.0.1", "trustedBy": "dockerhost1.yahoo.com" }, { "hostname": "dockerhost5.yahoo.com", - "type": "host", "ipAddress": "::1", "trustedBy": "dockerhost1.yahoo.com" }, { "hostname": "dockerhost5.yahoo.com", - "type": "host", "ipAddress": "127.0.0.1", "trustedBy": "dockerhost1.yahoo.com" } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/acl-tenant-node.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/acl-tenant-node.json index b2184c9d825..eb7a6ba61bb 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/acl-tenant-node.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/acl-tenant-node.json @@ -2,139 +2,116 @@ "trustedNodes": [ { "hostname": "cfg1", - "type": "config", "ipAddress": "(ignore)", "trustedBy": "foo.yahoo.com" }, { "hostname": "cfg2", - "type": "config", "ipAddress": "(ignore)", "trustedBy": "foo.yahoo.com" }, { "hostname": "cfg3", - "type": "config", "ipAddress": "(ignore)", "trustedBy": "foo.yahoo.com" }, { "hostname": "foo.yahoo.com", - "type": "tenant", "ipAddress": "(ignore)", "trustedBy": "foo.yahoo.com" }, { "hostname": "host1.yahoo.com", - "type": "tenant", "ipAddress": "::1", "trustedBy": "foo.yahoo.com" }, { "hostname": "host1.yahoo.com", - "type": "tenant", "ipAddress": "127.0.0.1", "trustedBy": "foo.yahoo.com" }, { "hostname": "host10.yahoo.com", - "type": "tenant", "ipAddress": "::1", "trustedBy": "foo.yahoo.com" }, { "hostname": "host10.yahoo.com", - "type": "tenant", "ipAddress": "127.0.0.1", "trustedBy": "foo.yahoo.com" }, { "hostname": "host2.yahoo.com", - "type": "tenant", "ipAddress": "::1", "trustedBy": "foo.yahoo.com" }, { "hostname": "host2.yahoo.com", - "type": "tenant", "ipAddress": "127.0.0.1", "trustedBy": "foo.yahoo.com" }, { "hostname": "host3.yahoo.com", - "type": "tenant", "ipAddress": "::1", "trustedBy": "foo.yahoo.com" }, { "hostname": "host3.yahoo.com", - "type": "tenant", "ipAddress": "127.0.0.1", "trustedBy": "foo.yahoo.com" }, { "hostname": "host4.yahoo.com", - "type": "tenant", "ipAddress": "::1", "trustedBy": "foo.yahoo.com" }, { "hostname": "host4.yahoo.com", - "type": "tenant", "ipAddress": "127.0.0.1", "trustedBy": "foo.yahoo.com" }, { "hostname": "host5.yahoo.com", - "type": "tenant", "ipAddress": "::1", "trustedBy": "foo.yahoo.com" }, { "hostname": "host5.yahoo.com", - "type": "tenant", "ipAddress": "127.0.0.1", "trustedBy": "foo.yahoo.com" }, { "hostname": "host55.yahoo.com", - "type": "tenant", "ipAddress": "::1", "trustedBy": "foo.yahoo.com" }, { "hostname": "host55.yahoo.com", - "type": "tenant", "ipAddress": "127.0.0.1", "trustedBy": "foo.yahoo.com" }, { "hostname": "host6.yahoo.com", - "type": "tenant", "ipAddress": "::1", "trustedBy": "foo.yahoo.com" }, { "hostname": "host6.yahoo.com", - "type": "tenant", "ipAddress": "127.0.0.1", "trustedBy": "foo.yahoo.com" }, { "hostname": "host7.yahoo.com", - "type": "tenant", "ipAddress": "::1", "trustedBy": "foo.yahoo.com" }, { "hostname": "host7.yahoo.com", - "type": "tenant", "ipAddress": "127.0.0.1", "trustedBy": "foo.yahoo.com" }, { "hostname": "test-container-1", - "type": "tenant", "ipAddress": "::2", "trustedBy": "foo.yahoo.com" } |