From 8992468386710293137f243a5aafdcbe78be4a11 Mon Sep 17 00:00:00 2001 From: Rudi Schlatte Date: Mon, 3 Jun 2024 15:41:10 +0200 Subject: [PATCH] Concretize what is a persistent volume component. Handle two different versions of specifying that a component is a volume storage Change-Id: I9d3d621388f4a14a8ed9a71515d771c1461242ea --- .../optimiser/kubevela/KubevelaAnalyzer.java | 64 ++++++++++++++++--- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/nebulous-requirements-extractor/src/main/java/eu/nebulouscloud/optimiser/kubevela/KubevelaAnalyzer.java b/nebulous-requirements-extractor/src/main/java/eu/nebulouscloud/optimiser/kubevela/KubevelaAnalyzer.java index a9efbea..0a2385d 100644 --- a/nebulous-requirements-extractor/src/main/java/eu/nebulouscloud/optimiser/kubevela/KubevelaAnalyzer.java +++ b/nebulous-requirements-extractor/src/main/java/eu/nebulouscloud/optimiser/kubevela/KubevelaAnalyzer.java @@ -10,7 +10,6 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import lombok.extern.slf4j.Slf4j; import org.ow2.proactive.sal.model.AttributeRequirement; -import org.ow2.proactive.sal.model.OperatingSystemFamily; import org.ow2.proactive.sal.model.Requirement; import org.ow2.proactive.sal.model.RequirementOperator; import java.util.ArrayList; @@ -18,6 +17,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.StreamSupport; /** * A collection of methods to extract node requirements from KubeVela files. @@ -27,6 +29,54 @@ public class KubevelaAnalyzer { private static final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); + /** + * Return true if a component is a volume storage node. If true, such a + * node should not be rewritten during deployment, and no virtual machine + * should be created.

+ * + * We currently look for one of the following structures in a component: + * + *

{@code
+     * type: raw
+     * properties:
+     *   apiVersion: v1
+     *   kind: PersistentVolumeClaim
+     * }
+ * + * or + * + *
{@code
+     * type: k8s-objects
+     * properties:
+     *   objects:
+     *   - apiVersion: v1
+     *     kind: PersistentVolumeClaim
+     * }
+ * + * Note: In the two samples above, the objects will have other attributes + * as well, which we omit for brevity.

+ * + * Note: This method should be redesigned once we discover other kinds of + * nodes (currently we have compute nodes and volume storage nodes).

+ * + * @param component The component; should be a child of the {@code spec:} + * top-level array in the KubeVela YAML. + * + * @return true if component is a volume storage node. + */ + public static final boolean isVolumeStorageComponent(JsonNode component) { + boolean form1 = component.at("/type").asText().equals("raw") + && component.at("/properties/kind").asText().equals("PersistentVolumeClaim"); + boolean form2 = component.at("/type").asText().equals("k8s-objects") + && StreamSupport.stream( + Spliterators.spliteratorUnknownSize( + component.withArray("/properties/objects") + .elements(), Spliterator.ORDERED), false) + .anyMatch((o) -> o.at("/kind").asText() + .equals("PersistentVolumeClaim")); + return form1 || form2; + } + /** * Given a KubeVela file, extract how many nodes to deploy for each * component. Note that this can be zero when the component should not be @@ -51,8 +101,7 @@ public class KubevelaAnalyzer { Map result = new HashMap<>(); ArrayNode components = kubevela.withArray("/spec/components"); for (final JsonNode c : components) { - // Skip components that define a volume - if (c.at("/type").asText().equals("raw")) continue; + if (isVolumeStorageComponent(c)) continue; result.put(c.get("name").asText(), 1); // default value; might get overwritten for (final JsonNode t : c.withArray("/traits")) { if (t.at("/type").asText().equals("scaler") @@ -183,7 +232,8 @@ public class KubevelaAnalyzer { * * - When asked to, we add the requirement that memory >= 2GB.

* - * - We skip components with `type: raw`, since these define volumes.

+ * - We skip volume storage components, since there is no virtual machine + * created for those.

* * - For the first version, we specify all requirements as "greater or * equal", i.e., we might not find precisely the node candidates that @@ -204,15 +254,13 @@ public class KubevelaAnalyzer { * includeNebulousRequirements is true. * @return a map of component name to (potentially empty) list of * requirements for that component. No requirements mean any node will - * suffice. No requirements are generated for components with - * `type:raw`. + * suffice. No requirements are generated for volume storage components. */ public static Map> getBoundedRequirements(JsonNode kubevela, boolean includeNebulousRequirements, Set cloudIDs) { Map> result = new HashMap<>(); ArrayNode components = kubevela.withArray("/spec/components"); for (final JsonNode c : components) { - // Skip components that define a volume - if (c.at("/type").asText().equals("raw")) continue; + if (isVolumeStorageComponent(c)) continue; String componentName = c.get("name").asText(); ArrayList reqs = new ArrayList<>(); if (includeNebulousRequirements) {