Feature 11074: Enhanced OSM declarative modelling for applications. OSM's SDK for intent manipulation

Change-Id: I6d03faa143eafcf30380b3b854c54f177dcf8f25
Signed-off-by: garciadeblas <gerardo.garciadeblas@telefonica.com>
diff --git a/docker/osm-nushell-krm-functions/operations/brick.nu b/docker/osm-nushell-krm-functions/operations/brick.nu
new file mode 100644
index 0000000..f0c7f64
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/brick.nu
@@ -0,0 +1,398 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+# Module with custom functions to manage the transformations and generations associated to the different types of Building Blocks supported by OSM.
+#
+# Supported Brick types are:
+#
+# - `basic`. Basic transformation of the ResourceList. It performs a cleanup and regularization of the target Kustomization (enforce the right path in the repo, ensure that `wait` is enabled, etc.), unless specified otherwise. In addition, it also supports the commonest transformations for a Kustomization, such as addition of optional components, extra labels and/or annotations, hot replacement of image names and tags, etc. For more details, check out the help for the `brick transform basic` command.
+# - `helmreleaseset`. Transformations for a ResourceList with a set of HelmReleases, so that values injected into the specific HelmReleases. It is a superset of the `basic` Brick, and its transformations are applied right after the corresponding basic transformations. For more details, check out the help for the `brick transform helmreleaseset` command.
+# - `custom`. Transformation of the ResourceList with a custom (user-provided) "create" transformation after a `basic` regularization is applied. For more details, check out the help for the `custom create` command.
+# - `custom-hr`. Transformation of the ResourceList with a custom (user-provided) "create" transformation after a `helmreleaseset` transformation (including `basic` regularizations) is applied. For more details, check out the help for the `custom create` command.
+# - `custom-full`. Transformation of the ResourceList with a custom (user-provided) "create" transformation. **No `basic` regularization is applied**, so any regularization (if needed) should be implemented in the custom command. For more details, check out the help for the `custom create` command.
+
+
+use ../krm *
+use ./location.nu
+use custom
+
+
+# Apply the `basic` transformation to ResourceList received from stdin according to the specification of the Brick.
+# The `basic` Brick transformation just does a cleanup and regularization of the target Kustomization
+export def "transform basic" [
+    brick: record  # Brick specification
+]: [
+    record -> record
+] {
+    let rl: record = $in
+
+    # Get the key parts
+    let brick_name: string = ($brick | get -i name | default "untitled-brick")
+    let kustomization_name: string = ($brick | get $.kustomization.name)
+    let kustomization_namespace: string = ($brick | get -i $.kustomization.namespace | default "flux-system")
+    let src: string = ($brick | get source | location from base path)
+    let options: record = ($brick | get -i options | default {})
+    ## Should it avoid path regularization?
+    let keep_path: bool = ($options | get -i keep-path | default false)
+    ## Should it avoid enforcing the wait?
+    let enforce_wait: bool = ($options | get -i enforce-wait | default true)
+    ## Should it avoid enforcing the prune?
+    let enforce_prune: bool = ($options | get -i enforce-prune | default true)
+    ## Should it enable (or append) some `components`?
+    let components: list = ($options | get -i components | default [])
+    ## Should it set or overwrite `targetNamespace`?
+    let targetNamespace: string = ($options | get -i targetNamespace | default "")
+    ## Should it overwrite `interval`?
+    let interval: string = ($options | get -i interval | default "")
+    ## Should it set or overwrite `retryInterval`?
+    let retryInterval: string = ($options | get -i retryInterval | default "")
+    ## Should it set or overwrite `serviceAccountName`?
+    let serviceAccountName: string = ($options | get -i serviceAccountName | default "")
+    ## Should it add custom `healthChecks`?
+    let healthChecks: list = ($options | get -i healthChecks | default [])
+    ## Should it add custom `healthCheckExprs`?
+    let healthCheckExprs: list = ($options | get -i healthCheckExprs | default [])
+    ## Should it set or overwrite a `namePrefix`?
+    let namePrefix: string = ($options | get -i namePrefix | default "")
+    ## Should it set or overwrite a `nameSuffix`?
+    let nameSuffix: string = ($options | get -i nameSuffix | default "")
+    ## Should it append additional `.metadata.labels` and `.spec.commonMetadata.labels`?
+    let new_labels: record = ($options | get -i new_labels | default {})
+    ## Should it append additional `.metadata.annotations` and `.spec.commonMetadata.annotations`?
+    let new_annotations: record = ($options | get -i new_annotations | default {})
+    ## Should it append additional `.spec.images` replacements?
+    let images: list = ($options | get -i images | default [])
+
+    # Transform as per the basic Brick model
+    $rl
+    # Path regularization, if applicable
+    | if $keep_path { $in } else {
+        $in
+        | (
+            patch resource update key
+                $.spec.path $src
+                "kustomize.toolkit.fluxcd.io/v1" Kustomization $kustomization_name $kustomization_namespace
+        )
+    }
+    # Enforce the wait, if applicable
+    | if $enforce_wait {
+        $in
+        | (
+            patch resource upsert key
+                $.spec.wait $enforce_wait
+                "kustomize.toolkit.fluxcd.io/v1" Kustomization $kustomization_name $kustomization_namespace
+        )
+    } else { $in }
+    # Enforce the prune, if applicable
+    | if $enforce_prune {
+        $in
+        | (
+            patch resource upsert key
+                $.spec.prune $enforce_prune
+                "kustomize.toolkit.fluxcd.io/v1" Kustomization $kustomization_name $kustomization_namespace
+        )
+    } else { $in }
+    # Enable (or append) some `components`, if applicable
+    | if ($components | is-not-empty) {
+        let tmp_rl: record = $in
+        let existing_components: list = (
+            $tmp_rl
+            | (
+                patch resource keep
+                    "kustomize.toolkit.fluxcd.io/v1" Kustomization $kustomization_name $kustomization_namespace
+            )
+            | get -i $.items.0.spec.components
+            | default []
+        )
+        let all_components: list = ($existing_components ++ $components) | uniq
+
+        $tmp_rl
+        | (
+            patch resource upsert key
+                $.spec.components $all_components
+                "kustomize.toolkit.fluxcd.io/v1" Kustomization $kustomization_name $kustomization_namespace
+        )
+    } else { $in }
+    # Set or overwrite `targetNamespace`, if applicable
+    | if ($targetNamespace | is-not-empty) {
+        $in
+        | (
+            patch resource upsert key
+                $.spec.targetNamespace $targetNamespace
+                "kustomize.toolkit.fluxcd.io/v1" Kustomization $kustomization_name $kustomization_namespace
+        )
+    } else { $in }
+    # Overwrite `interval`, if applicable
+    | if ($interval | is-not-empty) {
+        $in
+        | (
+            patch resource update key
+                $.spec.interval $interval
+                "kustomize.toolkit.fluxcd.io/v1" Kustomization $kustomization_name $kustomization_namespace
+        )
+    } else { $in }
+    # Set or overwrite `retryInterval`, if applicable
+    | if ($retryInterval | is-not-empty) {
+        $in
+        | (
+            patch resource upsert key
+                $.spec.retryInterval $retryInterval
+                "kustomize.toolkit.fluxcd.io/v1" Kustomization $kustomization_name $kustomization_namespace
+        )
+    } else { $in }
+    # Set or overwrite `serviceAccountName`, if applicable
+    | if ($serviceAccountName | is-not-empty) {
+        $in
+        | (
+            patch resource upsert key
+                $.spec.serviceAccountName $serviceAccountName
+                "kustomize.toolkit.fluxcd.io/v1" Kustomization $kustomization_name $kustomization_namespace
+        )
+    } else { $in }
+    # Enable (or append) some `healthChecks`, if applicable
+    | if ($healthChecks | is-not-empty) {
+        let tmp_rl: record = $in
+        let existing_healthChecks: list = ($tmp_rl | get -i $.spec.healthChecks | default [])
+        let all_healthChecks: list = ($existing_healthChecks ++ $healthChecks) | uniq
+
+        $tmp_rl
+        | (
+            patch resource upsert key
+                $.spec.healthChecks $all_healthChecks
+                "kustomize.toolkit.fluxcd.io/v1" Kustomization $kustomization_name $kustomization_namespace
+        )
+    } else { $in }
+    # Enable (or append) some `healthCheckExprs`, if applicable
+    | if ($healthCheckExprs | is-not-empty) {
+        let tmp_rl: record = $in
+        let existing_healthCheckExprs: list = ($tmp_rl | get -i $.spec.healthCheckExprs | default [])
+        let all_healthCheckExprs: list = ($existing_healthCheckExprs ++ $healthCheckExprs) | uniq
+
+        $tmp_rl
+        | (
+            patch resource upsert key
+                $.spec.healthCheckExprs $all_healthCheckExprs
+                "kustomize.toolkit.fluxcd.io/v1" Kustomization $kustomization_name $kustomization_namespace
+        )
+    } else { $in }
+    # Set or overwrite `namePrefix`, if applicable
+    | if ($namePrefix | is-not-empty) {
+        $in
+        | (
+            patch resource upsert key
+                $.spec.namePrefix $namePrefix
+                "kustomize.toolkit.fluxcd.io/v1" Kustomization $kustomization_name $kustomization_namespace
+        )
+    } else { $in }
+    # Set or overwrite `nameSuffix`, if applicable
+    | if ($nameSuffix | is-not-empty) {
+        $in
+        | (
+            patch resource upsert key
+                $.spec.nameSuffix $nameSuffix
+                "kustomize.toolkit.fluxcd.io/v1" Kustomization $kustomization_name $kustomization_namespace
+        )
+    } else { $in }
+    # Enable (or append) some `.metadata.labels` and `.spec.commonMetadata.labels`, if applicable
+    | if ($new_labels | is-not-empty) {
+        let tmp_rl: record = $in
+        let existing_labels: list = ($tmp_rl | get -i $.metadata.labels | default [])
+        let existing_common_labels: list = ($tmp_rl | get -i $.spec.commonMetadata.labels | default [])
+        let all_labels: list = ($existing_labels | merge $new_labels)
+        let all_common_labels: list = ($existing_common_labels | merge $new_labels)
+
+        $tmp_rl
+        | (
+            patch resource upsert key
+                $.metadata.labels
+                $all_labels
+                "kustomize.toolkit.fluxcd.io/v1" Kustomization $kustomization_name $kustomization_namespace
+        )
+        | (
+            patch resource upsert key
+                $.spec.commonMetadata.labels
+                $all_common_labels
+                "kustomize.toolkit.fluxcd.io/v1" Kustomization $kustomization_name $kustomization_namespace
+        )
+    } else { $in }
+    # Enable (or append) some `.metadata.annotations` and `.spec.commonMetadata.annotations`, if applicable
+    | if ($new_annotations | is-not-empty) {
+        let tmp_rl: record = $in
+        let existing_annotations: list = ($tmp_rl | get -i $.metadata.annotations | default [])
+        let existing_common_annotations: list = ($tmp_rl | get -i $.spec.commonMetadata.annotations | default [])
+        let all_annotations: list = ($existing_annotations | merge $new_annotations)
+        let all_common_annotations: list = ($existing_common_annotations | merge $new_annotations)
+
+        $tmp_rl
+        | (
+            patch resource upsert key
+                $.metadata.annotations
+                $all_annotations
+                "kustomize.toolkit.fluxcd.io/v1" Kustomization $kustomization_name $kustomization_namespace
+        )
+        | (
+            patch resource upsert key
+                $.spec.commonMetadata.annotations
+                $all_common_annotations
+                "kustomize.toolkit.fluxcd.io/v1" Kustomization $kustomization_name $kustomization_namespace
+        )
+    } else { $in }
+    # Enable (or append) some `.spec.images` replacements, if applicable
+    | if ($images | is-not-empty) {
+        let tmp_rl: record = $in
+        let existing_images: list = ($tmp_rl | get -i $.spec.images | default [])
+        let all_images: list = ($existing_images ++ $images) | uniq
+
+        $tmp_rl
+        | (
+            patch resource upsert key
+                $.spec.images $all_images
+                "kustomize.toolkit.fluxcd.io/v1" Kustomization $kustomization_name $kustomization_namespace
+        )
+    } else { $in }
+}
+
+
+# Apply the `helmreleaseset` transformation to ResourceList received from stdin according to the specification of the Brick.
+# The `basic` Brick transformation just does a cleanup and regularization of the target Kustomization
+export def "transform helmreleaseset" [
+    brick: record  # Brick specification
+]: [
+    record -> record
+] {
+    # Input ReleaseList after basic transformations
+    let rl: record = $in
+
+    # Get the key parts
+    let brick_name: string = ($brick | get -i name | default "untitled-brick")
+    let kustomization_name: string = ($brick | get $.kustomization.name)
+    let kustomization_namespace: string = ($brick | get -i $.kustomization.namespace | default "flux-system")
+    let hrset_values: list<record> = ($brick | get "hrset-values" | default [])
+    let public_age_key: string = ($brick | get -i $.public-age-key | default "")
+
+    # Apply HelmRelease-specific transformations
+    $hrset_values
+    | reduce --fold $rl {|elt, acc|
+        $acc
+        | (
+            overlaypatch helmrelease set values
+                # --ks-namespace: string
+                --ks-namespace $kustomization_namespace
+                # --hr-namespace: string
+                --hr-namespace ($elt | get $.HelmRelease.namespace)
+                # --operation: string = "add"
+                # --cm-key: string = "values.yaml"
+                --cm-key ($elt | get -i $.valuesFrom.configMapKeyRef.key | default "values.yaml")
+                # --cm-target-path: string
+                # --cm-optional
+                # --create-cm-with-values: record
+                --create-cm-with-values ($elt | get -i "create-cm" | default {})
+                # --secret-key: string = "values.yaml"
+                --secret-key ($elt | get -i $.valuesFrom.secretKeyRef.key | default "values.yaml")
+                # --secret-target-path: string
+                # --secret-optional
+                # --create-secret-with-values: record
+                --create-secret-with-values (
+                    $env
+                    | get -i ($elt | get -i $.create-secret.env-values-reference | default "")
+                    | default {}
+                )
+                # --public-age-key: string
+                --public-age-key $public_age_key
+                # kustomization_name: string
+                $kustomization_name
+                # helmrelease_name: string
+                ($elt | get $.HelmRelease.name)
+                # inline_values?: record
+                ($elt | get -i "inline-values" | default {})
+                # cm_name?: string
+                ($elt | get -i $.valuesFrom.configMapKeyRef.name | default "")
+                # secret_name?: string
+                ($elt | get -i $.valuesFrom.secretKeyRef.name | default "")
+        )
+    }
+}
+
+
+# Transform the ResourceList received from stdin according to the specification of a Brick transformation.
+#
+export def transform [
+    brick: record  # Brick specification
+    environment: record = {}    # Record with environment variables to load
+]: [
+    record -> record
+] {
+    # Get input ResourceList
+    let rl: record = $in
+
+    # Get the brick name
+    let brick_name: string = ($brick | get -i name | default "untitled-brick")
+
+    # Update the environment to include the brick name
+    let updated_environment: record = (
+        $environment
+        | upsert $.BRICK_NAME $brick_name
+    )
+
+    # Update the brick record accordingly
+    let updated_brick: record = (
+        $brick
+        | replace vars $updated_environment
+    )
+
+    # Get other key parts
+    let brick_type: string = ($updated_brick | get -i type | default "basic" | str downcase)
+
+    # Apply transformation according to the brick type
+    with-env $updated_environment {
+        match $brick_type {
+            "basic" => {
+                # Basic transformation of the ResourceList (just cleanup and regularization)
+                $rl
+                | transform basic $updated_brick
+            },
+            "helmreleaseset" => {
+                # Transformation of the ResourceList with a set of HelmReleases
+                $rl
+                | transform basic $updated_brick
+                | transform helmreleaseset $updated_brick
+            },
+            "custom-full" => {
+                # Transformation of the ResourceList with a custom "create" transformation
+                $rl
+                | custom brick create $updated_brick $updated_environment
+            },
+            "custom" => {
+                # Transformation of the ResourceList with a custom "create" transformation, after a basic cleanup and regularization
+                $rl
+                | transform basic $updated_brick
+                | custom brick create $updated_brick $updated_environment
+            },
+            "custom-hr" => {
+                # Transformation of the ResourceList with a custom "create" transformation, after a `helmreleaseset` transformation
+                $rl
+                | transform basic $updated_brick
+                | transform helmreleaseset $updated_brick
+                | custom brick create $updated_brick $updated_environment
+            },
+            _ => {
+                # Unknown brick type, throw an error
+                error make { msg: $"Error: Unknown Brick type: ($updated_brick | get type)" }
+            }
+        }
+    }
+}