aboutsummaryrefslogtreecommitdiffstats
path: root/config-provisioning/src
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@verizonmedia.com>2019-05-02 21:29:29 +0200
committerJon Bratseth <bratseth@verizonmedia.com>2019-05-02 21:29:29 +0200
commit8457cd74f0ac3d876d1f7fd6cd7ea7b503cae491 (patch)
tree4a2eb5ed891f5da85397797fc4c943c33ade8b65 /config-provisioning/src
parent75e2698805c454d54afb4b5a8bc62b046c4e3246 (diff)
Allow continuous node resource specs
Diffstat (limited to 'config-provisioning/src')
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/Capacity.java19
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/Flavor.java28
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/FlavorSpec.java107
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java33
4 files changed, 174 insertions, 13 deletions
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 6df617ea335..f635b986558 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
@@ -17,11 +17,11 @@ public final class Capacity {
private final boolean canFail;
- private final Optional<String> flavor;
+ private final Optional<FlavorSpec> flavor;
private final NodeType type;
- private Capacity(int nodeCount, Optional<String> flavor, boolean required, boolean canFail, NodeType type) {
+ private Capacity(int nodeCount, Optional<FlavorSpec> flavor, boolean required, boolean canFail, NodeType type) {
this.nodeCount = nodeCount;
this.required = required;
this.canFail = canFail;
@@ -36,7 +36,10 @@ public final class Capacity {
* The node flavor requested, or empty if no particular flavor is specified.
* This may be satisfied by the requested flavor or a suitable replacement
*/
- public Optional<String> flavor() { return flavor; }
+ public Optional<String> flavor() { return flavor.map(FlavorSpec::legacyFlavorName); }
+
+ /** Returns the capacity specified for each node, or empty to leave this decision to provisioning */
+ public Optional<FlavorSpec> flavorSpec() { return flavor; }
/** Returns whether the requested number of nodes must be met exactly for a request for this to succeed */
public boolean isRequired() { return required; }
@@ -65,10 +68,18 @@ public final class Capacity {
return fromNodeCount(capacity, Optional.empty(), false, true);
}
- public static Capacity fromNodeCount(int nodeCount, Optional<String> flavor, boolean required, boolean canFail) {
+ public static Capacity fromCount(int nodeCount, FlavorSpec flavor, boolean required, boolean canFail) {
+ return new Capacity(nodeCount, Optional.of(flavor), required, canFail, NodeType.tenant);
+ }
+
+ public static Capacity fromCount(int nodeCount, Optional<FlavorSpec> flavor, boolean required, boolean canFail) {
return new Capacity(nodeCount, flavor, required, canFail, NodeType.tenant);
}
+ public static Capacity fromNodeCount(int nodeCount, Optional<String> flavor, boolean required, boolean canFail) {
+ return new Capacity(nodeCount, flavor.map(FlavorSpec::fromLegacyFlavorName), required, canFail, 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/Flavor.java b/config-provisioning/src/main/java/com/yahoo/config/provision/Flavor.java
index 79a17c23dd7..8b6fa863af6 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/Flavor.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/Flavor.java
@@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableList;
import com.yahoo.config.provisioning.FlavorsConfig;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -31,6 +32,7 @@ public class Flavor {
/**
* Creates a Flavor, but does not set the replacesFlavors.
+ *
* @param flavorConfig config to be used for Flavor.
*/
public Flavor(FlavorsConfig.Flavor flavorConfig) {
@@ -49,6 +51,25 @@ public class Flavor {
this.idealHeadroom = flavorConfig.idealHeadroom();
}
+ /** Create a Flavor from a Flavor spec and all other fields set to Docker defaults */
+ public Flavor(FlavorSpec spec) {
+ if (spec.allocateByLegacyName())
+ throw new IllegalArgumentException("Can not create flavor '" + spec.legacyFlavorName() + "' from a spec: " +
+ "Non-docker flavors must be of a configured flavor");
+ this.name = spec.legacyFlavorName();
+ this.cost = 0;
+ this.isStock = true;
+ this.type = Type.DOCKER_CONTAINER;
+ this.minCpuCores = spec.cpuCores();
+ this.minMainMemoryAvailableGb = spec.memoryGb();
+ this.minDiskAvailableGb = spec.diskGb();
+ this.fastDisk = true;
+ this.bandwidth = 1;
+ this.description = "";
+ this.retired = false;
+ this.replacesFlavors = Collections.emptyList();
+ }
+
/** Returns the unique identity of this flavor */
public String name() { return name; }
@@ -147,6 +168,13 @@ public class Flavor {
this.fastDisk || ! other.fastDisk;
}
+ public FlavorSpec asSpec() {
+ if (isDocker())
+ return new FlavorSpec(minCpuCores, minMainMemoryAvailableGb, minDiskAvailableGb);
+ else
+ return FlavorSpec.fromLegacyFlavorName(name);
+ }
+
@Override
public int hashCode() { return name.hashCode(); }
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/FlavorSpec.java b/config-provisioning/src/main/java/com/yahoo/config/provision/FlavorSpec.java
new file mode 100644
index 00000000000..62cfb59c51c
--- /dev/null
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/FlavorSpec.java
@@ -0,0 +1,107 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.config.provision;
+
+import java.util.Objects;
+
+/**
+ * The node capacity specified by an application, which is matched to an actual flavor during provisioning.
+ *
+ * @author bratseth
+ */
+public class FlavorSpec {
+
+ private final double cpuCores;
+ private final double memoryGb;
+ private final double diskGb;
+
+ private final boolean allocateByLegacyName;
+ private final String legacyFlavorName;
+
+ public FlavorSpec(double cpuCores, double memoryGb, double diskGb) {
+ this.cpuCores = cpuCores;
+ this.memoryGb = memoryGb;
+ this.diskGb = diskGb;
+ this.allocateByLegacyName = false;
+ this.legacyFlavorName = null;
+ }
+
+ private FlavorSpec(double cpuCores, double memoryGb, double diskGb, boolean allocateByLegacyName, String legacyFlavorName) {
+ this.cpuCores = cpuCores;
+ this.memoryGb = memoryGb;
+ this.diskGb = diskGb;
+ this.allocateByLegacyName = allocateByLegacyName;
+ this.legacyFlavorName = legacyFlavorName;
+ }
+
+ public double cpuCores() { return cpuCores; }
+ public double memoryGb() { return memoryGb; }
+ public double diskGb() { return diskGb; }
+
+ /**
+ * If this is true, a non-docker legacy name was used to specify this and we'll respect that by mapping directly.
+ * The other getters of this will return 0.
+ */
+ public boolean allocateByLegacyName() { return allocateByLegacyName; }
+
+ /** Returns the legacy flavor string of this. This is never null. */
+ public String legacyFlavorName() {
+ if (legacyFlavorName != null)
+ return legacyFlavorName;
+ else
+ return "d-" + (int)Math.ceil(cpuCores) + "-" + (int)Math.ceil(memoryGb) + "-" + (int)Math.ceil(diskGb);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if ( ! (o instanceof FlavorSpec)) return false;
+ FlavorSpec other = (FlavorSpec)o;
+ if (allocateByLegacyName) {
+ return this.legacyFlavorName.equals(other.legacyFlavorName);
+ }
+ else {
+ if (this.cpuCores != other.cpuCores) return false;
+ if (this.memoryGb != other.memoryGb) return false;
+ if (this.diskGb != other.diskGb) return false;
+ return true;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ if (allocateByLegacyName)
+ return legacyFlavorName.hashCode();
+ else
+ return (int)(2503 * cpuCores + 22123 * memoryGb + 26987 * diskGb);
+ }
+
+ @Override
+ public String toString() {
+ if (allocateByLegacyName)
+ return "flavor '" + legacyFlavorName + "'";
+ else
+ return "cpu cores: " + cpuCores + ", memory: " + memoryGb + " Gb, disk " + diskGb + " Gb";
+ }
+
+ /**
+ * Create this from a legacy flavor string.
+ *
+ * @throws IllegalArgumentException if the given string does not map to a legacy flavor
+ */
+ public static FlavorSpec fromLegacyFlavorName(String flavorString) {
+ if (flavorString.startsWith("d-")) { // A docker flavor
+ String[] parts = flavorString.split("-");
+ double cpu = Integer.parseInt(parts[1]);
+ double mem = Integer.parseInt(parts[2]);
+ double dsk = Integer.parseInt(parts[3]);
+ if (cpu == 0) cpu = 0.5;
+ if (cpu == 2 && mem == 8 ) cpu = 1.5;
+ if (cpu == 2 && mem == 12 ) cpu = 2.3;
+ return new FlavorSpec(cpu, mem, dsk, false, flavorString);
+ }
+ else {
+ return new FlavorSpec(0, 0, 0, true, flavorString);
+ }
+ }
+
+}
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java
index e64028e216f..b87f6eeec31 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java
@@ -20,34 +20,49 @@ import java.util.stream.Collectors;
*/
public class NodeFlavors {
- /** Flavors <b>which are configured</b> in this zone */
- private final ImmutableMap<String, Flavor> flavors;
+ /** Flavors which are configured in this zone */
+ private final ImmutableMap<String, Flavor> configuredFlavors;
@Inject
public NodeFlavors(FlavorsConfig config) {
ImmutableMap.Builder<String, Flavor> b = new ImmutableMap.Builder<>();
for (Flavor flavor : toFlavors(config))
b.put(flavor.name(), flavor);
- this.flavors = b.build();
+ this.configuredFlavors = b.build();
}
public List<Flavor> getFlavors() {
- return new ArrayList<>(flavors.values());
+ return new ArrayList<>(configuredFlavors.values());
}
- /** Returns a flavor by name, or empty if there is no flavor with this name. */
+ /** Returns a flavor by name, or empty if there is no flavor with this name and it cannot be created on the fly. */
public Optional<Flavor> getFlavor(String name) {
- return Optional.ofNullable(flavors.get(name));
+ if (configuredFlavors.containsKey(name))
+ return Optional.of(configuredFlavors.get(name));
+
+ FlavorSpec flavorSpec = FlavorSpec.fromLegacyFlavorName(name);
+ if (flavorSpec.allocateByLegacyName())
+ return Optional.empty();
+ else
+ return Optional.of(new Flavor(flavorSpec));
}
- /** Returns the flavor with the given name or throws an IllegalArgumentException if it does not exist */
+ /**
+ * Returns the flavor with the given name or throws an IllegalArgumentException if it does not exist
+ * and cannot be created on the fly.
+ */
public Flavor getFlavorOrThrow(String flavorName) {
return getFlavor(flavorName).orElseThrow(() -> new IllegalArgumentException("Unknown flavor '" + flavorName +
- "'. Flavors are " + canonicalFlavorNames()));
+ "'. Flavors are " + canonicalFlavorNames()));
+ }
+
+ /** Returns true if this flavor is configured or can be created on the fly */
+ public boolean exists(String flavorName) {
+ return getFlavor(flavorName).isPresent();
}
private List<String> canonicalFlavorNames() {
- return flavors.values().stream().map(Flavor::canonicalName).distinct().sorted().collect(Collectors.toList());
+ return configuredFlavors.values().stream().map(Flavor::canonicalName).distinct().sorted().collect(Collectors.toList());
}
private static Collection<Flavor> toFlavors(FlavorsConfig config) {