diff options
author | Jon Bratseth <bratseth@gmail.com> | 2020-05-22 14:08:22 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@gmail.com> | 2020-05-22 14:08:22 +0200 |
commit | 2a7af074f571fb557f3bdc756be131a15288bff6 (patch) | |
tree | 653b610a73a357e81375b4bb4f612775bbf48a29 | |
parent | f1b06c72b6e6d1dbe8c6596b0fe8ec8c6f13a9f2 (diff) |
Clean up HostSpec constructors
22 files changed, 232 insertions, 95 deletions
diff --git a/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java b/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java index 1a1ed000478..6e21d0688ad 100644 --- a/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java +++ b/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java @@ -118,8 +118,7 @@ public class InMemoryProvisioner implements HostProvisioner { List<Host> defaultHosts = freeNodes.get(defaultResources); if (defaultHosts.isEmpty()) throw new IllegalArgumentException("No more hosts with default resources available"); Host newHost = freeNodes.removeValue(defaultResources, 0); - // Note: Always returns HostSpec with empty dockerImageRepo, which is OK since this method is never used when docker image repo is set - return new HostSpec(newHost.hostname(), newHost.aliases(), newHost.flavor(), Optional.empty(), newHost.version(), Optional.empty()); + return new HostSpec(newHost.hostname(), List.of(alias)); } @Override @@ -173,12 +172,12 @@ public class InMemoryProvisioner implements HostProvisioner { private HostSpec retire(HostSpec host) { return new HostSpec(host.hostname(), - host.aliases(), - host.flavor(), - Optional.of(host.membership().get().retire()), + host.realResources(), + host.advertisedResources(), + host.requestedResources().orElse(NodeResources.unspecified()), + host.membership().get().retire(), host.version(), Optional.empty(), - Optional.empty(), host.dockerImageRepo()); } @@ -190,7 +189,7 @@ public class InMemoryProvisioner implements HostProvisioner { // Check if the current allocations are compatible with the new request for (int i = allocation.size() - 1; i >= 0; i--) { Optional<NodeResources> currentResources = allocation.get(0).flavor().map(Flavor::resources); - if (currentResources.isEmpty() || requestedResources == NodeResources.unspecified) continue; + if (currentResources.isEmpty() || requestedResources.isUnspecified()) continue; if (!currentResources.get().compatibleWith(requestedResources)) { HostSpec removed = allocation.remove(i); freeNodes.put(currentResources.get(), new Host(removed.hostname())); // Return the node back to free pool @@ -202,7 +201,7 @@ public class InMemoryProvisioner implements HostProvisioner { // Find the smallest host that can fit the requested requested Optional<NodeResources> hostResources = freeNodes.keySet().stream() .sorted(new MemoryDiskCpu()) - .filter(resources -> requestedResources == NodeResources.unspecified || resources.satisfies(requestedResources)) + .filter(resources -> requestedResources.isUnspecified() || resources.satisfies(requestedResources)) .findFirst(); if (hostResources.isEmpty()) { if (canFail) @@ -214,10 +213,11 @@ public class InMemoryProvisioner implements HostProvisioner { Host newHost = freeNodes.removeValue(hostResources.get(), 0); if (freeNodes.get(hostResources.get()).isEmpty()) freeNodes.removeAll(hostResources.get()); ClusterMembership membership = ClusterMembership.from(clusterGroup, nextIndex++); - allocation.add(new HostSpec(newHost.hostname(), newHost.aliases(), - hostResources.map(Flavor::new), Optional.of(membership), + allocation.add(new HostSpec(newHost.hostname(), + hostResources.get(), hostResources.get(), requestedResources, + membership, newHost.version(), Optional.empty(), - requestedResources == NodeResources.unspecified ? Optional.empty() : Optional.of(requestedResources))); + Optional.empty())); } nextIndexInCluster.put(new Pair<>(clusterGroup.type(), clusterGroup.id()), nextIndex); diff --git a/config-model/src/main/java/com/yahoo/config/model/provision/SingleNodeProvisioner.java b/config-model/src/main/java/com/yahoo/config/model/provision/SingleNodeProvisioner.java index 1f0e0755667..212ad9e5a96 100644 --- a/config-model/src/main/java/com/yahoo/config/model/provision/SingleNodeProvisioner.java +++ b/config-model/src/main/java/com/yahoo/config/model/provision/SingleNodeProvisioner.java @@ -7,11 +7,14 @@ import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Flavor; import com.yahoo.config.provision.HostSpec; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.ProvisionLogger; import com.yahoo.net.HostName; +import com.yahoo.vespa.model.builder.xml.dom.NodesSpecification; import java.util.ArrayList; import java.util.List; +import java.util.Optional; /** * A host provisioner used when there is no hosts.xml file (using localhost as the only host) @@ -33,7 +36,10 @@ public class SingleNodeProvisioner implements HostProvisioner { public SingleNodeProvisioner(Flavor flavor) { host = new Host(HostName.getLocalhost()); - this.hostSpec = new HostSpec(host.hostname(), host.aliases(), flavor); + this.hostSpec = new HostSpec(host.hostname(), + List.of(), + flavor.resources(), flavor.resources(), + Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()); } @Override @@ -45,7 +51,11 @@ public class SingleNodeProvisioner implements HostProvisioner { public List<HostSpec> prepare(ClusterSpec cluster, Capacity capacity, ProvisionLogger logger) { // TODO: This should fail if capacity requested is more than 1 List<HostSpec> hosts = new ArrayList<>(); - hosts.add(new HostSpec(host.hostname(), host.aliases(), ClusterMembership.from(cluster, counter++))); + hosts.add(new HostSpec(host.hostname(), + List.of(), + NodeResources.unspecified(), NodeResources.unspecified(), + Optional.of(ClusterMembership.from(cluster, counter++)), + Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty())); return hosts; } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/HostResource.java b/config-model/src/main/java/com/yahoo/vespa/model/HostResource.java index 9dba6fde9d4..2390ad68aa2 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/HostResource.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/HostResource.java @@ -1,23 +1,16 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.model; -import com.yahoo.component.Version; import com.yahoo.config.application.api.DeployLogger; import com.yahoo.config.model.api.HostInfo; -import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.Flavor; import com.yahoo.config.provision.HostSpec; -import com.yahoo.config.provision.NodeResources; import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.stream.Collectors; /** @@ -44,7 +37,7 @@ public class HostResource implements Comparable<HostResource> { * @param host {@link com.yahoo.vespa.model.Host} object to bind to. */ public HostResource(Host host) { - this(host, new HostSpec(host.getHostname(), Optional.empty())); + this(host, new HostSpec(host.getHostname(), List.of())); } public HostResource(Host host, HostSpec spec) { diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ResourcesReductionValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ResourcesReductionValidator.java index 5343a322382..24b7b0949f6 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ResourcesReductionValidator.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ResourcesReductionValidator.java @@ -44,8 +44,8 @@ public class ResourcesReductionValidator implements ChangeValidator { ClusterSpec.Id clusterId, ValidationOverrides overrides, Instant now) { - if (current.minResources().nodeResources() == NodeResources.unspecified) return; - if (next.minResources().nodeResources() == NodeResources.unspecified) return; + if (current.minResources().nodeResources().isUnspecified()) return; + if (next.minResources().nodeResources().isUnspecified()) return; List<String> illegalChanges = Stream.of( validateResource("vCPU", diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java index ea47e490b12..8f737f02dca 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java @@ -159,8 +159,8 @@ public class NodesSpecification { * Returns a requirement from <code>count</code> non-dedicated nodes in one group */ public static NodesSpecification nonDedicated(int count, ConfigModelContext context) { - return new NodesSpecification(new ClusterResources(count, 1, NodeResources.unspecified), - new ClusterResources(count, 1, NodeResources.unspecified), + return new NodesSpecification(new ClusterResources(count, 1, NodeResources.unspecified()), + new ClusterResources(count, 1, NodeResources.unspecified()), false, context.getDeployState().getWantedNodeVespaVersion(), false, @@ -172,8 +172,8 @@ public class NodesSpecification { /** Returns a requirement from <code>count</code> dedicated nodes in one group */ public static NodesSpecification dedicated(int count, ConfigModelContext context) { - return new NodesSpecification(new ClusterResources(count, 1, NodeResources.unspecified), - new ClusterResources(count, 1, NodeResources.unspecified), + return new NodesSpecification(new ClusterResources(count, 1, NodeResources.unspecified()), + new ClusterResources(count, 1, NodeResources.unspecified()), true, context.getDeployState().getWantedNodeVespaVersion(), false, @@ -224,7 +224,7 @@ public class NodesSpecification { return new Pair<>(flavorResources, flavorResources); } else { - return new Pair<>(NodeResources.unspecified, NodeResources.unspecified); + return new Pair<>(NodeResources.unspecified(), NodeResources.unspecified()); } } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java index 741e5ebffd1..559a4b8b668 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java @@ -664,7 +664,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { .dockerImageRepository(deployState.getWantedDockerImageRepo()) .build(); int nodeCount = deployState.zone().environment().isProduction() ? 2 : 1; - Capacity capacity = Capacity.from(new ClusterResources(nodeCount, 1, NodeResources.unspecified), + Capacity capacity = Capacity.from(new ClusterResources(nodeCount, 1, NodeResources.unspecified()), false, !deployState.getProperties().isBootstrap()); var hosts = hostSystem.allocateHosts(clusterSpec, capacity, log); diff --git a/config-model/src/test/java/com/yahoo/vespa/model/HostResourceTest.java b/config-model/src/test/java/com/yahoo/vespa/model/HostResourceTest.java index 6aea0593f8a..dff1d338ffe 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/HostResourceTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/HostResourceTest.java @@ -6,8 +6,10 @@ import com.yahoo.config.model.test.MockRoot; import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.HostSpec; +import com.yahoo.config.provision.NodeResources; import org.junit.Test; +import java.util.List; import java.util.Optional; import static com.yahoo.config.provision.ClusterSpec.Type.container; @@ -53,7 +55,10 @@ public class HostResourceTest { private static HostResource hostResourceWithMemberships(ClusterMembership membership) { return new HostResource(Host.createHost(null, "hostname"), - new HostSpec("hostname", Optional.of(membership))); + new HostSpec("hostname", + NodeResources.unspecified(), NodeResources.unspecified(), NodeResources.unspecified(), + membership, + Optional.empty(), Optional.empty(), Optional.empty())); } private static int counter = 0; diff --git a/config-model/src/test/java/com/yahoo/vespa/model/VespaModelFactoryTest.java b/config-model/src/test/java/com/yahoo/vespa/model/VespaModelFactoryTest.java index ac77b821d4a..a9bf8bdcc49 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/VespaModelFactoryTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/VespaModelFactoryTest.java @@ -17,7 +17,9 @@ import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.HostSpec; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.ProvisionLogger; +import com.yahoo.vespa.model.builder.xml.dom.NodesSpecification; import org.junit.Before; import org.junit.Test; @@ -103,17 +105,17 @@ public class VespaModelFactoryTest { @Override public HostSpec allocateHost(String alias) { return new HostSpec(hostName, - List.of(), - ClusterMembership.from(ClusterSpec.request(ClusterSpec.Type.admin, new ClusterSpec.Id(routingClusterName)).vespaVersion("6.42").build(), - 0)); + NodeResources.unspecified(), NodeResources.unspecified(), NodeResources.unspecified(), + ClusterMembership.from(ClusterSpec.request(ClusterSpec.Type.admin, new ClusterSpec.Id(routingClusterName)).vespaVersion("6.42").build(), 0), + Optional.empty(), Optional.empty(), Optional.empty()); } @Override public List<HostSpec> prepare(ClusterSpec cluster, Capacity capacity, ProvisionLogger logger) { return List.of(new HostSpec(hostName, - List.of(), - ClusterMembership.from(ClusterSpec.request(ClusterSpec.Type.container, new ClusterSpec.Id(routingClusterName)).vespaVersion("6.42").build(), - 0))); + NodeResources.unspecified(), NodeResources.unspecified(), NodeResources.unspecified(), + ClusterMembership.from(ClusterSpec.request(ClusterSpec.Type.container, new ClusterSpec.Id(routingClusterName)).vespaVersion("6.42").build(), 0), + Optional.empty(), Optional.empty(), Optional.empty())); } }; diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java index ca3198637f3..fdd7ae57f0f 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java @@ -766,7 +766,7 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase { assertEquals("KMP_SETTING=1 KMP_AFFINITY=granularity=fine,verbose,compact,1,0 ", qrStartConfig.qrs().env()); } - private void verifyAvailableprocessors(boolean isHosted, Flavor flavor, int expectProcessors) throws IOException, SAXException { + private void verifyAvailableprocessors(boolean isHosted, Flavor flavor, int expectProcessors) { DeployState deployState = new DeployState.Builder() .modelHostProvisioner(flavor != null ? new SingleNodeProvisioner(flavor) : new SingleNodeProvisioner()) .properties(new TestProperties() @@ -790,7 +790,7 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase { } @Test - public void requireThatAvailableProcessorsFollowFlavor() throws IOException, SAXException { + public void requireThatAvailableProcessorsFollowFlavor() { verifyAvailableprocessors(false, null,0); verifyAvailableprocessors(true, null,0); verifyAvailableprocessors(true, new Flavor(new FlavorsConfig.Flavor.Builder().name("test-flavor").minCpuCores(9).build()), 9); diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/Capacity.java b/config-provisioning/src/main/java/com/yahoo/config/provision/Capacity.java index f723575c342..1a1cd243f89 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/Capacity.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/Capacity.java @@ -90,7 +90,7 @@ public final class Capacity { /** Creates this from a node type */ public static Capacity fromRequiredNodeType(NodeType type) { - return from(new ClusterResources(0, 0, NodeResources.unspecified), true, false, type); + return from(new ClusterResources(0, 0, NodeResources.unspecified()), true, false, type); } private static Capacity from(ClusterResources resources, boolean required, boolean canFail, NodeType type) { diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/HostSpec.java b/config-provisioning/src/main/java/com/yahoo/config/provision/HostSpec.java index 2a5d27a0fe7..c8c8a349fee 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/HostSpec.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/HostSpec.java @@ -22,11 +22,13 @@ public class HostSpec implements Comparable<HostSpec> { /** Aliases of this host */ private final List<String> aliases; + private final NodeResources realResources; + + private final NodeResources advertisedResources; + /** The current membership role of this host in the cluster it belongs to */ private final Optional<ClusterMembership> membership; - private final Optional<Flavor> flavor; - private final Optional<Version> version; private final Optional<DockerImage> dockerImageRepo; @@ -35,55 +37,130 @@ public class HostSpec implements Comparable<HostSpec> { private final Optional<NodeResources> requestedResources; + // TODO: Remove after June 2020 + @Deprecated public HostSpec(String hostname, Optional<ClusterMembership> membership) { - this(hostname, new ArrayList<>(), Optional.empty(), membership); + this(hostname, new ArrayList<>(), + NodeResources.unspecified(), NodeResources.unspecified(), + membership, + Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()); } + // TODO: Remove after June 2020 + @Deprecated public HostSpec(String hostname, ClusterMembership membership, Flavor flavor, Optional<Version> version) { - this(hostname, new ArrayList<>(), Optional.of(flavor), Optional.of(membership), version); + this(hostname, new ArrayList<>(), + flavor.resources(), flavor.resources(), + Optional.of(membership), version, Optional.empty(), Optional.empty(), Optional.empty()); } + /** Create a host in a non-cloud system, where hosts are specified in config */ public HostSpec(String hostname, List<String> aliases) { - this(hostname, aliases, Optional.empty(), Optional.empty()); + this(hostname, aliases, + NodeResources.unspecified(), NodeResources.unspecified(), + Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()); } + // TODO: Remove after June 2020 + @Deprecated public HostSpec(String hostname, List<String> aliases, Flavor flavor) { - this(hostname, aliases, Optional.of(flavor), Optional.empty()); + this(hostname, aliases, + flavor.resources(), flavor.resources(), + Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()); } + // TODO: Remove after June 2020 + @Deprecated public HostSpec(String hostname, List<String> aliases, ClusterMembership membership) { - this(hostname, aliases, Optional.empty(), Optional.of(membership)); + this(hostname, aliases, + NodeResources.unspecified(), NodeResources.unspecified(), + Optional.of(membership), + Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()); } + // TODO: Remove after June 2020 + @Deprecated public HostSpec(String hostname, List<String> aliases, Optional<Flavor> flavor, Optional<ClusterMembership> membership) { - this(hostname, aliases, flavor, membership, Optional.empty()); + this(hostname, aliases, + flavor.map(f -> f.resources()).orElse(NodeResources.unspecified()), + flavor.map(f -> f.resources()).orElse(NodeResources.unspecified()), + membership, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()); } + // TODO: Remove after June 2020 + @Deprecated public HostSpec(String hostname, List<String> aliases, Optional<Flavor> flavor, Optional<ClusterMembership> membership, Optional<Version> version) { - this(hostname, aliases, flavor, membership, version, Optional.empty()); + this(hostname, aliases, + flavor.map(f -> f.resources()).orElse(NodeResources.unspecified()), + flavor.map(f -> f.resources()).orElse(NodeResources.unspecified()), + membership, version, + Optional.empty(), Optional.empty(), Optional.empty()); } + // TODO: Remove after June 2020 + @Deprecated public HostSpec(String hostname, List<String> aliases, Optional<Flavor> flavor, Optional<ClusterMembership> membership, Optional<Version> version, Optional<NetworkPorts> networkPorts) { - this(hostname, aliases, flavor, membership, version, networkPorts, Optional.empty()); + this(hostname, aliases, + flavor.map(f -> f.resources()).orElse(NodeResources.unspecified()), + flavor.map(f -> f.resources()).orElse(NodeResources.unspecified()), + membership, version, networkPorts, + Optional.empty(), + Optional.empty()); } - public HostSpec(String hostname, List<String> aliases, Optional<Flavor> flavor, + // TODO: Remove after June 2020 + @Deprecated + public HostSpec(String hostname, List<String> aliases, + Optional<Flavor> flavor, Optional<ClusterMembership> membership, Optional<Version> version, Optional<NetworkPorts> networkPorts, Optional<NodeResources> requestedResources) { - this(hostname, aliases, flavor, membership, version, networkPorts, requestedResources, Optional.empty()); + this(hostname, aliases, + flavor.map(f -> f.resources()).orElse(NodeResources.unspecified()), + flavor.map(f -> f.resources()).orElse(NodeResources.unspecified()), + membership, version, networkPorts, requestedResources, Optional.empty()); } + // TODO: Remove after June 2020 + @Deprecated public HostSpec(String hostname, List<String> aliases, Optional<Flavor> flavor, Optional<ClusterMembership> membership, Optional<Version> version, Optional<NetworkPorts> networkPorts, Optional<NodeResources> requestedResources, Optional<DockerImage> dockerImageRepo) { + this(hostname, aliases, + flavor.map(f -> f.resources()).orElse(NodeResources.unspecified()), + flavor.map(f -> f.resources()).orElse(NodeResources.unspecified()), + membership, version, networkPorts, requestedResources, dockerImageRepo); + } + + /** Create a host in a hosted system */ + public HostSpec(String hostname, + NodeResources realResources, + NodeResources advertisedResurces, + NodeResources requestedResources, + ClusterMembership membership, + Optional<Version> version, + Optional<NetworkPorts> networkPorts, + Optional<DockerImage> dockerImageRepo) { + this(hostname, List.of(), + realResources, advertisedResurces, + Optional.of(membership), + version, networkPorts, requestedResources.asOptional(), dockerImageRepo); + } + + /** Create a fully specified host for any system */ + public HostSpec(String hostname, List<String> aliases, + NodeResources realResources, NodeResources advertisedResurces, + Optional<ClusterMembership> membership, Optional<Version> version, + Optional<NetworkPorts> networkPorts, Optional<NodeResources> requestedResources, + Optional<DockerImage> dockerImageRepo) { if (hostname == null || hostname.isEmpty()) throw new IllegalArgumentException("Hostname must be specified"); this.hostname = hostname; this.aliases = List.copyOf(aliases); - this.flavor = flavor; + this.realResources = realResources; + this.advertisedResources = advertisedResurces; this.membership = membership; this.version = Objects.requireNonNull(version, "Version cannot be null but can be empty"); this.networkPorts = Objects.requireNonNull(networkPorts, "Network ports cannot be null but can be empty"); @@ -97,7 +174,17 @@ public class HostSpec implements Comparable<HostSpec> { /** Returns the aliases of this host as an immutable list. This may be empty but never null. */ public List<String> aliases() { return aliases; } - public Optional<Flavor> flavor() { return flavor; } + /** The real resources available for Vespa processes on this node, after subtracting infrastructure overhead. */ + public NodeResources realResources() { return realResources; } + + /** The total advertised resources of this node, typically matching what's requested. */ + public NodeResources advertisedResources() { return advertisedResources; } + + /** A flavor contained the advertised resources of this host */ + // TODO: Remove after June 2020 + public Optional<Flavor> flavor() { + return advertisedResources.asOptional().map(resources -> new Flavor(resources)); + } /** Returns the current version of Vespa running on this node, or empty if not known */ public Optional<com.yahoo.component.Version> version() { return version; } @@ -108,13 +195,13 @@ public class HostSpec implements Comparable<HostSpec> { /** Returns the network port allocations on this host, or empty if not present */ public Optional<NetworkPorts> networkPorts() { return networkPorts; } - /** Returns the requested resources leading to this host being provisioned, or empty if not known */ + /** Returns the requested resources leading to this host being provisioned, or empty if unspecified */ public Optional<NodeResources> requestedResources() { return requestedResources; } public Optional<DockerImage> dockerImageRepo() { return dockerImageRepo; } public HostSpec withPorts(Optional<NetworkPorts> ports) { - return new HostSpec(hostname, aliases, flavor, membership, version, ports, requestedResources, dockerImageRepo); + return new HostSpec(hostname, aliases, realResources, advertisedResources, membership, version, ports, requestedResources, dockerImageRepo); } @Override diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java index 91604cd667d..e02aa7a5f22 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java @@ -3,6 +3,7 @@ package com.yahoo.config.provision; import java.util.Locale; import java.util.Objects; +import java.util.Optional; /** * The node resources required by an application cluster @@ -16,6 +17,7 @@ public class NodeResources { private static final double memoryUnitCost = 0.012; private static final double diskUnitCost = 0.0004; + // TODO: Make private after June 2020 public static final NodeResources unspecified = new NodeResources(0, 0, 0, 0); public enum DiskSpeed { @@ -252,7 +254,14 @@ public class NodeResources { return true; } - public boolean isUnspecified() { return this == unspecified; } + public static NodeResources unspecified() { return unspecified; } + + public boolean isUnspecified() { return this.equals(unspecified); } + + /** Returns this.isUnspecified() ? Optional.empty() : Optional.of(this) */ + public Optional<NodeResources> asOptional() { + return this.isUnspecified() ? Optional.empty() : Optional.of(this); + } private boolean equal(double a, double b) { return Math.abs(a - b) < 0.00000001; diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializer.java b/config-provisioning/src/main/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializer.java index 9ba26be072c..4ce9aa2d599 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializer.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializer.java @@ -45,6 +45,9 @@ public class AllocatedHostsSerializer { private static final String aliasesKey = "aliases"; private static final String hostSpecMembershipKey = "membership"; + private static final String realResourcesKey = "realResources"; + private static final String advertisedResourcesKey = "advertisedResources"; + // Flavor can be removed when all allocated nodes are docker nodes private static final String flavorKey = "flavor"; @@ -90,7 +93,9 @@ public class AllocatedHostsSerializer { object.setString(hostSpecDockerImageRepoKey, repo.repository()); }); }); - host.flavor().ifPresent(flavor -> toSlime(flavor, object)); + host.flavor().ifPresent(flavor -> toSlime(flavor, object)); // TODO: Remove this line after June 2020 + toSlime(host.realResources(), object.setObject(realResourcesKey)); + toSlime(host.advertisedResources(), object.setObject(advertisedResourcesKey)); host.requestedResources().ifPresent(resources -> toSlime(resources, object.setObject(requestedResourcesKey))); host.version().ifPresent(version -> object.setString(hostSpecCurrentVespaVersionKey, version.toFullString())); host.networkPorts().ifPresent(ports -> NetworkPortsSerializer.toSlime(ports, object.setArray(hostSpecNetworkPortsKey))); @@ -135,7 +140,8 @@ public class AllocatedHostsSerializer { private static HostSpec hostFromSlime(Inspector object, Optional<NodeFlavors> nodeFlavors) { return new HostSpec(object.field(hostSpecHostNameKey).asString(), aliasesFromSlime(object), - flavorFromSlime(object, nodeFlavors), + nodeResourcesFromSlime(object.field(realResourcesKey), object, nodeFlavors), + nodeResourcesFromSlime(object.field(advertisedResourcesKey), object, nodeFlavors), object.field(hostSpecMembershipKey).valid() ? Optional.of(membershipFromSlime(object)) : Optional.empty(), optionalString(object.field(hostSpecCurrentVespaVersionKey)).map(com.yahoo.component.Version::new), NetworkPortsSerializer.fromSlime(object.field(hostSpecNetworkPortsKey)), @@ -144,7 +150,7 @@ public class AllocatedHostsSerializer { } private static List<String> aliasesFromSlime(Inspector object) { - if ( ! object.field(aliasesKey).valid()) return Collections.emptyList(); + if ( ! object.field(aliasesKey).valid()) return List.of(); List<String> aliases = new ArrayList<>(); object.field(aliasesKey).traverse((ArrayTraverser)(index, alias) -> aliases.add(alias.asString())); return aliases; @@ -167,6 +173,18 @@ public class AllocatedHostsSerializer { storageTypeFromSlime(resources.field(storageTypeKey)))); } + private static NodeResources nodeResourcesFromSlime(Inspector resources, Inspector parent, + Optional<NodeFlavors> nodeFlavors) { + if ( ! resources.valid()) // TODO: Remove the fallback using nodeFlavors after June 2020 + return flavorFromSlime(parent, nodeFlavors).map(f -> f.resources()).orElse(NodeResources.unspecified); + return new NodeResources(resources.field(vcpuKey).asDouble(), + resources.field(memoryKey).asDouble(), + resources.field(diskKey).asDouble(), + resources.field(bandwidthKey).asDouble(), + diskSpeedFromSlime(resources.field(diskSpeedKey)), + storageTypeFromSlime(resources.field(storageTypeKey))); + } + private static NodeResources.DiskSpeed diskSpeedFromSlime(Inspector diskSpeed) { switch (diskSpeed.asString()) { case "fast" : return NodeResources.DiskSpeed.fast; diff --git a/config-provisioning/src/test/java/com/yahoo/config/provision/NodeFlavorsTest.java b/config-provisioning/src/test/java/com/yahoo/config/provision/NodeFlavorsTest.java index c259f31df16..e184e85c399 100644 --- a/config-provisioning/src/test/java/com/yahoo/config/provision/NodeFlavorsTest.java +++ b/config-provisioning/src/test/java/com/yahoo/config/provision/NodeFlavorsTest.java @@ -34,7 +34,7 @@ public class NodeFlavorsTest { NodeFlavors nodeFlavors = new NodeFlavors(config); Flavor banana = nodeFlavors.getFlavor("banana").get(); assertEquals(3, banana.cost()); - assertEquals(10, banana.resources().vcpu(), delta); + assertEquals(13, banana.resources().vcpu(), delta); assertEquals("10 * 1.3", 13, banana.resources().vcpu(), delta); } diff --git a/config-provisioning/src/test/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializerTest.java b/config-provisioning/src/test/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializerTest.java index 33157a01c07..a7e62f762e7 100644 --- a/config-provisioning/src/test/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializerTest.java +++ b/config-provisioning/src/test/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializerTest.java @@ -14,7 +14,6 @@ import com.yahoo.config.provisioning.FlavorsConfig; import org.junit.Test; import java.io.IOException; -import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; @@ -34,33 +33,44 @@ public class AllocatedHostsSerializerTest { NodeFlavors configuredFlavors = configuredFlavorsFrom("C/12/45/100", 12, 45, 100, 50, Flavor.Type.BARE_METAL); Set<HostSpec> hosts = new LinkedHashSet<>(); hosts.add(new HostSpec("empty", - Optional.empty())); + List.of())); hosts.add(new HostSpec("with-aliases", List.of("alias1", "alias2"))); hosts.add(new HostSpec("allocated", - List.of(), - Optional.empty(), - Optional.of(ClusterMembership.from("container/test/0/0", Version.fromString("6.73.1"), - Optional.of(DockerImage.fromString("docker.foo.com:4443/vespa/bar")))), - Optional.empty(), Optional.empty(), Optional.empty(), + NodeResources.unspecified(), + NodeResources.unspecified(), + NodeResources.unspecified(), + ClusterMembership.from("container/test/0/0", Version.fromString("6.73.1"), + Optional.of(DockerImage.fromString("docker.foo.com:4443/vespa/bar"))), + Optional.empty(), Optional.empty(), Optional.of(DockerImage.fromString("docker.foo.com:4443/vespa/bar")))); - hosts.add(new HostSpec("flavor-from-resources-1", - Collections.emptyList(), new Flavor(new NodeResources(0.5, 3.1, 4, 1)))); hosts.add(new HostSpec("flavor-from-resources-2", - Collections.emptyList(), - Optional.of(new Flavor(new NodeResources(0.5, 3.1, 4, 1, NodeResources.DiskSpeed.slow))), - Optional.empty(), + new NodeResources(0.5, 3.1, 4, 1, NodeResources.DiskSpeed.slow), + new NodeResources(1.0, 6.2, 8, 2, NodeResources.DiskSpeed.slow), + new NodeResources(0.5, 3.1, 4, 1, NodeResources.DiskSpeed.any), + ClusterMembership.from("container/test/0/0", Version.fromString("6.73.1"), + Optional.of(DockerImage.fromString("docker.foo.com:4443/vespa/bar"))), Optional.empty(), Optional.empty(), - Optional.of(new NodeResources(0.5, 3.1, 4, 1, NodeResources.DiskSpeed.any)))); - hosts.add(new HostSpec("configured-flavor", - Collections.emptyList(), configuredFlavors.getFlavorOrThrow("C/12/45/100"))); + Optional.empty())); hosts.add(new HostSpec("with-version", - Collections.emptyList(), Optional.empty(), Optional.empty(), Optional.of(Version.fromString("3.4.5")))); + NodeResources.unspecified(), + NodeResources.unspecified(), + NodeResources.unspecified(), + ClusterMembership.from("container/test/0/0", Version.fromString("6.73.1"), + Optional.of(DockerImage.fromString("docker.foo.com:4443/vespa/bar"))), + Optional.of(Version.fromString("3.4.5")), + Optional.empty(), Optional.empty())); hosts.add(new HostSpec("with-ports", - Collections.emptyList(), Optional.empty(), Optional.empty(), Optional.empty(), + NodeResources.unspecified(), + NodeResources.unspecified(), + NodeResources.unspecified(), + ClusterMembership.from("container/test/0/0", Version.fromString("6.73.1"), + Optional.of(DockerImage.fromString("docker.foo.com:4443/vespa/bar"))), + Optional.empty(), Optional.of(new NetworkPorts(List.of(new NetworkPorts.Allocation(1234, "service1", "configId1", "suffix1"), - new NetworkPorts.Allocation(4567, "service2", "configId2", "suffix2")))))); + new NetworkPorts.Allocation(4567, "service2", "configId2", "suffix2")))), + Optional.empty())); assertAllocatedHosts(AllocatedHosts.withHosts(hosts), configuredFlavors); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackageTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackageTest.java index 9c7da7134e6..b59b5a15760 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackageTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackageTest.java @@ -39,7 +39,10 @@ public class ZKApplicationPackageTest { private static final String TEST_FLAVOR_NAME = "test-flavor"; private static final Optional<Flavor> TEST_FLAVOR = new MockNodeFlavors().getFlavor(TEST_FLAVOR_NAME); private static final AllocatedHosts ALLOCATED_HOSTS = AllocatedHosts.withHosts( - Collections.singleton(new HostSpec("foo.yahoo.com", Collections.emptyList(), TEST_FLAVOR, Optional.empty(), + Collections.singleton(new HostSpec("foo.yahoo.com", + TEST_FLAVOR.get().resources(), + TEST_FLAVOR.get().resources(), + Optional.empty(), Optional.of(Version.fromString("6.0.1")), Optional.empty(), Optional.empty(), Optional.of(DockerImage.fromString("docker repo"))))); diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java index f0f53161d9b..475f2feaebd 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java @@ -40,8 +40,8 @@ public class AllocationOptimizer { AllocatableClusterResources current, Limits limits) { if (limits.isEmpty()) - limits = Limits.of(new ClusterResources(minimumNodes, 1, NodeResources.unspecified), - new ClusterResources(maximumNodes, maximumNodes, NodeResources.unspecified)); + limits = Limits.of(new ClusterResources(minimumNodes, 1, NodeResources.unspecified()), + new ClusterResources(maximumNodes, maximumNodes, NodeResources.unspecified())); Optional<AllocatableClusterResources> bestAllocation = Optional.empty(); for (int groups = limits.min().groups(); groups <= limits.max().groups(); groups++) { for (int nodes = limits.min().nodes(); nodes <= limits.max().nodes(); nodes++) { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Activator.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Activator.java index 36034b62cfb..1ce989dad08 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Activator.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Activator.java @@ -183,7 +183,7 @@ class Activator { for (Node node : nodes) { HostSpec hostSpec = getHost(node.hostname(), hosts); node = hostSpec.membership().get().retired() ? node.retire(nodeRepository.clock().instant()) : node.unretire(); - if (hostSpec.flavor().isPresent()) // Docker nodes may change flavor + if (hostSpec.flavor().isPresent() && ! hostSpec.flavor().get().resources().equals(node.flavor().resources())) // A resized node node = node.with(hostSpec.flavor().get()); Allocation allocation = node.allocation().get() .with(hostSpec.membership().get()) diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java index bd92357ea79..78ccf258675 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java @@ -7,6 +7,7 @@ import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; +import com.yahoo.config.provision.Flavor; import com.yahoo.config.provision.HostFilter; import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.NodeResources; @@ -196,12 +197,12 @@ public class NodeRepositoryProvisioner implements Provisioner { log.log(Level.FINE, () -> "Prepared node " + node.hostname() + " - " + node.flavor()); Allocation nodeAllocation = node.allocation().orElseThrow(IllegalStateException::new); hosts.add(new HostSpec(node.hostname(), - List.of(), - Optional.of(node.flavor()), - Optional.of(nodeAllocation.membership()), + nodeRepository.resourcesCalculator().realResourcesOf(node, nodeRepository), + node.flavor().resources(), + requestedResources, + nodeAllocation.membership(), node.status().vespaVersion(), nodeAllocation.networkPorts(), - requestedResources.isUnspecified() ? Optional.empty() : Optional.of(requestedResources), node.status().dockerImage())); if (nodeAllocation.networkPorts().isPresent()) { log.log(Level.FINE, () -> "Prepared node " + node.hostname() + " has port allocations"); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/os/OsVersionsTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/os/OsVersionsTest.java index 6b03def7e57..914008af227 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/os/OsVersionsTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/os/OsVersionsTest.java @@ -229,7 +229,6 @@ public class OsVersionsTest { // All hosts are deprovisioning assertEquals(hostCount, hostNodes.get().deprovisioning().size()); - // Nodes complete their upgrade by being reprovisioned completeUpgradeOf(hostNodes.get().deprovisioning().asList(), NodeType.confighost); assertEquals(hostCount, hostNodes.get().onOsVersion(version1).size()); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java index e1a66d2ae21..7ef780b8b7f 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java @@ -270,8 +270,8 @@ public class DynamicDockerProvisionTest { app1, cluster1); // Stop specifying node resources - tester.activate(app1, cluster1, Capacity.from(new ClusterResources(6, 3, NodeResources.unspecified), - new ClusterResources(9, 3, NodeResources.unspecified))); + tester.activate(app1, cluster1, Capacity.from(new ClusterResources(6, 3, NodeResources.unspecified()), + new ClusterResources(9, 3, NodeResources.unspecified()))); tester.assertNodes("Existing allocation is preserved", 9, 3, 2, 20, 15, app1, cluster1); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java index 03c07515cd5..9e211fd497c 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java @@ -418,8 +418,8 @@ public class ProvisioningTest { ApplicationId app1 = tester.makeApplicationId("app1"); ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build(); - tester.activate(app1, cluster1, Capacity.from(new ClusterResources(2, 1, NodeResources.unspecified), - new ClusterResources(4, 1, NodeResources.unspecified))); + tester.activate(app1, cluster1, Capacity.from(new ClusterResources(2, 1, NodeResources.unspecified()), + new ClusterResources(4, 1, NodeResources.unspecified()))); tester.assertNodes("Initial allocation at min with default resources", 2, 1, 1.5, 8, 50, 0.3, app1, cluster1); @@ -479,8 +479,8 @@ public class ProvisioningTest { app1, cluster1); // Stop specifying node resources - tester.activate(app1, cluster1, Capacity.from(new ClusterResources(6, 3, NodeResources.unspecified), - new ClusterResources(9, 3, NodeResources.unspecified))); + tester.activate(app1, cluster1, Capacity.from(new ClusterResources(6, 3, NodeResources.unspecified()), + new ClusterResources(9, 3, NodeResources.unspecified()))); tester.assertNodes("No change", 9, 3, 8, 35, 15, app1, cluster1); @@ -622,7 +622,7 @@ public class ProvisioningTest { tester.makeReadyHosts(4, defaultResources).deployZoneApp(); ApplicationId application = tester.makeApplicationId(); ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("music")).vespaVersion("4.5.6").build(); - tester.prepare(application, cluster, Capacity.from(new ClusterResources(5, 1, NodeResources.unspecified), false, false)); + tester.prepare(application, cluster, Capacity.from(new ClusterResources(5, 1, NodeResources.unspecified()), false, false)); // No exception; Success } @@ -881,7 +881,7 @@ public class ProvisioningTest { allHosts.addAll(content0); allHosts.addAll(content1); - Function<Integer, Capacity> capacity = count -> Capacity.from(new ClusterResources(count, 1, NodeResources.unspecified), required, true); + Function<Integer, Capacity> capacity = count -> Capacity.from(new ClusterResources(count, 1, NodeResources.unspecified()), required, true); int expectedContainer0Size = tester.capacityPolicies().decideSize(container0Size, capacity.apply(container0Size), containerCluster0, application); int expectedContainer1Size = tester.capacityPolicies().decideSize(container1Size, capacity.apply(container1Size), containerCluster1, application); int expectedContent0Size = tester.capacityPolicies().decideSize(content0Size, capacity.apply(content0Size), contentCluster0, application); |