diff options
41 files changed, 261 insertions, 168 deletions
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java index ebc9aa247d8..e23d6c5b968 100644 --- a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java @@ -49,6 +49,7 @@ public interface ModelContext { boolean hostedVespa(); Zone zone(); Set<Rotation> rotations(); + boolean isBootstrap(); } } diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/DeployProperties.java b/config-model/src/main/java/com/yahoo/config/model/deploy/DeployProperties.java index d3e91f8866c..b259f6cf3fb 100644 --- a/config-model/src/main/java/com/yahoo/config/model/deploy/DeployProperties.java +++ b/config-model/src/main/java/com/yahoo/config/model/deploy/DeployProperties.java @@ -25,6 +25,7 @@ public class DeployProperties { private final String athenzDnsSuffix; private final boolean hostedVespa; private final Version vespaVersion; + private final boolean isBootstrap; private DeployProperties(boolean multitenant, ApplicationId applicationId, @@ -33,7 +34,8 @@ public class DeployProperties { boolean hostedVespa, URI ztsUrl, String athenzDnsSuffix, - Version vespaVersion) { + Version vespaVersion, + boolean isBootstrap) { this.loadBalancerName = loadBalancerName; this.ztsUrl = ztsUrl; this.athenzDnsSuffix = athenzDnsSuffix; @@ -42,9 +44,9 @@ public class DeployProperties { this.applicationId = applicationId; this.serverSpecs.addAll(configServerSpecs); this.hostedVespa = hostedVespa; + this.isBootstrap = isBootstrap; } - public boolean multitenant() { return multitenant; } @@ -78,6 +80,9 @@ public class DeployProperties { return vespaVersion; } + /** Returns whether this deployment happens during bootstrap *prepare* (not set on activate) */ + public boolean isBootstrap() { return isBootstrap; } + public static class Builder { private ApplicationId applicationId = ApplicationId.defaultId(); @@ -88,6 +93,7 @@ public class DeployProperties { private String athenzDnsSuffix; private boolean hostedVespa = false; private Version vespaVersion = Version.fromIntValues(1, 0, 0); + private boolean isBootstrap = false; public Builder applicationId(ApplicationId applicationId) { this.applicationId = applicationId; @@ -129,8 +135,13 @@ public class DeployProperties { return this; } + public Builder isBootstrap(boolean isBootstrap) { + this.isBootstrap = isBootstrap; + return this; + } + public DeployProperties build() { - return new DeployProperties(multitenant, applicationId, configServerSpecs, loadBalancerName, hostedVespa, ztsUrl, athenzDnsSuffix, vespaVersion); + return new DeployProperties(multitenant, applicationId, configServerSpecs, loadBalancerName, hostedVespa, ztsUrl, athenzDnsSuffix, vespaVersion, isBootstrap); } } 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 e6b89d7f390..c744c509b9a 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 @@ -24,7 +24,8 @@ import java.util.Optional; import java.util.Set; /** - * In memory host provisioner. NB! ATM cannot be reused after allocate has been called. + * In memory host provisioner for testing only. + * NB! ATM cannot be reused after allocate has been called. * * @author hmusum * @author bratseth @@ -97,6 +98,9 @@ public class InMemoryProvisioner implements HostProvisioner { return hosts; } + /** Returns the current allocations of this as a mutable map */ + public Map<ClusterSpec, List<HostSpec>> allocations() { return allocations; } + @Override public HostSpec allocateHost(String alias) { if (legacyMapping.containsKey(alias)) return legacyMapping.get(alias); @@ -129,14 +133,16 @@ public class InMemoryProvisioner implements HostProvisioner { allocation.addAll(allocateHostGroup(cluster.with(Optional.of(ClusterSpec.Group.from(0))), flavor, capacity, - startIndexForClusters)); + startIndexForClusters, + requestedCapacity.canFail())); } else { for (int i = 0; i < groups; i++) { allocation.addAll(allocateHostGroup(cluster.with(Optional.of(ClusterSpec.Group.from(i))), flavor, capacity / groups, - allocation.size())); + allocation.size(), + requestedCapacity.canFail())); } } for (ListIterator<HostSpec> i = allocation.listIterator(); i.hasNext(); ) { @@ -155,13 +161,18 @@ public class InMemoryProvisioner implements HostProvisioner { host.version()); } - private List<HostSpec> allocateHostGroup(ClusterSpec clusterGroup, String flavor, int nodesInGroup, int startIndex) { + private List<HostSpec> allocateHostGroup(ClusterSpec clusterGroup, String flavor, int nodesInGroup, int startIndex, boolean canFail) { List<HostSpec> allocation = allocations.getOrDefault(clusterGroup, new ArrayList<>()); allocations.put(clusterGroup, allocation); int nextIndex = nextIndexInCluster.getOrDefault(new Pair<>(clusterGroup.type(), clusterGroup.id()), startIndex); while (allocation.size() < nodesInGroup) { - if (freeNodes.get(flavor).isEmpty()) throw new IllegalArgumentException("Insufficient capacity of flavor '" + flavor + "'"); + if (freeNodes.get(flavor).isEmpty()) { + if (canFail) + throw new IllegalArgumentException("Insufficient capacity of flavor '" + flavor + "'"); + else + break; + } Host newHost = freeNodes.removeValue(flavor, 0); ClusterMembership membership = ClusterMembership.from(clusterGroup, nextIndex++); allocation.add(new HostSpec(newHost.hostname(), newHost.aliases(), newHost.flavor(), Optional.of(membership), newHost.version())); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java b/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java index a6d24f33b5d..e88153342f9 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java @@ -153,6 +153,7 @@ public class VespaModelFactory implements ModelFactory { .multitenant(properties.multitenant()) .hostedVespa(properties.hostedVespa()) .vespaVersion(getVersion()) + .isBootstrap(properties.isBootstrap()) .build(); } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java index 44de53991f4..ff8d3211d47 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java @@ -45,17 +45,16 @@ public class DomAdminV4Builder extends DomAdminBuilderBase { protected void doBuildAdmin(Admin admin, Element w3cAdminElement) { ModelElement adminElement = new ModelElement(w3cAdminElement); admin.addConfigservers(getConfigServersFromSpec(admin)); - Version version = context.getDeployState().getWantedNodeVespaVersion(); - + // Note: These two elements only exists in admin version 4.0 // This build handles admin version 3.0 by ignoring its content (as the content is not useful) Optional<NodesSpecification> requestedSlobroks = - NodesSpecification.optionalDedicatedFromParent(adminElement.getChild("slobroks"), version); + NodesSpecification.optionalDedicatedFromParent(adminElement.getChild("slobroks"), context); Optional<NodesSpecification> requestedLogservers = - NodesSpecification.optionalDedicatedFromParent(adminElement.getChild("logservers"), version); + NodesSpecification.optionalDedicatedFromParent(adminElement.getChild("logservers"), context); - assignSlobroks(requestedSlobroks.orElse(NodesSpecification.nonDedicated(3, version)), admin); - assignLogserver(requestedLogservers.orElse(NodesSpecification.nonDedicated(1, version)), admin); + assignSlobroks(requestedSlobroks.orElse(NodesSpecification.nonDedicated(3, context)), admin); + assignLogserver(requestedLogservers.orElse(NodesSpecification.nonDedicated(1, context)), admin); addLogForwarders(adminElement.getChild("logforwarding"), admin); } 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 f7ed7241133..94359e8672e 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 @@ -2,6 +2,7 @@ package com.yahoo.vespa.model.builder.xml.dom; import com.yahoo.component.Version; +import com.yahoo.config.model.ConfigModelContext; import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.ClusterSpec; @@ -34,34 +35,38 @@ public class NodesSpecification { * at the discretion of the component fulfilling it */ private final boolean required; - - /** The flavor the nodes should have, or empty to use the default */ - private final Optional<String> flavor; + private final boolean canFail; + private final boolean exclusive; + /** The flavor the nodes should have, or empty to use the default */ + private final Optional<String> flavor; + /** The identifier of the custom docker image layer to use (not supported yet) */ private final Optional<String> dockerImage; - private NodesSpecification(boolean dedicated, int count, int groups, Version version, boolean required, - boolean exclusive, + private NodesSpecification(boolean dedicated, int count, int groups, Version version, + boolean required, boolean canFail, boolean exclusive, Optional<String> flavor, Optional<String> dockerImage) { this.dedicated = dedicated; this.count = count; this.groups = groups; this.version = version; this.required = required; + this.canFail = canFail; this.exclusive = exclusive; this.flavor = flavor; this.dockerImage = dockerImage; } - private NodesSpecification(boolean dedicated, Version version, ModelElement nodesElement) { + private NodesSpecification(boolean dedicated, boolean canFail, Version version, ModelElement nodesElement) { this(dedicated, nodesElement.requiredIntegerAttribute("count"), nodesElement.getIntegerAttribute("groups", 1), version, nodesElement.getBooleanAttribute("required", false), + canFail, nodesElement.getBooleanAttribute("exclusive", false), Optional.ofNullable(nodesElement.getStringAttribute("flavor")), Optional.ofNullable(nodesElement.getStringAttribute("docker-image"))); @@ -70,8 +75,11 @@ public class NodesSpecification { /** * Returns a requirement for dedicated nodes taken from the given <code>nodes</code> element */ - public static NodesSpecification from(ModelElement nodesElement, Version version) { - return new NodesSpecification(true, version, nodesElement); + public static NodesSpecification from(ModelElement nodesElement, ConfigModelContext context) { + return new NodesSpecification(true, + ! context.getDeployState().getProperties().isBootstrap(), + context.getDeployState().getWantedNodeVespaVersion(), + nodesElement); } /** @@ -79,11 +87,11 @@ public class NodesSpecification { * contained in the given parent element, or empty if the parent element is null, or the nodes elements * is not present. */ - public static Optional<NodesSpecification> fromParent(ModelElement parentElement, Version version) { + public static Optional<NodesSpecification> fromParent(ModelElement parentElement, ConfigModelContext context) { if (parentElement == null) return Optional.empty(); ModelElement nodesElement = parentElement.getChild("nodes"); if (nodesElement == null) return Optional.empty(); - return Optional.of(from(nodesElement, version)); + return Optional.of(from(nodesElement, context)); } /** @@ -91,18 +99,28 @@ public class NodesSpecification { * contained in the given parent element, or empty if the parent element is null, or the nodes elements * is not present. */ - public static Optional<NodesSpecification> optionalDedicatedFromParent(ModelElement parentElement, Version version) { + public static Optional<NodesSpecification> optionalDedicatedFromParent(ModelElement parentElement, + ConfigModelContext context) { if (parentElement == null) return Optional.empty(); ModelElement nodesElement = parentElement.getChild("nodes"); if (nodesElement == null) return Optional.empty(); return Optional.of(new NodesSpecification(nodesElement.getBooleanAttribute("dedicated", false), - version, nodesElement)); + ! context.getDeployState().getProperties().isBootstrap(), + context.getDeployState().getWantedNodeVespaVersion(), + nodesElement)); } /** Returns a requirement from <code>count</code> nondedicated nodes in one group */ - public static NodesSpecification nonDedicated(int count, Version version) { - return new NodesSpecification(false, count, 1, version, false, false, - Optional.empty(), Optional.empty()); + public static NodesSpecification nonDedicated(int count, ConfigModelContext context) { + return new NodesSpecification(false, + count, + 1, + context.getDeployState().getWantedNodeVespaVersion(), + false, + ! context.getDeployState().getProperties().isBootstrap(), + false, + Optional.empty(), + Optional.empty()); } /** @@ -124,9 +142,12 @@ public class NodesSpecification { /** Returns the number of host groups this specifies. Default is 1 */ public int groups() { return groups; } - public Map<HostResource, ClusterMembership> provision(HostSystem hostSystem, ClusterSpec.Type clusterType, ClusterSpec.Id clusterId, DeployLogger logger) { + public Map<HostResource, ClusterMembership> provision(HostSystem hostSystem, + ClusterSpec.Type clusterType, + ClusterSpec.Id clusterId, + DeployLogger logger) { ClusterSpec cluster = ClusterSpec.request(clusterType, clusterId, version, exclusive); - return hostSystem.allocateHosts(cluster, Capacity.fromNodeCount(count, flavor, required), groups, logger); + return hostSystem.allocateHosts(cluster, Capacity.fromNodeCount(count, flavor, required, canFail), groups, logger); } @Override 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 2572b0d772b..a007c4765c0 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 @@ -151,7 +151,6 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { private void addClusterContent(ContainerCluster cluster, Element spec, ConfigModelContext context) { DocumentFactoryBuilder.buildDocumentFactories(cluster, spec); - addConfiguredComponents(cluster, spec); addSecretStore(cluster, spec); addHandlers(cluster, spec); @@ -514,7 +513,11 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { ClusterSpec.Id.from(cluster.getName()), context.getDeployState().getWantedNodeVespaVersion(), false); - return cluster.getHostSystem().allocateHosts(clusterSpec, Capacity.fromNodeCount(1), 1, logger).keySet().iterator().next(); + Capacity capacity = Capacity.fromNodeCount(1, + Optional.empty(), + false, + ! context.getDeployState().getProperties().isBootstrap()); + return cluster.getHostSystem().allocateHosts(clusterSpec, capacity, 1, logger).keySet().iterator().next(); } } else { return cluster.getHostSystem().getHost(Container.SINGLENODE_CONTAINER_SERVICESPEC); @@ -522,8 +525,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { } private List<Container> createNodesFromNodeCount(ContainerCluster cluster, Element nodesElement, ConfigModelContext context) { - NodesSpecification nodesSpecification = NodesSpecification.from(new ModelElement(nodesElement), - context.getDeployState().getWantedNodeVespaVersion()); + NodesSpecification nodesSpecification = NodesSpecification.from(new ModelElement(nodesElement), context); Map<HostResource, ClusterMembership> hosts = nodesSpecification.provision(cluster.getRoot().getHostSystem(), ClusterSpec.Type.container, ClusterSpec.Id.from(cluster.getName()), @@ -559,8 +561,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { cluster.setHostClusterId(referenceId); Map<HostResource, ClusterMembership> hosts = - StorageGroup.provisionHosts(NodesSpecification.from(new ModelElement(referencedNodesElement), - context.getDeployState().getWantedNodeVespaVersion()), + StorageGroup.provisionHosts(NodesSpecification.from(new ModelElement(referencedNodesElement), context), referenceId, cluster.getRoot().getHostSystem(), context.getDeployLogger()); @@ -582,9 +583,9 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { NodesSpecification nodesSpec; if (contentNodesElementOrNull == null) - nodesSpec = NodesSpecification.nonDedicated(1, context.getDeployState().getWantedNodeVespaVersion()); + nodesSpec = NodesSpecification.nonDedicated(1, context); else - nodesSpec = NodesSpecification.from(new ModelElement(contentNodesElementOrNull), context.getDeployState().getWantedNodeVespaVersion()); + nodesSpec = NodesSpecification.from(new ModelElement(contentNodesElementOrNull), context); Map<HostResource, ClusterMembership> hosts = StorageGroup.provisionHosts(nodesSpec, diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java b/config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java index edf3f7c840d..1b5baefe4e4 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java @@ -409,9 +409,9 @@ public class StorageGroup { Optional<NodesSpecification> nodeRequirement; if (nodesElement.isPresent() && nodesElement.get().getStringAttribute("count") != null ) // request these nodes - nodeRequirement = Optional.of(NodesSpecification.from(nodesElement.get(), context.getDeployState().getWantedNodeVespaVersion())); + nodeRequirement = Optional.of(NodesSpecification.from(nodesElement.get(), context)); else if (! nodesElement.isPresent() && subGroups.isEmpty() && owner.getRoot().getDeployState().isHosted()) // request one node - nodeRequirement = Optional.of(NodesSpecification.nonDedicated(1, context.getDeployState().getWantedNodeVespaVersion())); + nodeRequirement = Optional.of(NodesSpecification.nonDedicated(1, context)); else // Nodes or groups explicitly listed, and/opr not hosted - resolve in GroupBuilder nodeRequirement = Optional.empty(); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java index 68ae4d2b242..154f719ff10 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java @@ -292,8 +292,8 @@ public class ContentCluster extends AbstractConfigProducer implements else if (admin.multitenant()) { String clusterName = contentClusterName + "-controllers"; NodesSpecification nodesSpecification = - NodesSpecification.optionalDedicatedFromParent(contentElement.getChild("controllers"), context.getDeployState().getWantedNodeVespaVersion()) - .orElse(NodesSpecification.nonDedicated(3, context.getDeployState().getWantedNodeVespaVersion())); + NodesSpecification.optionalDedicatedFromParent(contentElement.getChild("controllers"), context) + .orElse(NodesSpecification.nonDedicated(3, context)); Collection<HostResource> hosts = nodesSpecification.isDedicated() ? getControllerHosts(nodesSpecification, admin, clusterName, context) : drawControllerHosts(nodesSpecification.count(), rootGroup, containers); diff --git a/config-model/src/test/java/com/yahoo/config/model/MockModelContext.java b/config-model/src/test/java/com/yahoo/config/model/MockModelContext.java index c3fffa96076..6821b7e3b4b 100644 --- a/config-model/src/test/java/com/yahoo/config/model/MockModelContext.java +++ b/config-model/src/test/java/com/yahoo/config/model/MockModelContext.java @@ -127,6 +127,9 @@ public class MockModelContext implements ModelContext { public Set<Rotation> rotations() { return new HashSet<>(); } + + @Override + public boolean isBootstrap() { return false; } }; } } 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 4d221af45a0..2261affb65b 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 @@ -209,6 +209,9 @@ public class VespaModelFactoryTest { public String athenzDnsSuffix() { return null; } + + @Override + public boolean isBootstrap() { return false; } }; } }; 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 299b7282e4a..5204da08307 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 @@ -15,13 +15,16 @@ public final class Capacity { private final boolean required; + private final boolean canFail; + private final Optional<String> flavor; private final NodeType type; - private Capacity(int nodeCount, Optional<String> flavor, boolean required, NodeType type) { + private Capacity(int nodeCount, Optional<String> flavor, boolean required, boolean canFail, NodeType type) { this.nodeCount = nodeCount; this.required = required; + this.canFail = canFail; this.flavor = flavor; this.type = type; } @@ -39,6 +42,13 @@ public final class Capacity { public boolean isRequired() { return required; } /** + * Returns true if an exception should be thrown if the specified capacity can not be satisfied + * (to whatever policies are applied and taking required true/false into account). + * Returns false if it is preferable to still succeed with partially satisfied capacity. + */ + public boolean canFail() { return canFail; } + + /** * Returns the node type (role) requested. This is tenant nodes by default. * If some other type is requested the node count and flavor may be ignored * and all nodes of the requested type returned instead. @@ -52,46 +62,22 @@ public final class Capacity { /** Creates this from a desired node count: The request may be satisfied with a smaller number of nodes. */ public static Capacity fromNodeCount(int capacity) { - return fromNodeCount(capacity, Optional.empty(), false); + return fromNodeCount(capacity, Optional.empty(), false, true); } + // TODO: Remove after July 2018 + @Deprecated public static Capacity fromNodeCount(int nodeCount, Optional<String> flavor, boolean required) { - return new Capacity(nodeCount, flavor, required, NodeType.tenant); - } - - /** Creates this from a node type */ - public static Capacity fromRequiredNodeType(NodeType type) { - return new Capacity(0, Optional.empty(), true, type); - } - - /** Creates this from a desired node count: The request may be satisfied with a smaller number of nodes. */ - // TODO: Remove after April 2018 - public static Capacity fromNodeCount(int nodeCount, String flavor) { - return fromNodeCount(nodeCount, Optional.of(flavor)); - } - - /** Creates this from a desired node count: The request may be satisfied with a smaller number of nodes. */ - // TODO: Remove after April 2018 - public static Capacity fromNodeCount(int nodeCount, Optional<String> flavor) { - return new Capacity(nodeCount, flavor, false, NodeType.tenant); + return new Capacity(nodeCount, flavor, required, true, NodeType.tenant); } - /** Creates this from a required node count: Requests must fail unless the node count can be satisfied exactly */ - // TODO: Remove after April 2018 - public static Capacity fromRequiredNodeCount(int nodeCount) { - return fromRequiredNodeCount(nodeCount, Optional.empty()); + public static Capacity fromNodeCount(int nodeCount, Optional<String> flavor, boolean required, boolean canFail) { + return new Capacity(nodeCount, flavor, required, canFail, NodeType.tenant); } - /** Creates this from a required node count: Requests must fail unless the node count can be satisfied exactly */ - // TODO: Remove after April 2018 - public static Capacity fromRequiredNodeCount(int nodeCount, String flavor) { - return fromRequiredNodeCount(nodeCount, Optional.of(flavor)); - } - - /** Creates this from a required node count: Requests must fail unless the node count can be satisfied exactly */ - // TODO: Remove after April 2018 - public static Capacity fromRequiredNodeCount(int nodeCount, Optional<String> flavor) { - return new Capacity(nodeCount, flavor, true, NodeType.tenant); + /** Creates this from a node type */ + public static Capacity fromRequiredNodeType(NodeType type) { + return new Capacity(0, Optional.empty(), true, false, type); } } diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java index 837e062e356..caacabd09b5 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java @@ -51,11 +51,6 @@ public final class ClusterSpec { */ public boolean isExclusive() { return exclusive; } - // TODO: Remove after April 2018 - public ClusterSpec changeGroup(Optional<Group> newGroup) { - return with(newGroup); - } - public ClusterSpec with(Optional<Group> newGroup) { return new ClusterSpec(type, id, newGroup, vespaVersion, exclusive); } @@ -64,18 +59,10 @@ public final class ClusterSpec { return new ClusterSpec(type, id, groupId, vespaVersion, exclusive); } - // TODO: Remove after April 2018 - public static ClusterSpec request(Type type, Id id, Version vespaVersion) { - return request(type, id, vespaVersion, false); - } public static ClusterSpec request(Type type, Id id, Version vespaVersion, boolean exclusive) { return new ClusterSpec(type, id, Optional.empty(), vespaVersion, exclusive); } - // TODO: Remove after April 2018 - public static ClusterSpec from(Type type, Id id, Group groupId, Version vespaVersion) { - return new ClusterSpec(type, id, Optional.of(groupId), vespaVersion, false); - } public static ClusterSpec from(Type type, Id id, Group groupId, Version vespaVersion, boolean exclusive) { return new ClusterSpec(type, id, Optional.of(groupId), vespaVersion, exclusive); } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java index ab7702e26d1..109afe87e89 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java @@ -214,7 +214,8 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye * node in the config server cluster) */ @Override - public Optional<com.yahoo.config.provision.Deployment> deployFromLocalActive(ApplicationId application, Duration timeout) { + public Optional<com.yahoo.config.provision.Deployment> deployFromLocalActive(ApplicationId application, + Duration timeout) { Tenant tenant = tenantRepository.getTenant(application.tenant()); if (tenant == null) return Optional.empty(); LocalSession activeSession = getActiveSession(tenant, application); @@ -583,8 +584,16 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye // Keep track of deployment per application Map<ApplicationId, Future<?>> futures = new HashMap<>(); Set<ApplicationId> failedDeployments = new HashSet<>(); - applicationIds.forEach(appId -> deployFromLocalActive(appId).ifPresent( - deployment -> futures.put(appId, executor.submit(deployment::activate)))); + + for (ApplicationId appId : applicationIds) { + Optional<com.yahoo.config.provision.Deployment> deploymentOptional = deployFromLocalActive(appId); + if ( ! deploymentOptional.isPresent()) continue; + + Deployment deployment = (Deployment)deploymentOptional.get(); + deployment.setBootstrap(true); // Only available inside the config server; hence the cast + futures.put(appId, executor.submit(deployment::activate)); + } + for (Map.Entry<ApplicationId, Future<?>> f : futures.entrySet()) { try { f.getValue().get(); diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java index 74757032eaa..acc5e8dcf61 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java @@ -56,9 +56,12 @@ public class Deployment implements com.yahoo.config.provision.Deployment { private boolean ignoreLockFailure = false; private boolean ignoreSessionStaleFailure = false; + private boolean isBootstrap = false; + private Deployment(LocalSession session, ApplicationRepository applicationRepository, Optional<Provisioner> hostProvisioner, Tenant tenant, - Duration timeout, Clock clock, boolean prepared, boolean validate, Version version) { + Duration timeout, Clock clock, boolean prepared, boolean validate, Version version, + boolean isBootstrap) { this.session = session; this.applicationRepository = applicationRepository; this.hostProvisioner = hostProvisioner; @@ -68,20 +71,21 @@ public class Deployment implements com.yahoo.config.provision.Deployment { this.prepared = prepared; this.validate = validate; this.version = version; + this.isBootstrap = isBootstrap; } public static Deployment unprepared(LocalSession session, ApplicationRepository applicationRepository, Optional<Provisioner> hostProvisioner, Tenant tenant, Duration timeout, Clock clock, boolean validate, Version version) { return new Deployment(session, applicationRepository, hostProvisioner, tenant, - timeout, clock, false, validate, version); + timeout, clock, false, validate, version, false); } public static Deployment prepared(LocalSession session, ApplicationRepository applicationRepository, Optional<Provisioner> hostProvisioner, Tenant tenant, Duration timeout, Clock clock) { return new Deployment(session, applicationRepository, hostProvisioner, tenant, - timeout, clock, true, true, session.getVespaVersion()); + timeout, clock, true, true, session.getVespaVersion(), false); } public Deployment setIgnoreLockFailure(boolean ignoreLockFailure) { @@ -105,6 +109,7 @@ public class Deployment implements com.yahoo.config.provision.Deployment { .timeoutBudget(timeoutBudget) .ignoreValidationErrors( ! validate) .vespaVersion(version.toString()) + .isBootstrap(isBootstrap) .build(), Optional.empty(), tenant.getPath(), @@ -161,10 +166,14 @@ public class Deployment implements com.yahoo.config.provision.Deployment { * This is sometimes needed after activation, but can also be requested without * doing prepare and activate in the same session. */ + @Override public void restart(HostFilter filter) { hostProvisioner.get().restart(session.getApplicationId(), filter); } + /** Set this to true if this deployment is done to bootstrap the config server */ + public void setBootstrap(boolean isBootstrap) { this.isBootstrap = isBootstrap; } + /** Exposes the session of this for testing only */ public LocalSession session() { return session; } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java index cec879c6e14..28dc0cc8414 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java @@ -123,6 +123,7 @@ public class ModelContextImpl implements ModelContext { private final boolean hostedVespa; private final Zone zone; private final Set<Rotation> rotations; + private final boolean isBootstrap; public Properties(ApplicationId applicationId, boolean multitenant, @@ -132,7 +133,8 @@ public class ModelContextImpl implements ModelContext { String athenzDnsSuffix, boolean hostedVespa, Zone zone, - Set<Rotation> rotations) { + Set<Rotation> rotations, + boolean isBootstrap) { this.applicationId = applicationId; this.multitenant = multitenant; this.configServerSpecs = configServerSpecs; @@ -142,6 +144,7 @@ public class ModelContextImpl implements ModelContext { this.hostedVespa = hostedVespa; this.zone = zone; this.rotations = rotations; + this.isBootstrap = isBootstrap; } @Override @@ -175,6 +178,9 @@ public class ModelContextImpl implements ModelContext { @Override public Set<Rotation> rotations() { return rotations; } + @Override + public boolean isBootstrap() { return isBootstrap; } + } } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java index 651e5a6bbb0..6b872fc4601 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java @@ -10,9 +10,11 @@ import com.yahoo.config.model.api.ModelFactory; import com.yahoo.config.model.application.provider.MockFileRegistry; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.AllocatedHosts; +import com.yahoo.config.provision.HostName; import com.yahoo.config.provision.TenantName; import com.yahoo.config.provision.Version; import com.yahoo.log.LogLevel; +import com.yahoo.vespa.config.server.ConfigServerSpec; import com.yahoo.vespa.config.server.GlobalComponentRegistry; import com.yahoo.vespa.config.server.tenant.Rotations; import com.yahoo.vespa.config.server.tenant.TenantRepository; @@ -25,6 +27,7 @@ import com.yahoo.vespa.config.server.session.SessionZooKeeperClient; import com.yahoo.vespa.config.server.session.SilentDeployLogger; import com.yahoo.vespa.curator.Curator; +import java.net.URI; import java.time.Instant; import java.util.Map; import java.util.Optional; @@ -104,11 +107,16 @@ public class ActivatedModelsBuilder extends ModelsBuilder<Application> { } private ModelContext.Properties createModelContextProperties(ApplicationId applicationId) { - return createModelContextProperties( - applicationId, - configserverConfig, - zone(), - new Rotations(curator, TenantRepository.getTenantPath(tenant)).readRotationsFromZooKeeper(applicationId)); + return new ModelContextImpl.Properties(applicationId, + configserverConfig.multitenant(), + ConfigServerSpec.fromConfig(configserverConfig), + HostName.from(configserverConfig.loadBalancerAddress()), + configserverConfig.ztsUrl() != null ? URI.create(configserverConfig.ztsUrl()) : null, + configserverConfig.athenzDnsSuffix(), + configserverConfig.hostedVespa(), + zone(), + new Rotations(curator, TenantRepository.getTenantPath(tenant)).readRotationsFromZooKeeper(applicationId), + false); // We may be bootstrapping, but we only know and care during prepare } } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java index 560f650dcba..b2eea704a5e 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java @@ -2,29 +2,22 @@ package com.yahoo.vespa.config.server.modelfactory; import com.google.common.util.concurrent.UncheckedTimeoutException; -import com.yahoo.cloud.config.ConfigserverConfig; import com.yahoo.config.application.api.ApplicationPackage; import com.yahoo.config.model.api.HostProvisioner; -import com.yahoo.config.model.api.ModelContext; import com.yahoo.config.model.api.ModelFactory; import com.yahoo.config.provision.AllocatedHosts; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ApplicationLockException; import com.yahoo.config.provision.Environment; -import com.yahoo.config.provision.HostName; import com.yahoo.config.provision.OutOfCapacityException; -import com.yahoo.config.provision.Rotation; import com.yahoo.config.provision.Version; import com.yahoo.config.provision.Zone; import com.yahoo.lang.SettableOptional; import com.yahoo.log.LogLevel; -import com.yahoo.vespa.config.server.ConfigServerSpec; -import com.yahoo.vespa.config.server.deploy.ModelContextImpl; import com.yahoo.vespa.config.server.http.InternalServerException; import com.yahoo.vespa.config.server.http.UnknownVespaVersionException; import com.yahoo.vespa.config.server.provision.StaticProvisioner; -import java.net.URI; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; @@ -209,22 +202,7 @@ public abstract class ModelsBuilder<MODELRESULT extends ModelResult> { Optional<AllocatedHosts> allocatedHosts, Instant now); - protected ModelContext.Properties createModelContextProperties(ApplicationId applicationId, - ConfigserverConfig configserverConfig, - Zone zone, - Set<Rotation> rotations) { - return new ModelContextImpl.Properties(applicationId, - configserverConfig.multitenant(), - ConfigServerSpec.fromConfig(configserverConfig), - HostName.from(configserverConfig.loadBalancerAddress()), - configserverConfig.ztsUrl() != null ? URI.create(configserverConfig.ztsUrl()) : null, - configserverConfig.athenzDnsSuffix(), - configserverConfig.hostedVespa(), - zone, - rotations); - } - - /** + /** * Returns a host provisioner returning the previously allocated hosts if available and when on hosted Vespa, * returns empty otherwise, which may either mean that no hosts are allocated or that we are running * non-hosted and should default to use hosts defined in the application package, depending on context diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java index f53a48b3783..b44896ecb89 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java @@ -35,16 +35,18 @@ public final class PrepareParams { private final boolean ignoreValidationErrors; private final boolean dryRun; private final boolean verbose; + private final boolean isBootstrap; private final Optional<Version> vespaVersion; private final Set<Rotation> rotations; private PrepareParams(ApplicationId applicationId, TimeoutBudget timeoutBudget, boolean ignoreValidationErrors, - boolean dryRun, boolean verbose, Optional<Version> vespaVersion, Set<Rotation> rotations) { + boolean dryRun, boolean verbose, boolean isBootstrap, Optional<Version> vespaVersion, Set<Rotation> rotations) { this.timeoutBudget = timeoutBudget; this.applicationId = applicationId; this.ignoreValidationErrors = ignoreValidationErrors; this.dryRun = dryRun; this.verbose = verbose; + this.isBootstrap = isBootstrap; this.vespaVersion = vespaVersion; this.rotations = rotations; } @@ -54,6 +56,7 @@ public final class PrepareParams { private boolean ignoreValidationErrors = false; private boolean dryRun = false; private boolean verbose = false; + private boolean isBootstrap = false; private ApplicationId applicationId = ApplicationId.defaultId(); private TimeoutBudget timeoutBudget = new TimeoutBudget(Clock.systemUTC(), Duration.ofSeconds(30)); private Optional<Version> vespaVersion = Optional.empty(); @@ -81,6 +84,11 @@ public final class PrepareParams { return this; } + public Builder isBootstrap(boolean isBootstrap) { + this.isBootstrap = isBootstrap; + return this; + } + public Builder timeoutBudget(TimeoutBudget timeoutBudget) { this.timeoutBudget = timeoutBudget; return this; @@ -113,7 +121,7 @@ public final class PrepareParams { public PrepareParams build() { return new PrepareParams(applicationId, timeoutBudget, ignoreValidationErrors, dryRun, - verbose, vespaVersion, rotations); + verbose, isBootstrap, vespaVersion, rotations); } } @@ -170,6 +178,8 @@ public final class PrepareParams { return verbose; } + public boolean isBootstrap() { return isBootstrap; } + public TimeoutBudget getTimeoutBudget() { return timeoutBudget; } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java index 114ad936eda..0d9f8ce64b1 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java @@ -157,7 +157,8 @@ public class SessionPreparer { configserverConfig.athenzDnsSuffix(), configserverConfig.hostedVespa(), zone, - rotationsSet); + rotationsSet, + params.isBootstrap()); this.preparedModelsBuilder = new PreparedModelsBuilder(modelFactoryRegistry, permanentApplicationPackage, configDefinitionRepo, diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ConfigServerBootstrapTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ConfigServerBootstrapTest.java index 082c3058598..f9c99b323af 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/ConfigServerBootstrapTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ConfigServerBootstrapTest.java @@ -2,6 +2,10 @@ package com.yahoo.vespa.config.server; import com.yahoo.cloud.config.ConfigserverConfig; +import com.yahoo.config.model.api.HostProvisioner; +import com.yahoo.config.model.provision.InMemoryProvisioner; +import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.Version; import com.yahoo.container.handler.VipStatus; import com.yahoo.container.jdisc.config.HealthMonitorConfig; import com.yahoo.container.jdisc.state.StateMonitor; @@ -34,9 +38,10 @@ public class ConfigServerBootstrapTest { public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test - public void testBootStrap() throws Exception { + public void testBootstrap() throws Exception { ConfigserverConfig configserverConfig = createConfigserverConfig(temporaryFolder); - DeployTester tester = new DeployTester("src/test/apps/hosted/", configserverConfig); + InMemoryProvisioner provisioner = new InMemoryProvisioner(true, "host0", "host1", "host3"); + DeployTester tester = new DeployTester("src/test/apps/hosted/", configserverConfig, provisioner); tester.deployApp("myApp", "4.5.6", Instant.now()); File versionFile = temporaryFolder.newFile(); @@ -45,6 +50,13 @@ public class ConfigServerBootstrapTest { RpcServer rpcServer = createRpcServer(configserverConfig); VipStatus vipStatus = new VipStatus(); + // Take a host away so that there are too few for the application, to verify we can still bootstrap + ClusterSpec contentCluster = ClusterSpec.from(ClusterSpec.Type.content, + ClusterSpec.Id.from("music"), + ClusterSpec.Group.from(0), + new com.yahoo.component.Version(4, 5, 6), + false); + provisioner.allocations().get(contentCluster).remove(0); ConfigServerBootstrap bootstrap = new ConfigServerBootstrap(tester.applicationRepository(), rpcServer, versionState, createStateMonitor(), vipStatus); assertFalse(vipStatus.isInRotation()); waitUntil(rpcServer::isRunning, "failed waiting for Rpc server running"); @@ -58,7 +70,7 @@ public class ConfigServerBootstrapTest { } @Test - public void testBootStrapWhenRedeploymentFails() throws Exception { + public void testBootstrapWhenRedeploymentFails() throws Exception { ConfigserverConfig configserverConfig = createConfigserverConfig(temporaryFolder); DeployTester tester = new DeployTester("src/test/apps/hosted/", configserverConfig); tester.deployApp("myApp", "4.5.6", Instant.now()); diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java index 5d9a5f0fadc..ce53dc3f2fb 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java @@ -50,7 +50,8 @@ public class ModelContextImplTest { null, false, Zone.defaultZone(), - rotations), + rotations, + false), Optional.empty(), new Version(6), new Version(6)); diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java index a4f5679aa39..b15356a172e 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java @@ -83,6 +83,10 @@ public class DeployTester { this(appPath, Collections.singletonList(createModelFactory(Clock.systemUTC())), configserverConfig, Clock.systemUTC()); } + public DeployTester(String appPath, ConfigserverConfig configserverConfig, HostProvisioner provisioner) { + this(appPath, Collections.singletonList(createModelFactory(Clock.systemUTC())), configserverConfig, Clock.systemUTC(), provisioner); + } + public DeployTester(String appPath, ConfigserverConfig configserverConfig, Clock clock) { this(appPath, Collections.singletonList(createModelFactory(clock)), configserverConfig, clock); } @@ -95,8 +99,12 @@ public class DeployTester { this(appPath, modelFactories, configserverConfig, clock, Zone.defaultZone()); } + public DeployTester(String appPath, List<ModelFactory> modelFactories, ConfigserverConfig configserverConfig, Clock clock, HostProvisioner provisioner) { + this(appPath, modelFactories, configserverConfig, clock, Zone.defaultZone(), provisioner); + } + public DeployTester(String appPath, List<ModelFactory> modelFactories, ConfigserverConfig configserverConfig, Clock clock, Zone zone) { - this(appPath, modelFactories, configserverConfig, clock, Zone.defaultZone(), createProvisioner()); + this(appPath, modelFactories, configserverConfig, clock, zone, createProvisioner()); } public DeployTester(String appPath, List<ModelFactory> modelFactories, ConfigserverConfig configserverConfig, Clock clock, Zone zone, HostProvisioner provisioner) { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java index d31b4438a38..dff6378a19a 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java @@ -64,7 +64,7 @@ public class GroupPreparer { // Allocate from the prioritized list NodeAllocation allocation = new NodeAllocation(application, cluster, requestedNodes, highestIndex, nodeRepository); allocation.offer(prioritizer.prioritize()); - if (! allocation.fullfilled()) + if (! allocation.fullfilled() && requestedNodes.canFail()) throw new OutOfCapacityException("Could not satisfy " + requestedNodes + " for " + cluster + " in " + application.toShortString() + outOfCapacityDetails(allocation)); 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 3fa70b3242f..833418b6f1f 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 @@ -90,7 +90,7 @@ public class NodeRepositoryProvisioner implements Provisioner { log.log(LogLevel.DEBUG, () -> "Decided flavor for requested tenant nodes: " + flavor); boolean exclusive = capacityPolicies.decideExclusivity(cluster.isExclusive()); effectiveGroups = wantedGroups > nodeCount ? nodeCount : wantedGroups; // cannot have more groups than nodes - requestedNodes = NodeSpec.from(nodeCount, flavor, exclusive); + requestedNodes = NodeSpec.from(nodeCount, flavor, exclusive, requestedCapacity.canFail()); } else { requestedNodes = NodeSpec.from(requestedCapacity.type()); diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java index b2572a781fe..e8c2926700e 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java @@ -40,6 +40,9 @@ public interface NodeSpec { /** Returns whether the given node count is sufficient to fulfill this spec */ boolean fulfilledBy(int count); + /** Returns whether this should throw an exception if the requested nodes are not fully available */ + boolean canFail(); + /** Returns the ideal number of nodes that should be retired to fulfill this spec */ int idealRetiredCount(int acceptedCount, int currentRetiredCount); @@ -53,8 +56,8 @@ public interface NodeSpec { */ Node assignRequestedFlavor(Node node); - static NodeSpec from(int nodeCount, Flavor flavor, boolean exclusive) { - return new CountNodeSpec(nodeCount, flavor, exclusive); + static NodeSpec from(int nodeCount, Flavor flavor, boolean exclusive, boolean canFail) { + return new CountNodeSpec(nodeCount, flavor, exclusive, canFail); } static NodeSpec from(NodeType type) { @@ -67,12 +70,14 @@ public interface NodeSpec { private final int count; private final Flavor requestedFlavor; private final boolean exclusive; + private final boolean canFail; - public CountNodeSpec(int count, Flavor flavor, boolean exclusive) { + public CountNodeSpec(int count, Flavor flavor, boolean exclusive, boolean canFail) { Objects.requireNonNull(flavor, "A flavor must be specified"); this.count = count; this.requestedFlavor = flavor; this.exclusive = exclusive; + this.canFail = canFail; } // TODO: Remove usage of this @@ -102,16 +107,21 @@ public interface NodeSpec { public boolean specifiesNonStockFlavor() { return ! requestedFlavor.isStock(); } @Override + public boolean saturatedBy(int count) { return fulfilledBy(count); } // min=max for count specs + + @Override public boolean fulfilledBy(int count) { return count >= this.count; } @Override - public boolean saturatedBy(int count) { return fulfilledBy(count); } // min=max for count specs + public boolean canFail() { return canFail; } @Override public int idealRetiredCount(int acceptedCount, int currentRetiredCount) { return acceptedCount - this.count; } @Override - public NodeSpec fraction(int divisor) { return new CountNodeSpec(count/divisor, requestedFlavor, exclusive); } + public NodeSpec fraction(int divisor) { + return new CountNodeSpec(count/divisor, requestedFlavor, exclusive, canFail); + } @Override public Node assignRequestedFlavor(Node node) { @@ -166,6 +176,9 @@ public interface NodeSpec { public boolean saturatedBy(int count) { return false; } @Override + public boolean canFail() { return false; } + + @Override public int idealRetiredCount(int acceptedCount, int currentRetiredCount) { /* * All nodes marked with wantToRetire get marked as retired just before this function is called, diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java index 04a4a7f5fb8..de14bc7e480 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java @@ -140,7 +140,7 @@ public class MockNodeRepository extends NodeRepository { ClusterSpec.Id.from("id3"), Version.fromString("6.42"), false); - activate(provisioner.prepare(app3, cluster3, Capacity.fromNodeCount(2, Optional.of("docker"), false), 1, null), app3, provisioner); + activate(provisioner.prepare(app3, cluster3, Capacity.fromNodeCount(2, Optional.of("docker"), false, true), 1, null), app3, provisioner); } private void activate(List<HostSpec> hosts, ApplicationId application, NodeRepositoryProvisioner provisioner) { diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java index 84569077053..dc00eda01a0 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java @@ -294,7 +294,7 @@ public class FailedExpirerTest { List<HostSpec> preparedNodes = provisioner.prepare(applicationId, clusterSpec, Capacity.fromNodeCount(hostname.length, Optional.of(flavor.name()), - false), + false, true), 1, null); NestedTransaction transaction = new NestedTransaction().add(new CuratorTransaction(curator)); provisioner.activate(transaction, applicationId, new HashSet<>(preparedNodes)); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java index d03e1b9ed4b..f4fa8461f12 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java @@ -147,7 +147,7 @@ public class InactiveAndFailedExpirerTest { new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(2, Optional.of("default"), - false), + false, true), 1) ) ); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InfrastructureProvisionerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InfrastructureProvisionerTest.java index 586498619c6..f0c4ad2ef2d 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InfrastructureProvisionerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InfrastructureProvisionerTest.java @@ -100,7 +100,7 @@ public class InfrastructureProvisionerTest { Node node = tester.addNode("id-" + id, "node-" + id, "default", NodeType.config); Optional<Node> nodeWithAllocation = wantedVespaVersion.map(version -> { ConfigServerApplication application = ConfigServerApplication.CONFIG_SERVER_APPLICATION; - ClusterSpec clusterSpec = ClusterSpec.from(application.getClusterType(), application.getClusterId(), ClusterSpec.Group.from(0), version); + ClusterSpec clusterSpec = ClusterSpec.from(application.getClusterType(), application.getClusterId(), ClusterSpec.Group.from(0), version, false); ClusterMembership membership = ClusterMembership.from(clusterSpec, 1); Allocation allocation = new Allocation(application.getApplicationId(), membership, new Generation(0, 0), false); return node.with(allocation); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java index e4560ef685d..b1bceec6d9f 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java @@ -109,8 +109,8 @@ public class NodeFailTester { assertEquals(wantedNodesApp2, tester.nodeRepository.getNodes(app2, Node.State.active).size()); Map<ApplicationId, MockDeployer.ApplicationContext> apps = new HashMap<>(); - apps.put(app1, new MockDeployer.ApplicationContext(app1, clusterApp1, Capacity.fromNodeCount(wantedNodesApp1, Optional.of("default"), false), 1)); - apps.put(app2, new MockDeployer.ApplicationContext(app2, clusterApp2, Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default"), false), 1)); + apps.put(app1, new MockDeployer.ApplicationContext(app1, clusterApp1, Capacity.fromNodeCount(wantedNodesApp1, Optional.of("default"), false, true), 1)); + apps.put(app2, new MockDeployer.ApplicationContext(app2, clusterApp2, Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default"), false, true), 1)); tester.deployer = new MockDeployer(tester.provisioner, apps); tester.serviceMonitor = new ServiceMonitorStub(apps, tester.nodeRepository); tester.metric = new MetricsReporterTest.TestMetric(); @@ -133,8 +133,8 @@ public class NodeFailTester { ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.75.0"), false); ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.75.0"), false); Capacity allHosts = Capacity.fromRequiredNodeType(NodeType.host); - Capacity capacity1 = Capacity.fromNodeCount(3, Optional.of("docker"), false); - Capacity capacity2 = Capacity.fromNodeCount(5, Optional.of("docker"), false); + Capacity capacity1 = Capacity.fromNodeCount(3, Optional.of("docker"), false, true); + Capacity capacity2 = Capacity.fromNodeCount(5, Optional.of("docker"), false, true); tester.activate(nodeAdminApp, clusterNodeAdminApp, allHosts); tester.activate(app1, clusterApp1, capacity1); tester.activate(app2, clusterApp2, capacity2); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTester.java index ec71b36064f..421211b7c49 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTester.java @@ -112,7 +112,7 @@ public class NodeRetirerTester { for (int i = 0; i < flavorIds.length; i++) { Flavor flavor = flavors.get(flavorIds[i]); ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("cluster-" + i), Version.fromString("6.99"), false); - Capacity capacity = Capacity.fromNodeCount(numNodes[i], Optional.of(flavor.name()), false); + Capacity capacity = Capacity.fromNodeCount(numNodes[i], Optional.of(flavor.name()), false, true); // If the number of node the app wants is divisible by 2, make it into 2 groups, otherwise as 1 int numGroups = numNodes[i] % 2 == 0 ? 2 : 1; clusterContexts.add(new MockDeployer.ClusterContext(applicationId, cluster, capacity, numGroups)); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java index 898054f23ff..c7a649faf98 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java @@ -133,9 +133,9 @@ public class OperatorChangeApplicationMaintainerTest { Map<ApplicationId, MockDeployer.ApplicationContext> apps = new HashMap<>(); apps.put(app1, new MockDeployer.ApplicationContext(app1, clusterApp1, - Capacity.fromNodeCount(wantedNodesApp1, Optional.of("default"), false), 1)); + Capacity.fromNodeCount(wantedNodesApp1, Optional.of("default"), false, true), 1)); apps.put(app2, new MockDeployer.ApplicationContext(app2, clusterApp2, - Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default"), false), 1)); + Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default"), false, true), 1)); this.deployer = new MockDeployer(provisioner, apps); } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java index ed360abc5ea..de7f4fde6ae 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java @@ -193,9 +193,9 @@ public class PeriodicApplicationMaintainerTest { void runApplicationMaintainer(Optional<List<Node>> overriddenNodesNeedingMaintenance) { Map<ApplicationId, MockDeployer.ApplicationContext> apps = new HashMap<>(); apps.put(app1, new MockDeployer.ApplicationContext(app1, clusterApp1, - Capacity.fromNodeCount(wantedNodesApp1, Optional.of("default"), false), 1)); + Capacity.fromNodeCount(wantedNodesApp1, Optional.of("default"), false, true), 1)); apps.put(app2, new MockDeployer.ApplicationContext(app2, clusterApp2, - Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default"), false), 1)); + Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default"), false, true), 1)); MockDeployer deployer = new MockDeployer(provisioner, apps); new TestablePeriodicApplicationMaintainer(deployer, nodeRepository, Duration.ofMinutes(30), overriddenNodesNeedingMaintenance).run(); } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java index ca4929ece14..e5a46adef8e 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java @@ -92,7 +92,7 @@ public class RetiredExpirerTest { clock.advance(Duration.ofHours(30)); // Retire period spent MockDeployer deployer = new MockDeployer(provisioner, - Collections.singletonMap(applicationId, new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(wantedNodes, Optional.of("default"), false), 1))); + Collections.singletonMap(applicationId, new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(wantedNodes, Optional.of("default"), false, true), 1))); createRetiredExpirer(deployer).run(); assertEquals(3, nodeRepository.getNodes(applicationId, Node.State.active).size()); assertEquals(4, nodeRepository.getNodes(applicationId, Node.State.inactive).size()); @@ -120,7 +120,7 @@ public class RetiredExpirerTest { clock.advance(Duration.ofHours(30)); // Retire period spent MockDeployer deployer = new MockDeployer(provisioner, - Collections.singletonMap(applicationId, new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(2, Optional.of("default"), false), 1))); + Collections.singletonMap(applicationId, new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(2, Optional.of("default"), false, true), 1))); createRetiredExpirer(deployer).run(); assertEquals(2, nodeRepository.getNodes(applicationId, Node.State.active).size()); assertEquals(6, nodeRepository.getNodes(applicationId, Node.State.inactive).size()); @@ -153,7 +153,7 @@ public class RetiredExpirerTest { new MockDeployer(provisioner, Collections.singletonMap( applicationId, - new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(wantedNodes, Optional.of("default"), false), 1))); + new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(wantedNodes, Optional.of("default"), false, true), 1))); // Allow the 1st and 3rd retired nodes permission to inactivate diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java index 943cb60bf04..62212447c2e 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java @@ -118,7 +118,7 @@ public class AllocationSimulator { public void addCluster(String task, int count, Flavor flavor, String id) { // TODO: Implement - NodeSpec.CountNodeSpec nodeSpec = new NodeSpec.CountNodeSpec(count, flavor, false); + NodeSpec.CountNodeSpec nodeSpec = new NodeSpec.CountNodeSpec(count, flavor, false, true); nodes = new NodeList(nodes.asList()); } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java index 2cabee98c0d..16aa613db4a 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java @@ -201,7 +201,7 @@ public class DockerProvisioningTest { private void prepareAndActivate(ApplicationId application, int nodeCount, boolean exclusive, ProvisioningTester tester) { Set<HostSpec> hosts = new HashSet<>(tester.prepare(application, ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContainer"), Version.fromString("6.39"), exclusive), - Capacity.fromNodeCount(nodeCount, Optional.of(dockerFlavor), false), + Capacity.fromNodeCount(nodeCount, Optional.of(dockerFlavor), false, true), 1)); tester.activate(application, hosts); } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java index 142789eea51..62ff978047b 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java @@ -100,8 +100,8 @@ public class MultigroupProvisioningTest { tester.makeReadyNodes(10, "small"); - deploy(application1, Capacity.fromNodeCount(1, Optional.of("small"), true), 1, tester); - deploy(application1, Capacity.fromNodeCount(2, Optional.of("small"), true), 2, tester); + deploy(application1, Capacity.fromNodeCount(1, Optional.of("small"), true, true), 1, tester); + deploy(application1, Capacity.fromNodeCount(2, Optional.of("small"), true, true), 2, tester); } @Test @@ -113,8 +113,8 @@ public class MultigroupProvisioningTest { tester.makeReadyNodes(10, "small"); tester.makeReadyNodes(10, "large"); - deploy(application1, Capacity.fromNodeCount(1, Optional.of("small"), true), 1, tester); - deploy(application1, Capacity.fromNodeCount(2, Optional.of("large"), true), 2, tester); + deploy(application1, Capacity.fromNodeCount(1, Optional.of("small"), true, true), 1, tester); + deploy(application1, Capacity.fromNodeCount(2, Optional.of("large"), true, true), 2, tester); } @Test @@ -135,7 +135,7 @@ public class MultigroupProvisioningTest { new MockDeployer(tester.provisioner(), Collections.singletonMap(application1, new MockDeployer.ApplicationContext(application1, cluster(), - Capacity.fromNodeCount(8, Optional.of("large"), false), 1))); + Capacity.fromNodeCount(8, Optional.of("large"), false, true), 1))); new RetiredExpirer(tester.nodeRepository(), tester.orchestrator(), deployer, tester.clock(), Duration.ofDays(30), Duration.ofHours(12), new JobControl(tester.nodeRepository().database())).run(); @@ -144,10 +144,10 @@ public class MultigroupProvisioningTest { } private void deploy(ApplicationId application, int nodeCount, int groupCount, String flavor, ProvisioningTester tester) { - deploy(application, Capacity.fromNodeCount(nodeCount, Optional.of(flavor), false), groupCount, tester); + deploy(application, Capacity.fromNodeCount(nodeCount, Optional.of(flavor), false, true), groupCount, tester); } private void deploy(ApplicationId application, int nodeCount, int groupCount, ProvisioningTester tester) { - deploy(application, Capacity.fromNodeCount(nodeCount, Optional.of("default"), false), groupCount, tester); + deploy(application, Capacity.fromNodeCount(nodeCount, Optional.of("default"), false, true), groupCount, tester); } private void deploy(ApplicationId application, Capacity capacity, int wantedGroups, ProvisioningTester tester) { int nodeCount = capacity.nodeCount(); 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 a7d2b97dbe6..758d7cc71d9 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 @@ -494,6 +494,19 @@ public class ProvisioningTest { } @Test + public void out_of_capacity_but_cannot_fail() { + ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east"))); + tester.makeReadyNodes(4, "default"); + ApplicationId application = tester.makeApplicationId(); + ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, + ClusterSpec.Id.from("music"), + new com.yahoo.component.Version(4, 5, 6), + false); + tester.prepare(application, cluster, Capacity.fromNodeCount(5, Optional.empty(), false, false), 1); + // No exception; Success + } + + @Test public void out_of_desired_flavor() { ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east"))); @@ -832,7 +845,7 @@ public class ProvisioningTest { allHosts.addAll(content0); allHosts.addAll(content1); - Function<Integer, Capacity> capacity = count -> Capacity.fromNodeCount(count, Optional.empty(), required); + Function<Integer, Capacity> capacity = count -> Capacity.fromNodeCount(count, Optional.empty(), required, true); int expectedContainer0Size = tester.capacityPolicies().decideSize(capacity.apply(container0Size)); int expectedContainer1Size = tester.capacityPolicies().decideSize(capacity.apply(container1Size)); int expectedContent0Size = tester.capacityPolicies().decideSize(capacity.apply(content0Size)); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java index 60ce3c9b567..1f26cf035b1 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java @@ -136,7 +136,7 @@ public class ProvisioningTester { } public List<HostSpec> prepare(ApplicationId application, ClusterSpec cluster, int nodeCount, int groups, boolean required, String flavor) { - return prepare(application, cluster, Capacity.fromNodeCount(nodeCount, Optional.ofNullable(flavor), required), groups); + return prepare(application, cluster, Capacity.fromNodeCount(nodeCount, Optional.ofNullable(flavor), required, true), groups); } public List<HostSpec> prepare(ApplicationId application, ClusterSpec cluster, Capacity capacity, int groups) { diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifierTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifierTest.java index 445d18bed7c..20168074513 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifierTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifierTest.java @@ -238,7 +238,8 @@ public class NodeIdentifierTest { ClusterSpec.Type.container, new ClusterSpec.Id(clusterId), ClusterSpec.Group.from(0), - Version.emptyVersion), + Version.emptyVersion, + false), clusterIndex), Generation.inital(), false)); |