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/app.nu b/docker/osm-nushell-krm-functions/operations/app.nu
new file mode 100644
index 0000000..09a3085
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/app.nu
@@ -0,0 +1,211 @@
+#######################################################################################
+# 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 an App instance, invoking the corresponding KSU renderizations to appropriate target folders in a given profile.
+
+
+# Import required modules
+use ../krm *
+# use ./replace.nu *
+# use ./ksu.nu *
+use ./replace.nu
+use ./ksu.nu
+
+
+# Create an instance of an App, based on an App instance model received from stdin.
+export def create [
+ --dry-run # If set, only prints the generated ResourceList(s) along with the target folder(s) (i.e., it does not write to any folder).
+ --print-target-folders # If set, print the target folder(s). Requires --dry-run.
+ environment: record # Record with environment variables to load.
+]: [
+ record -> nothing
+ record -> table
+] {
+ # TODO: Format checks
+
+ # Save the original app instance record
+ let in_instance: record = $in
+
+ # Remove from the environment those keys that are reserved, dynamic or forbidden, since they will be overriden or may cause known issues, and add one that mimics the KSU name
+ const forbidden_keys: list<cell-path> = [
+ $.KSU_NAME
+ $.PATTERN_NAME
+ $.BRICK_NAME
+ # Add new reserved keys here as needed:
+ # . . .
+ ]
+ let updated_environment: record = (
+ $environment
+ | reject -i ...$forbidden_keys
+ )
+
+ # Load environment variables and update the record
+ let instance_rendered: record = (
+ $in_instance
+ | replace vars $updated_environment
+ )
+
+ # Get the key parts
+ let app_name: string = ($instance_rendered | get $.metadata.name | str downcase)
+ let spec: record = ($instance_rendered | get spec)
+ let ksus: list<record> = ($spec | get ksus)
+
+ # Process all App's KSUs
+ $ksus | each {|k|
+ $k
+ | ksu create --dry-run=$dry_run --print-target-folder=$print_target_folders $updated_environment
+ }
+ # Make sure it only returns a value when its's not an empty list
+ | if ($in | is-not-empty) { $in } else { $in | ignore }
+}
+
+
+# Delete an instance of an App, based on an App instance model received from stdin.
+export def delete [
+ --dry-run # If set, only prints the ResourceList(s) with the resources that would be removed.
+ --print-target-folders # If set, print the target folder(s) to be removed. Requires --dry-run.
+ environment: record # Record with environment variables to load.
+]: [
+ record -> nothing
+ record -> table
+] {
+ # Save the original app instance record
+ let in_instance: record = $in
+
+ # Remove from the environment those keys that are reserved, dynamic or forbidden, since they will be overriden or may cause known issues, and add one that mimics the KSU name
+ const forbidden_keys: list<cell-path> = [
+ $.KSU_NAME
+ $.PATTERN_NAME
+ $.BRICK_NAME
+ # Add new reserved keys here as needed:
+ # . . .
+ ]
+ let updated_environment: record = (
+ $environment
+ | reject -i ...$forbidden_keys
+ )
+
+ # Load environment variables and update the record
+ let instance_rendered: record = (
+ $in_instance
+ | replace vars $updated_environment
+ )
+
+ # Get the key parts
+ let app_name: string = ($instance_rendered | get $.metadata.name | str downcase)
+ let spec: record = ($instance_rendered | get spec)
+ let ksus: list<record> = ($spec | get ksus)
+
+ # Process all App's KSUs
+ $ksus | each {|k|
+ $k
+ | ksu delete --dry-run=$dry_run --print-target-folder=$print_target_folders $updated_environment
+ }
+ # Make sure it only returns a value when its's not an empty list
+ | if ($in | is-not-empty) { $in } else { $in | ignore }
+}
+
+
+# Update an instance of an App, based on an App instance model received from stdin.
+export def "update existing" [
+ --dry-run # If set, only prints the ResourceList(s) with the resources that would be removed.
+ --print-target-folders # If set, print the target folder(s) to be updated. Requires --dry-run.
+ --diff-files # If set, returns the list of files expected to change in the target folder(s). Requires --dry-run.
+ --diffs # If set, returns the expected full diff expected to change in the target folder(s). Requires --dry-run. It can be combined with `--diff-files`
+ environment: record # Record with environment variables to load.
+]: [
+ record -> nothing
+ record -> table
+ record -> string
+] {
+ # Save the original app instance record
+ let in_instance: record = $in
+
+ # Remove from the environment those keys that are reserved, dynamic or forbidden, since they will be overriden or may cause known issues, and add one that mimics the KSU name
+ const forbidden_keys: list<cell-path> = [
+ $.KSU_NAME
+ $.PATTERN_NAME
+ $.BRICK_NAME
+ # Add new reserved keys here as needed:
+ # . . .
+ ]
+ let updated_environment: record = (
+ $environment
+ | reject -i ...$forbidden_keys
+ )
+
+ # Load environment variables and update the record
+ let instance_rendered: record = (
+ $in_instance
+ | replace vars $updated_environment
+ # Overwrite the ksu section with its original values, since we do not want to replace the placeholders yet
+ | upsert $.spec.ksus ($in_instance | get $.spec.ksus)
+ )
+
+ # Get the key parts
+ let app_name: string = ($instance_rendered | get $.metadata.name | str downcase)
+ let spec: record = ($instance_rendered | get spec)
+ let ksus: list<record> = ($spec | get ksus)
+
+ # Process all App's KSUs
+ $ksus | each {|k|
+ $k
+ | (
+ ksu update
+ --print-target-folder=$print_target_folders
+ --dry-run=$dry_run
+ --diff-files=$diff_files
+ --diff=$diffs
+ $updated_environment
+ )
+ }
+ # Make sure it only returns a value when it is not an empty list
+ | if ($in | is-not-empty) {
+ let output: any = $in
+
+ # If the output is a list of strings, it better provides their concatenation
+ let output_type: string = ($output | describe)
+ if ($output_type == "list<string>") {
+ $output | str join "\n"
+ # Otherwise, it returns the value as it is
+ } else {
+ $output
+ }
+ } else { $in | ignore }
+}
+
+export alias update = update existing
+
+
+# Get the Kustomizations that would be created on an instance of an App, based on an App instance model received from stdin.
+export def "get kustomization" [
+ environment: record # Record with environment variables to load.
+]: [
+ record -> record
+] {
+ create --dry-run $environment
+ | get $.items | default []
+ | flatten
+ | where apiVersion == 'kustomize.toolkit.fluxcd.io/v1'
+ | where kind == 'Kustomization'
+ | get $.metadata
+ | select name namespace
+ | default 'flux-system' namespace
+}
+
+export alias "get kustomizations" = get kustomization
+export alias "get ks" = get kustomization
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)" }
+ }
+ }
+ }
+}
diff --git a/docker/osm-nushell-krm-functions/operations/custom/mod.nu b/docker/osm-nushell-krm-functions/operations/custom/mod.nu
new file mode 100644
index 0000000..c82b4d3
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/custom/mod.nu
@@ -0,0 +1,109 @@
+#######################################################################################
+# 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.
+#######################################################################################
+
+# Placeholder module for supporting custom transformations
+
+# Import SDK modules
+use ../../krm *
+use ../location.nu
+
+
+# Placeholder for a custom "create" transformation for a Brick of `custom`, `custom-hr`, or `full-custom` types, to be applied to the ResourceList received from stdin.
+# - If the Brick is of `custom` type, a `basic` Brick transformation will be applied right before in the pipeline.
+# - If the Brick is of `custom-hr` type, a `helmreleaseset` Brick transformation will be applied right before in the pipeline.
+# - If the Brick is of `full-custom` type, only this transformation will be applied to the original ResourceList.
+export def "create" [
+ brick: record # Brick specification
+ environment: record = {} # Record with environment variables to load
+]: [
+ record -> record
+] {
+ let rl: record = $in
+
+ # Get the key parts
+ let brick_name: string = ($brick | get -i name | default "untitled-brick")
+ let brick_type: string = ($brick | get -i type | default "basic" | str downcase)
+ let kustomization_name: string = ($brick | get $.kustomization.name)
+ let kustomization_namespace: string = ($brick | get -i $.kustomization.namespace | default "flux-system")
+
+ # Here would come your custom transformations over `rl`
+ # . . .
+ print $"Here we are applying a custom `create` transformation of '($brick_type)' type."
+ # The print above is just informative. Please remove in your final custom transformation.
+
+ # Here we should return the result of the custom transformations.
+ # For the sake of the example, let's return just the original ResouceList with no transformations
+ $rl
+}
+
+
+# Placeholder for a custom "update" transformation for a Brick of `custom`, `custom-hr`, or `full-custom` types, to be applied to the ResourceList received from stdin.
+# - If the Brick is of `custom` type, a `basic` Brick transformation will be applied right before in the pipeline.
+# - If the Brick is of `custom-hr` type, a `helmreleaseset` Brick transformation will be applied right before in the pipeline.
+# - If the Brick is of `full-custom` type, only this transformation will be applied to the original ResourceList.
+export def "update" [
+ brick: record # Brick specification
+ environment: record = {} # Record with environment variables to load
+]: [
+ record -> record
+] {
+ let rl: record = $in
+
+ # Get the key parts
+ let brick_name: string = ($brick | get -i name | default "untitled-brick")
+ let brick_type: string = ($brick | get -i type | default "basic" | str downcase)
+ let kustomization_name: string = ($brick | get $.kustomization.name)
+ let kustomization_namespace: string = ($brick | get -i $.kustomization.namespace | default "flux-system")
+
+ # Here would come your custom transformations over `rl`
+ # . . .
+ print $"Here we are applying a custom `update` transformation of '($brick_type)' type."
+ # The print above is just informative. Please remove in your final custom transformation.
+
+ # Here we should return the result of the custom transformations.
+ # For the sake of the example, let's return just the original ResouceList with no transformations
+ $rl
+}
+
+
+# Placeholder for a custom "delete" transformation for a Brick of `custom`, `custom-hr`, or `full-custom` types, to be applied to the ResourceList received from stdin.
+# - If the Brick is of `custom` type, a `basic` Brick transformation will be applied right before in the pipeline.
+# - If the Brick is of `custom-hr` type, a `helmreleaseset` Brick transformation will be applied right before in the pipeline.
+# - If the Brick is of `full-custom` type, only this transformation will be applied to the original ResourceList.
+export def "delete" [
+ brick: record # Brick specification
+ environment: record = {} # Record with environment variables to load
+]: [
+ record -> record
+] {
+ let rl: record = $in
+
+ # Get the key parts
+ let brick_name: string = ($brick | get -i name | default "untitled-brick")
+ let brick_type: string = ($brick | get -i type | default "basic" | str downcase)
+ let kustomization_name: string = ($brick | get $.kustomization.name)
+ let kustomization_namespace: string = ($brick | get -i $.kustomization.namespace | default "flux-system")
+
+ # Here would come your custom transformations over `rl`
+ # . . .
+ print $"Here we are applying a custom `delete` transformation of '($brick_type)' type."
+ # The print above is just informative. Please remove in your final custom transformation.
+
+ # Here we should return the result of the custom transformations.
+ # For the sake of the example, let's return just the original ResouceList with no transformations
+ $rl
+}
diff --git a/docker/osm-nushell-krm-functions/operations/ksu.nu b/docker/osm-nushell-krm-functions/operations/ksu.nu
new file mode 100644
index 0000000..ffcc19f
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/ksu.nu
@@ -0,0 +1,233 @@
+#######################################################################################
+# 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 KSUs and their renderization of the corresponding ResourceList into a given target folder.
+
+
+use ../krm *
+use ./location.nu
+use ./pattern.nu
+
+
+# Render a KSU, based on a KSU instance model received from stdin.
+export def create [
+ --dry-run # If set, only prints the generated ResourceList(s) along with the target folder(s) (i.e., it does not write to any folder)
+ --print-target-folder # If set, prints the target folder. Requires --dry-run
+ environment: record = {} # Record with environment variables to load
+]: [
+ record -> record
+ record -> nothing
+] {
+ # Get KSU structure
+ let in_ksu: record = $in
+
+ # Get the KSU name
+ let ksu_name: string = ($in_ksu | get "name")
+
+ # Add to the environment a key with the KSU name, so that it can be replaced
+ let updated_environment: record = (
+ $environment
+ | upsert $.KSU_NAME $ksu_name
+ )
+
+ # Update the KSU record accordingly
+ let updated_ksu: record = (
+ $in_ksu
+ | replace vars $updated_environment
+ )
+
+ # Get the rest of key parts
+ let sync: bool = ($updated_ksu | get -i "sync" | default false)
+ let target: string = (
+ $updated_ksu
+ | get "target"
+ | location to absolute path
+ )
+ let patterns: list<record> = ($updated_ksu | get "patterns")
+
+ # Process all the patterns and create a list of ResourceLists using the updated environment
+ $patterns
+ | each {|pat|
+ $pat
+ | pattern create $updated_environment
+ }
+ # Merge all the ResourceLists
+ | reduce {|elt, acc|
+ $acc
+ | concatenate resourcelists $elt
+ }
+ # Render
+ | if $dry_run {
+ if $print_target_folder { print $"TARGET FOLDER: ($target)" }
+ $in
+ } else {
+ $in
+ | convert resourcelist to folder --sync=$sync $target
+ }
+}
+
+
+# Delete a KSU, based on a KSU instance model received from stdin.
+export def delete [
+ --dry-run # If set, only prints the ResourceList(s) that would be removed (i.e., it does not write to any folder).
+ --print-target-folder # If set, prints the target folder and the list of files to be delete delete. Requires --dry-run.
+ environment: record = {} # Record with environment variables to load.
+]: [
+ record -> record
+ record -> nothing
+] {
+ # Get KSU structure
+ let in_ksu: record = $in
+
+ # Get the KSU name
+ let ksu_name: string = ($in_ksu | get "name")
+
+ # Add to the environment a key with the KSU name, so that it can be replaced
+ let updated_environment: record = (
+ $environment
+ | upsert $.KSU_NAME $ksu_name
+ )
+
+ # Update the KSU record accordingly
+ let updated_ksu: record = (
+ $in_ksu
+ | replace vars $updated_environment
+ )
+
+ # Get the rest of key parts
+ let target: string = (
+ $updated_ksu
+ | get "target"
+ | location to absolute path
+ )
+
+ # Delete
+ | if $dry_run {
+ if $print_target_folder {
+ print $"TARGET FOLDER: ($target)"
+ (ls ($"($target)/**/*" | into glob ) | table -e | print)
+ }
+ # Returns the ResourceList that would be deleted
+ {} | convert folder to resourcelist $target
+ } else {
+ rm -rf $target
+ }
+}
+
+
+# Update a KSU, based on a KSU instance model received from stdin.
+export def update [
+ --dry-run # If set, only prints the ResourceList(s) that would be re-generated (i.e., it does not write to any folder).
+ --print-target-folder # If set, print the target folder(s) to be updated. Requires --dry-run.
+ --diff-files # If set, lists the expected diff with respect to the existing folder(s). Requires --dry-run.
+ --diff # If set, prints the expected diff with respect to the existing folder(s). Requires --dry-run. It can be combined with `--diff-files`.
+ environment: record = {} # Record with environment variables to load.
+]: [
+ record -> record
+ record -> string
+ record -> nothing
+] {
+ # Get KSU structure
+ let in_ksu: record = $in
+
+ # If it is not a dry-run, we simply need to re-create the KSU and return
+ if not $dry_run {
+ ## Note that the raw input variables are used, since the full environment pre-processing already happens in both custom commands
+ $in_ksu | delete $environment
+ $in_ksu | create $environment
+
+ return
+ }
+ # ... otherwise, all the dry-run calculations will need to be performed
+
+ # Get the KSU name
+ let ksu_name: string = ($in_ksu | get "name")
+
+ # Calculate the original target folder
+ let target: string = (
+ $in_ksu
+ | replace vars ($environment | upsert $.KSU_NAME $ksu_name)
+ | get "target"
+ | location to absolute path
+ )
+
+ # Generate the resource contents of the planned update in a temporary fleet repos base
+ let tmp_fleet_repos_base: path = (mktemp -t -d)
+
+ let tmp_environment: record = (
+ $environment
+ | upsert $.FLEET_REPOS_BASE $tmp_fleet_repos_base
+ )
+
+ let tmp_target: string = (
+ $in_ksu
+ | replace vars ($tmp_environment | upsert $.KSU_NAME $ksu_name)
+ | get "target"
+ | location to absolute path
+ )
+
+ # Render the desired manifests into a temporary location
+ $in_ksu | create $tmp_environment
+
+ # If specified, prints the target folder
+ if $print_target_folder {
+ print $"TARGET FOLDER: ($target)\n"
+ }
+
+ # If specified, prints all the differences with respect to the original folder
+ if ($diff_files or $diff) {
+ let differences: string = (
+ []
+ # Add list of different files, if needed
+ | if $diff_files {
+ $in
+ | append (
+ ^diff -rqN $target $tmp_target
+ # Prevent the diff error code, due to potential file differences, ends up breaking the full pipeline
+ | complete | get stdout
+ )
+ # Add detail of differences, if needed
+ } else { $in }
+ | if $diff {
+ $in
+ | append (
+ ^diff -rN $target $tmp_target
+ # Prevent the diff error code, due to potential file differences, ends up breaking the full pipeline
+ | complete | get stdout
+ )
+ } else { $in }
+ | str join "\n\n"
+ )
+
+ # Remove the temporary fleet repos base folder
+ rm -rf $tmp_fleet_repos_base
+
+ # Return the differences found by diff
+ $differences
+
+ # Otherwise, just returns the planned ResourceList
+ } else {
+ # Converts the planned resources to a ResourceList
+ let output_rl: record = ( {} | convert folder to resourcelist $tmp_target )
+
+ # Remove the temporary fleet repos base folder
+ rm -rf $tmp_fleet_repos_base
+
+ # Finally, returns the calculated ResourceList
+ $output_rl
+ }
+}
diff --git a/docker/osm-nushell-krm-functions/operations/location.nu b/docker/osm-nushell-krm-functions/operations/location.nu
new file mode 100644
index 0000000..1f568f3
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/location.nu
@@ -0,0 +1,173 @@
+#######################################################################################
+# 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.
+#######################################################################################
+
+# Helper module to manage KSU source or target locations, so that they can be safely translated to well-known paths in the local filesystem.
+
+
+# Helper function to convert a profile type name to the canonical name for that profile type so that it can build a folder path deterministically.
+# NOT EXPORTED
+def "normalize profile type" [
+]: [
+ string -> string
+] {
+ $in
+ | if $in in ["controller", "infra-controller", "infra-controllers", "infra_controller", "infra_controllers"] {
+ "infra-controller-profiles"
+ } else if $in in ["config", "infra-config", "infra-configs", "infra_config", "infra_configs"] {
+ "infra-config-profiles"
+ } else if $in in ["managed", "resources", "managed-resources", "managed_resources"] {
+ "managed-resources"
+ } else if $in in ["app", "apps", "applications", "cnf", "cnfs", "nf", "nfs"] {
+ "app-profiles"
+ } else {
+ $in
+ }
+}
+
+
+# Helper function to convert an OKA type name to the canonical name for that OKA type so that it can build a folder path deterministically.
+# NOT EXPORTED
+def "normalize oka type" [
+]: [
+ string -> string
+] {
+ $in
+ | if $in in ["controller", "infra-controller", "infra-controllers", "infra_controller", "infra_controllers"] {
+ "infra-controllers"
+ } else if $in in ["config", "infra-config", "infra-configs", "infra_config", "infra_configs"] {
+ "infra-configs"
+ } else if $in in ["managed", "resources", "managed-resources", "managed_resources", "cloud-resources", "cloud_resources"] {
+ "cloud-resources"
+ } else if $in in ["app", "apps", "applications", "cnf", "cnfs", "nf", "nfs"] {
+ "apps"
+ } else {
+ $in
+ }
+}
+
+
+# Convert a location into its components to determine a path in the local filesystem.
+export def "to path components" [
+ default_project_name: string = "osm_admin" # Default project name
+ default_repos_base: string = "/repos" # Base path for the local repo clones
+]: [
+ record -> list<path>
+] {
+ let in_location: record = $in
+
+ # Absolute path of the local repo clone
+ let repo: string = (
+ $in_location
+ # Is it a path?
+ | if ($in | get -i "repo-path" | is-not-empty ) {
+ # $in_location
+ $in
+ | get "repo-path"
+ | path join
+ # Maybe it was specified by repo name?
+ } else if (
+ ($in | get -i "repo-name" | is-not-empty )
+ ) {
+ [
+ # ($in_location | get -i "repos-base" | default $default_repos_base),
+ # ($in_location | get "repo-name")
+ ($in | get -i "repos-base" | default $default_repos_base),
+ ($in | get "repo-name")
+ ]
+ | path join
+ # Otherwise, throws an error
+ } else {
+ error make { msg: $"Error: Invalid location spec. Missing `repo-path` or `repo-name` key. Non conformant: \n($in_location | to yaml)"}
+ }
+ # Ensure that the absolute path starts by "/"
+ | if ($in | str starts-with "/") {
+ $in
+ } else {
+ $"/($in)"
+ }
+ )
+
+ # Get the base path prior to the last item (e.g., profile path or OKA folder)
+ let base: string = (
+ $in_location
+ # Is it a path?
+ | if ($in | get -i "base-path" | is-not-empty ) {
+ $in
+ | get "base-path"
+ | path join
+ # Maybe it is a profile spec?
+ } else if (
+ ($in | get -i "profile-type" | is-not-empty ) and
+ ($in | get -i "profile-name" | is-not-empty )
+ ) {
+ [
+ ($in | get -i "project-name" | default $default_project_name),
+ ($in | get "profile-type" | normalize profile type),
+ ($in | get "profile-name")
+ ]
+ | path join
+ # Maybe it is an OKA subfolder spec?
+ } else if (
+ ($in | get -i "oka-type" | is-not-empty ) and
+ ($in | get -i "oka-name" | is-not-empty )
+ ) {
+ [
+ ($in | get "oka-type" | normalize oka type),
+ ($in | get "oka-name")
+ ]
+ | path join
+ # Otherwise, it is malformed
+ } else {
+ error make { msg: $"Error: Invalid location spec. Missing `base-path` or `profile-type`+`profile-name` or `oka-type`+`oka-name` key. Non conformant: \n($in | to yaml)"}
+ }
+ )
+
+ # Check that the final relative path is available
+ if ($in_location | get -i "relative-path" | is-empty ) {
+ error make { msg: $"Error: Invalid location spec. Missing `relative-path` key. Non conformant: \n($in_location | to yaml)"}
+ }
+
+ # Finally, return the path components
+ [ $repo, $base, ($in_location | get "relative-path" | path join) ]
+}
+
+
+# Convert a location to an absolute path in the local filesystem.
+export def "to absolute path" [
+ default_project_name: string = "osm_admin" # Default project name
+ default_repos_base: string = "/repos" # Base path for the local repo clones
+]: [
+ record -> path
+] {
+ $in
+ | to path components $default_project_name $default_repos_base
+ | path join
+}
+
+
+# Convert a location to a relative path in the local filesystem with respect to the root of the locally cloned repo.
+export def "from base path" [
+ default_project_name: string = "osm_admin" # Default project name
+]: [
+ record -> path
+] {
+ $in
+ | to path components $default_project_name
+ # Drop the first item (the `repo-path`)
+ | skip 1
+ | path join
+}
diff --git a/docker/osm-nushell-krm-functions/operations/mod.nu b/docker/osm-nushell-krm-functions/operations/mod.nu
new file mode 100644
index 0000000..9be239b
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/mod.nu
@@ -0,0 +1,30 @@
+#######################################################################################
+# 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.
+#######################################################################################
+
+# Meta-module of custom commands for common operations managing and modifying App intents in a generalized fashion.
+# This meta-module comprises all the modules of with the high-level commands for App Modelling.
+
+
+# Import SDK modules
+use ../krm *
+
+# Import submodules
+export module ./app.nu
+export module ./ksu.nu
+export module ./pattern.nu
+export module ./brick.nu
+export module ./location.nu
diff --git a/docker/osm-nushell-krm-functions/operations/pattern.nu b/docker/osm-nushell-krm-functions/operations/pattern.nu
new file mode 100644
index 0000000..d59842e
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/pattern.nu
@@ -0,0 +1,83 @@
+#######################################################################################
+# 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 a Pattern definition, taking into account its corresponding source template and the set of transformations specified for its constituent Bricks.
+
+
+use ../krm *
+use ./replace.nu
+use ./location.nu
+use ./brick.nu
+
+# Generate a ResourceList based on a Pattern instance model received from stdin.
+#
+# Initially, the ResourceList will be generated from the templates at the `source` location (replacing environment variables as needed), and then the transformations indicated by the Bricks will be applied.
+export def create [
+ environment: record = {} # Record with environment variables to load
+]: [
+ record -> record
+] {
+ let in_pattern: record = $in
+
+ # Get the pattern name and its parameters
+ let pattern_name: string = ($in_pattern | get "name")
+ let pattern_params: record = (
+ $in_pattern
+ | get -i "parameters"
+ | default {}
+ # If applicable, update placeholder values at the custom environment parameters
+ | replace vars (
+ $environment
+ | upsert $.PATTERN_NAME $pattern_name
+ )
+ )
+
+ # Update the environment to include the pattern name
+ let updated_environment: record = (
+ $environment
+ | upsert $.PATTERN_NAME $pattern_name
+ | merge $pattern_params
+ )
+
+ # Update the pattern record accordingly
+ let updated_pattern: record = (
+ $in_pattern
+ | replace vars $updated_environment
+ )
+
+ # Get other key parts
+ let src: string = (
+ $updated_pattern
+ | get "source"
+ | location to absolute path
+ )
+ let bricks: list<record> = ($updated_pattern | get "bricks")
+
+ # Generate ResourceList from source template folder
+ let rl: record = (
+ convert folder to resourcelist $src
+ | replace vars $updated_environment
+ )
+
+ # Apply transformations according to the specified bricks
+ with-env $updated_environment {
+ $bricks
+ | reduce --fold $rl {|elt, acc|
+ $acc | brick transform $elt $updated_environment
+ }
+ }
+}
diff --git a/docker/osm-nushell-krm-functions/operations/replace.nu b/docker/osm-nushell-krm-functions/operations/replace.nu
new file mode 100644
index 0000000..0181c40
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/replace.nu
@@ -0,0 +1,58 @@
+#######################################################################################
+# 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 helper functions to manage the replacement of placeholder variables by the values of well-known enviroment variables.
+
+
+# Helper function to replace placeholder variables by the content of their homonym environment variables in a record received from stdin.
+export def vars [
+ environment: record # Record with environment variables to load
+ defaults: record = {
+ FLEET_REPOS_BASE: "/repos"
+ CATALOG_REPOS_BASE: "/repos"
+ PROJECT_NAME: "osm_admin"
+ } # Record with default values for the variables to be replaced
+]: [
+ record -> record
+] {
+ let in_record: record = $in
+
+ # Environment with default values when undefined
+ let full_environment: record = (
+ $defaults
+ | merge $environment
+ )
+
+ let variable_enumeration: string = (
+ $full_environment
+ | columns
+ | each { |col|
+ $"\${($col)}"
+ }
+ | str join ","
+ )
+
+ $in_record
+ | to yaml
+ | with-env $full_environment {
+ $in
+ | (
+ ^envsubst $variable_enumeration
+ )
+ }
+ | from yaml
+}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/app.nu b/docker/osm-nushell-krm-functions/operations/tests/app.nu
new file mode 100644
index 0000000..ec5cb18
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/app.nu
@@ -0,0 +1,261 @@
+#######################################################################################
+# 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.
+#######################################################################################
+
+# Tests of App instance management
+
+use ../../krm *
+use ../app.nu
+use ../location.nu
+use ../replace.nu
+
+
+# --- all-in-one example (example 1) ---
+
+export def "test app example one" []: [
+ nothing -> nothing
+] {
+ let expected: list<record> = (
+ open artifacts/sw-catalogs/apps/example1/expected_result.yaml
+ )
+
+ let fleet_repos_base: path = (mktemp -t -d)
+
+ let environment: record = {
+ FLEET_REPOS_BASE: $fleet_repos_base
+ CATALOG_REPOS_BASE: ($env.pwd | path join artifacts)
+ APPNAME: myapp01
+ APPNAMESPACE: app-namespace
+ PROFILE_TYPE: apps
+ PROFILE_NAME: mycluster01
+ secret-values-for-postgres-operator-myapp01: {
+ POSTGRES_OPERATOR_HOST: postgres-operator-host
+ POSTGRES_OPERATOR_PORT: 5432
+ POSTGRES_OPERATOR_USER: postgres-operator-user
+ POSTGRES_OPERATOR_PASSWORD: postgres-operator-password
+ }
+ secret-values-for-postgres-operator-ui-myapp01: {
+ POSTGRES_OPERATOR_UI_HOST: postgres-operator-ui-host
+ POSTGRES_OPERATOR_UI_PORT: 8080
+ POSTGRES_OPERATOR_UI_USER: postgres-operator-ui-user
+ POSTGRES_OPERATOR_UI_PASSWORD: postgres-operator-ui-password
+ }
+ }
+
+ let actual: list<record> = (
+ open artifacts/sw-catalogs/apps/example1/app-instance-from-model.yaml
+ | app create --dry-run $environment
+ )
+
+ # Overwrites the encrypted part of the secrets in both
+ let actual_trimmed: list<record> = (
+ $actual
+ # For each KSU's ResourceList
+ | each {|k|
+ $k
+ # Delete sops key from all secrets
+ | ( patch resource reject key $.sops '' Secret )
+ # Replace encrypted value by empty string
+ | ( patch resource update key $.data."values.yaml" 'ENCRYPTED' '' Secret )
+ }
+ )
+ let expected_trimmed: list<record> = (
+ $expected
+ # For each KSU's ResourceList
+ | each {|k|
+ $k
+ # Delete sops key from all secrets
+ | ( patch resource reject key $.sops '' Secret )
+ # Replace encrypted value by empty string
+ | ( patch resource update key $.data."values.yaml" 'ENCRYPTED' '' Secret )
+ }
+ )
+
+ # Checks
+ assert equal $actual_trimmed $expected_trimmed
+
+ # Cleanup
+ rm -rf $fleet_repos_base
+}
+
+
+# --- all-in-one example (example 2) ---
+
+export def "test app example two" []: [
+ nothing -> nothing
+] {
+ let expected: list<record> = (
+ open artifacts/sw-catalogs/apps/example2/expected_result.yaml
+ )
+ let fleet_repos_base: path = (mktemp -t -d)
+
+ let environment: record = {
+ FLEET_REPOS_BASE: $fleet_repos_base
+ CATALOG_REPOS_BASE: ($env.pwd | path join artifacts)
+ APPNAME: myapp02
+ APPNAMESPACE: app-namespace
+ PROFILE_TYPE: apps
+ PROFILE_NAME: mycluster02
+ secret-values-for-postgres-operator-myapp02: {
+ POSTGRES_OPERATOR_HOST: postgres-operator-host
+ POSTGRES_OPERATOR_PORT: 5432
+ POSTGRES_OPERATOR_USER: postgres-operator-user
+ POSTGRES_OPERATOR_PASSWORD: postgres-operator-password
+ }
+ secret-values-for-postgres-operator-ui-myapp01: {
+ POSTGRES_OPERATOR_UI_HOST: postgres-operator-ui-host
+ POSTGRES_OPERATOR_UI_PORT: 8080
+ POSTGRES_OPERATOR_UI_USER: postgres-operator-ui-user
+ POSTGRES_OPERATOR_UI_PASSWORD: postgres-operator-ui-password
+ }
+ }
+
+ let actual: list<record> = (
+ open artifacts/sw-catalogs/apps/example2/app-instance-from-model.yaml
+ | app create --dry-run $environment
+ )
+
+ # Overwrites the encrypted part of the secrets in both
+ let actual_trimmed: list<record> = (
+ $actual
+ # For each KSU's ResourceList
+ | each {|k|
+ $k
+ # Delete sops key from all secrets
+ | ( patch resource reject key $.sops '' Secret )
+ # Replace encrypted value by empty string
+ | ( patch resource update key $.data."values.yaml" 'ENCRYPTED' '' Secret )
+ }
+ )
+ let expected_trimmed: list<record> = (
+ $expected
+ # For each KSU's ResourceList
+ | each {|k|
+ $k
+ # Delete sops key from all secrets
+ # | ( patch resource reject key $.sops '' Secret )
+ # Replace encrypted value by empty string
+ # | ( patch resource update key $.data."values.yaml" 'ENCRYPTED' '' Secret )
+ }
+ )
+
+ # Checks
+ assert equal $actual_trimmed $expected_trimmed
+
+ # Cleanup
+ rm -rf $fleet_repos_base
+}
+
+
+export def "test app example two written to folder" []: [
+ nothing -> nothing
+] {
+ let expected: list<record> = (
+ open artifacts/sw-catalogs/apps/example2/expected_result.yaml
+ )
+ let fleet_repos_base: path = (mktemp -t -d)
+
+ let environment: record = {
+ FLEET_REPOS_BASE: $fleet_repos_base
+ CATALOG_REPOS_BASE: ($env.pwd | path join artifacts)
+ APPNAME: myapp02
+ APPNAMESPACE: app-namespace
+ PROFILE_TYPE: apps
+ PROFILE_NAME: mycluster02
+ secret-values-for-postgres-operator-myapp02: {
+ POSTGRES_OPERATOR_HOST: postgres-operator-host
+ POSTGRES_OPERATOR_PORT: 5432
+ POSTGRES_OPERATOR_USER: postgres-operator-user
+ POSTGRES_OPERATOR_PASSWORD: postgres-operator-password
+ }
+ secret-values-for-postgres-operator-ui-myapp01: {
+ POSTGRES_OPERATOR_UI_HOST: postgres-operator-ui-host
+ POSTGRES_OPERATOR_UI_PORT: 8080
+ POSTGRES_OPERATOR_UI_USER: postgres-operator-ui-user
+ POSTGRES_OPERATOR_UI_PASSWORD: postgres-operator-ui-password
+ }
+ }
+
+ # Retrieve instance model
+ let instance_model: record = (open artifacts/sw-catalogs/apps/example2/app-instance-from-model.yaml)
+
+ # Write to folder
+ $instance_model | app create $environment
+
+ # Calculate the actual ResourceLists from the target folders
+ let targets: list<string> = (
+ $instance_model
+ | replace vars $environment
+ | get $.spec.ksus
+ | each {|k|
+ $k
+ | get "target"
+ | location to absolute path
+ }
+ )
+ let actual: list<record> = (
+ $targets
+ | each {|t|
+ {} | convert folder to resourcelist $t
+ }
+ )
+
+ # Overwrites the encrypted part of the secrets in both
+ let actual_trimmed: list<record> = (
+ $actual
+ # For each KSU's ResourceList
+ | each {|k|
+ $k
+ # Delete sops key from all secrets
+ | ( patch resource reject key $.sops '' Secret )
+ # Replace encrypted value by empty string
+ | ( patch resource update key $.data."values.yaml" 'ENCRYPTED' '' Secret )
+ }
+ )
+ let expected_trimmed: list<record> = (
+ $expected
+ # For each KSU's ResourceList
+ | each {|k|
+ $k
+ # Delete sops key from all secrets
+ # | ( patch resource reject key $.sops '' Secret )
+ # Replace encrypted value by empty string
+ # | ( patch resource update key $.data."values.yaml" 'ENCRYPTED' '' Secret )
+ }
+ )
+
+ # Ensures that the items from both ResourceLists are sorted in the same order and removes irrelevant indexes and keys from `kpt`
+ let actual_fixed: list<record> = ($actual_trimmed | get $.items.0 | sort-by $.kind | sort-by $.metadata.name
+ | each {|k|
+ $k
+ | reject -i $.metadata.annotations."config.kubernetes.io/index"
+ | reject -i $.metadata.annotations."internal.config.kubernetes.io/index"
+ | reject -i $.metadata.annotations."internal.config.kubernetes.io/seqindent"
+ })
+ let expected_fixed = ($expected_trimmed | get $.items.0 | sort-by $.kind | sort-by $.metadata.name
+ | each {|k|
+ $k
+ | reject -i $.metadata.annotations."config.kubernetes.io/index"
+ | reject -i $.metadata.annotations."internal.config.kubernetes.io/index"
+ | reject -i $.metadata.annotations."internal.config.kubernetes.io/seqindent"
+ })
+
+ # Checks
+ assert equal $actual_fixed $expected_fixed
+
+ # Cleanup
+ rm -rf $fleet_repos_base
+}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/app-instance-from-model.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/app-instance-from-model.yaml
new file mode 100644
index 0000000..dcd06ac
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/app-instance-from-model.yaml
@@ -0,0 +1,169 @@
+#######################################################################################
+# 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.
+#######################################################################################
+
+apiVersion: osm.softwaredefinition.io/v1alpha1
+# kind: AppBlueprint
+kind: AppInstantiation
+metadata:
+ name: example1-app
+spec:
+ # description: "Example App Blueprint"
+ description: "Example App Instantiation"
+ version: "1.0.0"
+ ksus:
+ - name: main
+ target:
+ # Absolute path to KSU folder: repo-path + base-path + relative-path
+ repo-path:
+ - ${FLEET_REPOS_BASE} # Default: `/repos`
+ - fleet # Repo name.
+ base-path:
+ - ${PROJECT_NAME} # Project name.
+ - ${PROFILE_TYPE}
+ - ${PROFILE_NAME}
+ relative-path: # App folder + ksu folder (in case of multiple KSUs)
+ - ${APPNAME}
+ - main
+ patterns:
+ - name: main-pattern
+ source:
+ # Absolute path to OKA folder: repo-path + base-path + relative-path
+ repo-path:
+ - ${CATALOG_REPOS_BASE} # Default: `/repos`
+ - sw-catalogs # Repo name
+ base-path: # Absolute path to OKA folder
+ - apps # OKA type
+ - example1 # OKA folder
+ relative-path: # Pattern template folder (default: `templates/`)
+ - templates
+ - main-pattern
+ bricks:
+ - name: main-brick
+ type: basic
+ # type: HelmReleaseSet
+ kustomization:
+ name: main-kustomization-${APPNAME}
+ # OPTIONAL:
+ # namespace: flux-system
+ source: # Absolute path to manifests referenced by the Kustomization (brick folder): base-path + relative-path
+ repo-path:
+ - ${CATALOG_REPOS_BASE} # Default: `/repos`
+ - sw-catalogs # Repo name
+ base-path: # Absolute path to OKA folder
+ - apps # OKA type
+ - example1 # OKA folder
+ relative-path:
+ - manifests
+ - main-pattern
+ - main-brick-manifests
+ - name: database-brick
+ type: HelmReleaseSet
+ kustomization:
+ name: database-kustomization-${APPNAME}
+ # OPTIONAL:
+ # namespace: flux-system
+ source:
+ repo-path:
+ - ${CATALOG_REPOS_BASE} # Default: `/repos`
+ - sw-catalogs # Repo name
+ base-path: # Absolute path to OKA folder
+ - apps # OKA type
+ - example1 # OKA folder
+ relative-path:
+ - manifests
+ - main-pattern
+ - database-manifests
+ public-age-key: age18juyaw9kvzgkpqx8kun2n7qtvqva6ajj7ulse435thkgnlfjhf2qj76h64
+ hrset-values:
+ - name: db-operator
+ HelmRelease:
+ name: postgres-operator-${APPNAME}
+ namespace: ${APPNAMESPACE}
+ inline-values:
+ key1: value1
+ key2: value2
+ valuesFrom:
+ configMapKeyRef:
+ name: postgres-operator-cm-${APPNAME}
+ # OPTIONAL:
+ # key: values.yaml
+ secretKeyRef:
+ name: postgres-operator-secret-${APPNAME}
+ # OPTIONAL:
+ # key: values.yaml
+ create-cm: # If the map is empty, the ConfigMap will not be created
+ cm-key1: cm-value1
+ cm-key2: cm-value2
+ create-secret: # If the map is empty, the Secret will not be created
+ env-values-reference: secret-values-for-postgres-operator-${APPNAME}
+ - name: db-op-user-interface
+ HelmRelease:
+ name: postgres-operator-ui-${APPNAME}
+ namespace: ${APPNAMESPACE}
+ inline-values:
+ key1: value1
+ key2: value2
+ valuesFrom:
+ configMapKeyRef:
+ name: postgres-operator-ui-cm-${APPNAME}
+ # OPTIONAL:
+ # key: values.yaml
+ secretKeyRef:
+ name: postgres-operator-ui-secret-${APPNAME}
+ # OPTIONAL:
+ # key: values.yaml
+ create-cm: # If the map is empty, the ConfigMap will not be created
+ cm-key1: cm-value1
+ cm-key2: cm-value2
+ create-secret: # If the map is empty, the Secret will not be created
+ env-values-reference: secret-values-for-postgres-operator-ui-${APPNAME}
+ # parameters:
+ # - name: replicaCount
+ # description: "Number of replicas"
+ # # default: 1
+ # value: 1
+ # - name: ingressHost
+ # description: "Ingress hostname"
+ # value: "ingress for-${APPNAME}"
+ # readinessChecks:
+ # - resource:
+ # kind: Deployment
+ # name: main-deployment-${APPNAME}
+ # condition:
+ # type: Available
+ # status: "True"
+ # - resource:
+ # kind: Service
+ # name: main-service
+ # condition:
+ # type: Ready
+ # TODO:
+ # outputValues:
+ # - name: apiEndpoint
+ # valueFrom:
+ # serviceIP:
+ # name: main-service
+ # - name: adminPassword
+ # valueFrom:
+ # secretKeyRef:
+ # name: app-secrets
+ # key: admin-password
+ # TODO:
+ # dependencies:
+ # - name: nginx-ingress
+ # type: InfraController
+ # version: ">=1.0.0"
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/expected_result.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/expected_result.yaml
new file mode 100644
index 0000000..ae6fa90
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/expected_result.yaml
@@ -0,0 +1,218 @@
+#######################################################################################
+# 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.
+#######################################################################################
+
+- apiVersion: config.kubernetes.io/v1
+ kind: ResourceList
+ items:
+ - apiVersion: kustomize.toolkit.fluxcd.io/v1
+ kind: Kustomization
+ metadata:
+ name: database-kustomization-myapp01
+ namespace: flux-system
+ annotations:
+ config.kubernetes.io/index: '0'
+ config.kubernetes.io/path: ks-database.yaml
+ internal.config.kubernetes.io/index: '0'
+ internal.config.kubernetes.io/path: ks-database.yaml
+ internal.config.kubernetes.io/seqindent: compact
+ spec:
+ interval: 1h0m0s
+ path: apps/example1/manifests/main-pattern/database-manifests
+ prune: true
+ sourceRef:
+ kind: GitRepository
+ name: sw-catalogs
+ namespace: flux-system
+ targetNamespace: app-namespace
+ postBuild:
+ substitute:
+ appname: myapp01
+ appnamespace: app-namespace
+ wait: true
+ patches:
+ - target:
+ kind: HelmRelease
+ name: postgres-operator-myapp01
+ namespace: app-namespace
+ patch: |
+ - op: add
+ path: /spec/values
+ value:
+ key1: value1
+ key2: value2
+ - target:
+ kind: HelmRelease
+ name: postgres-operator-myapp01
+ namespace: app-namespace
+ patch: |
+ - op: add
+ path: /spec/valuesFrom/-
+ value:
+ kind: ConfigMap
+ name: postgres-operator-cm-myapp01
+ key: values.yaml
+ - target:
+ kind: HelmRelease
+ name: postgres-operator-myapp01
+ namespace: app-namespace
+ patch: |
+ - op: add
+ path: /spec/valuesFrom/-
+ value:
+ kind: Secret
+ name: postgres-operator-secret-myapp01
+ key: values.yaml
+ - target:
+ kind: HelmRelease
+ name: postgres-operator-ui-myapp01
+ namespace: app-namespace
+ patch: |
+ - op: add
+ path: /spec/values
+ value:
+ key1: value1
+ key2: value2
+ - target:
+ kind: HelmRelease
+ name: postgres-operator-ui-myapp01
+ namespace: app-namespace
+ patch: |
+ - op: add
+ path: /spec/valuesFrom/-
+ value:
+ kind: ConfigMap
+ name: postgres-operator-ui-cm-myapp01
+ key: values.yaml
+ - target:
+ kind: HelmRelease
+ name: postgres-operator-ui-myapp01
+ namespace: app-namespace
+ patch: |
+ - op: add
+ path: /spec/valuesFrom/-
+ value:
+ kind: Secret
+ name: postgres-operator-ui-secret-myapp01
+ key: values.yaml
+ - apiVersion: kustomize.toolkit.fluxcd.io/v1
+ kind: Kustomization
+ metadata:
+ name: main-kustomization-myapp01
+ namespace: flux-system
+ annotations:
+ config.kubernetes.io/index: '0'
+ config.kubernetes.io/path: ks-main.yaml
+ internal.config.kubernetes.io/index: '0'
+ internal.config.kubernetes.io/path: ks-main.yaml
+ internal.config.kubernetes.io/seqindent: compact
+ spec:
+ interval: 1h0m0s
+ path: apps/example1/manifests/main-pattern/main-brick-manifests
+ prune: true
+ sourceRef:
+ kind: GitRepository
+ name: sw-catalogs
+ namespace: flux-system
+ targetNamespace: app-namespace
+ postBuild:
+ substitute:
+ app_name: myapp01
+ wait: true
+ - apiVersion: v1
+ kind: ConfigMap
+ metadata:
+ name: postgres-operator-cm-myapp01
+ namespace: app-namespace
+ annotations:
+ config.kubernetes.io/path: postgres-operator-cm-myapp01.yaml
+ internal.config.kubernetes.io/path: postgres-operator-cm-myapp01.yaml
+ data:
+ values.yaml: |-
+ cm-key1: cm-value1
+ cm-key2: cm-value2
+ - apiVersion: v1
+ kind: Secret
+ metadata:
+ name: postgres-operator-secret-myapp01
+ namespace: app-namespace
+ annotations:
+ config.kubernetes.io/path: postgres-operator-secret-myapp01.yaml
+ internal.config.kubernetes.io/path: postgres-operator-secret-myapp01.yaml
+ data:
+ values.yaml: ENC[AES256_GCM,data:wzm5y3kpbk+ZVM2TsNd6D4rmEZ7FU5jxM3dZiGILjZ7Hy00oS0ua9nwc0WU+IwSz0S4m7RiETYRnWITELpq0xoogCXRVIz7SkOivWE6/lv+H0SgQOqZ0iDHZgE/kly8S61kh1QGAeCCUZcpUyXwjgZ1S/iwZqAlVl2cizkGvYMMdgJAGBqTrXakM6LH10J5JBZX/05Cy9Y1rSzGDl6XsJwpnj0znyYIAGsq97nsjctzVGUX0TTIvE03de+T5ZB/nxOZrpcsUSbrbHBj4J5/uijOzV4NCjZ6OV7PWl64fKlpVcLt8z5geO9RMVBT/WlhV,iv:bGxvvh4umOmM7iRHiu0i6lWjWeD1yOkhPGSiMGDgkq0=,tag:v0Og8/hjsEuPLB1wmXSWSw==,type:str]
+ sops:
+ kms: []
+ gcp_kms: []
+ azure_kv: []
+ hc_vault: []
+ age:
+ - recipient: age18juyaw9kvzgkpqx8kun2n7qtvqva6ajj7ulse435thkgnlfjhf2qj76h64
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHV3d4R2JzS0VmazdCUXBR
+ citpNE9uVmdGbm8vNWdCK3dQR2NObFRaQlVFCjJJZGU5b25pRC8zQ0NOT1J1eEcr
+ bzBzdHE5ZlpBelJaSnpLVGlhcmp3QUkKLS0tIC9Fam5DcTZHQ0pzK3o5N0o2MVJr
+ c3krQTlvU2FXckxxTUkraDBRMWJwd3cK0sd3dx5Arzh4XxJVUFOi1cPnF9UIsv18
+ VXKeyoIOK8pvPS5c4UvCTfBFdYs69Sg6+6FfbCoFJepaT+VfaM1XPA==
+ -----END AGE ENCRYPTED FILE-----
+ lastmodified: 2025-04-29T18:26:22Z
+ mac: ENC[AES256_GCM,data:io3iCpdT61n7ECEkyM2KhBgp9oZ49I2YBjuUA8HQSLyDDzFrNNeZh0bVOWI3HT89egkZDt8nmj8+A4Bx9sC666iBcqcFx+VuH//Db7SZXYJXFm1qNP+lt+3ic4fesJgYKG5ld+7FtB1ApKccmE7Ri0yrWGYsrMJh2aRK6T3VyzQ=,iv:9lvXUEPwIZxphwEc91QCPPSxkVNIiUAZWQWby502FJA=,tag:0IxYm+VLLKOoZ80FQlLspQ==,type:str]
+ pgp: []
+ encrypted_regex: ^(data|stringData)$
+ version: 3.8.1
+ - apiVersion: v1
+ kind: ConfigMap
+ metadata:
+ name: postgres-operator-ui-cm-myapp01
+ namespace: app-namespace
+ annotations:
+ config.kubernetes.io/path: postgres-operator-ui-cm-myapp01.yaml
+ internal.config.kubernetes.io/path: postgres-operator-ui-cm-myapp01.yaml
+ data:
+ values.yaml: |-
+ cm-key1: cm-value1
+ cm-key2: cm-value2
+ - apiVersion: v1
+ kind: Secret
+ metadata:
+ name: postgres-operator-ui-secret-myapp01
+ namespace: app-namespace
+ annotations:
+ config.kubernetes.io/path: postgres-operator-ui-secret-myapp01.yaml
+ internal.config.kubernetes.io/path: postgres-operator-ui-secret-myapp01.yaml
+ data:
+ values.yaml: ENC[AES256_GCM,data:37zRz8FafCMe/cfZGI9Ojx+DP1Y09C5C4P6E4wkPZJfqb8brliWAzQ14cOHZgeOQBugcuguMLTacGz+QCDvU9u5ZFs9ouXY3pWbs9wa4Ywoaum8T2gK1edzEoQ9U0QG5wqndwti4p1wo8Im6+A/VOpFXcEGXdzSLfxcryTGA5EXBtwG9/SvhTvx6CCZGvWcNtjqjJjwNOOtFyJJFBuc7CrWFgYliSwwZ2q4INJhDTrrQ81zKDzdkW/ac+uapey/XQ3lYK56n7K+Z+hxoBJLYRfsRdtBCXxXpThfYXPu608WDHUcedjitATsk7NwA/F+mqmm1T5FO7ePspSaTZgvhBMi+OP1HyIgKbmiEhg==,iv:dVYZTjkNYAgNpFUvFTIiyEFM51tv45G1yp1NPG/xJKQ=,tag:xcrQZq1swfcjfPjcNgN0JQ==,type:str]
+ sops:
+ kms: []
+ gcp_kms: []
+ azure_kv: []
+ hc_vault: []
+ age:
+ - recipient: age18juyaw9kvzgkpqx8kun2n7qtvqva6ajj7ulse435thkgnlfjhf2qj76h64
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqUS9vaVRVbk9DTkpreVNU
+ Y0t6TW9sbk9Ua1hWSUdVa0hrOWVVSkZIbkVnCnpVcUpxdkQweWw1MjhBVldkaXVX
+ OTRlYW9jQ3ZxNnhiMGExanJYNU9oREkKLS0tIGl3U201ZmNPM2xaY0FyOWZsZGNs
+ aFBQTU1BK3BTSWZ6djFMRDFkbjkrYnMKX/Uo7ePikgbnOfewdHSmCXE4aMfZU0IQ
+ jr9ZlHVGPvaS6yy7fezVZOJI++nwF7fyXZfJW9mm0rSRBy5vnKQYNg==
+ -----END AGE ENCRYPTED FILE-----
+ lastmodified: 2025-04-29T18:26:22Z
+ mac: ENC[AES256_GCM,data:h0ZEFSy0jijGOmjKIFWi5GOfGYgSCWg2BNgMFXT2AwwR2j/xcJWBwFvKPzy7X028Onz0aNmnPa7hf4eI8HS2pp4FEeUbTp8Z+X8it01XDbhmXqnykeHRU40CaKtqaNBbT2uKa/jpT5KgZl+2mnLUL9VU1vlu7BFIQrYPPtOTxgI=,iv:cBSpvEZHFGEAVqWyXUF9OtmuwN4DM3nS3ZgJepUknZ0=,tag:W2utUcIePQpqHkZYV4FiFA==,type:str]
+ pgp: []
+ encrypted_regex: ^(data|stringData)$
+ version: 3.8.1
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/manifests/main-pattern/database-manifests/hrset-database.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/manifests/main-pattern/database-manifests/hrset-database.yaml
new file mode 100644
index 0000000..8623232
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/manifests/main-pattern/database-manifests/hrset-database.yaml
@@ -0,0 +1,58 @@
+#######################################################################################
+# 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.
+#######################################################################################
+
+---
+apiVersion: helm.toolkit.fluxcd.io/v2beta1
+kind: HelmRelease
+metadata:
+ name: postgres-operator-${appname}
+ namespace: ${appnamespace}
+spec:
+ chart:
+ spec:
+ chart: postgres-operator
+ reconcileStrategy: ChartVersion
+ sourceRef:
+ kind: HelmRepository
+ name: postgres-operator-charts-${appname}
+ namespace: ${appnamespace}
+ interval: 3m0s
+ targetNamespace: ${appnamespace}
+ values: {}
+
+---
+
+apiVersion: helm.toolkit.fluxcd.io/v2beta1
+kind: HelmRelease
+metadata:
+ name: postgres-operator-ui-${appname}
+ namespace: ${appnamespace}
+spec:
+ dependsOn:
+ - name: postgres-operator-${appname}
+ namespace: ${appnamespace}
+ chart:
+ spec:
+ chart: postgres-operator-ui
+ reconcileStrategy: ChartVersion
+ sourceRef:
+ kind: HelmRepository
+ name: postgres-operator-charts-${appname}
+ namespace: ${appnamespace}
+ interval: 3m0s
+ targetNamespace: ${appnamespace}
+ values: {}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/manifests/main-pattern/database-manifests/repo-zalando-postgres.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/manifests/main-pattern/database-manifests/repo-zalando-postgres.yaml
new file mode 100644
index 0000000..eaf2dda
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/manifests/main-pattern/database-manifests/repo-zalando-postgres.yaml
@@ -0,0 +1,29 @@
+#######################################################################################
+# 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.
+#######################################################################################
+
+---
+apiVersion: source.toolkit.fluxcd.io/v1beta2
+kind: HelmRepository
+metadata:
+ name: postgres-operator-charts-${appname}
+ namespace: ${appnamespace}
+spec:
+ interval: 10m0s
+ # type: oci
+ # url: oci://registry-1.docker.io/bitnamicharts
+ type: default
+ url: https://opensource.zalando.com/postgres-operator/charts/postgres-operator
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/manifests/main-pattern/main-brick-manifests/configmap.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/manifests/main-pattern/main-brick-manifests/configmap.yaml
new file mode 100644
index 0000000..5bd5c50
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/manifests/main-pattern/main-brick-manifests/configmap.yaml
@@ -0,0 +1,23 @@
+#######################################################################################
+# 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.
+#######################################################################################
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: my-cm-${appname}
+data:
+ my-key: my-value
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/templates/main-pattern/ks-database.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/templates/main-pattern/ks-database.yaml
new file mode 100644
index 0000000..af2831c
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/templates/main-pattern/ks-database.yaml
@@ -0,0 +1,36 @@
+#######################################################################################
+# 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.
+#######################################################################################
+
+---
+apiVersion: kustomize.toolkit.fluxcd.io/v1
+kind: Kustomization
+metadata:
+ name: database-kustomization-${APPNAME}
+ namespace: flux-system
+spec:
+ interval: 1h0m0s
+ path: ./apps/example1/manifests/main-pattern/database-manifests
+ prune: true
+ sourceRef:
+ kind: GitRepository
+ name: sw-catalogs
+ namespace: flux-system
+ targetNamespace: ${APPNAMESPACE}
+ postBuild:
+ substitute:
+ appname: ${APPNAME}
+ appnamespace: ${APPNAMESPACE}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/templates/main-pattern/ks-main.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/templates/main-pattern/ks-main.yaml
new file mode 100644
index 0000000..d80fe76
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/templates/main-pattern/ks-main.yaml
@@ -0,0 +1,35 @@
+#######################################################################################
+# 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.
+#######################################################################################
+
+---
+apiVersion: kustomize.toolkit.fluxcd.io/v1
+kind: Kustomization
+metadata:
+ name: main-kustomization-${APPNAME}
+ namespace: flux-system
+spec:
+ interval: 1h0m0s
+ path: ./apps/example1/manifests/main-pattern/main-brick-manifests
+ prune: true
+ sourceRef:
+ kind: GitRepository
+ name: sw-catalogs
+ namespace: flux-system
+ targetNamespace: ${APPNAMESPACE}
+ postBuild:
+ substitute:
+ app_name: ${APPNAME}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/app-instance-from-model.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/app-instance-from-model.yaml
new file mode 100644
index 0000000..fe248e4
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/app-instance-from-model.yaml
@@ -0,0 +1,270 @@
+#######################################################################################
+# 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.
+#######################################################################################
+
+apiVersion: osm.softwaredefinition.io/v1alpha1
+# kind: AppBlueprint
+kind: AppInstantiation
+metadata:
+ name: example2-app
+spec:
+ # description: "Example App Blueprint"
+ description: "Example App Instantiation"
+ version: "1.0.0"
+ ksus:
+ - name: main
+ target:
+ # Absolute path to KSU folder: repo-path + base-path + relative-path
+ repo-path:
+ - ${FLEET_REPOS_BASE} # Default: `/repos`
+ - fleet # Repo name.
+ base-path:
+ - ${PROJECT_NAME} # Project name.
+ - ${PROFILE_TYPE}
+ - ${PROFILE_NAME}
+ relative-path: # App folder + ksu folder (in case of multiple KSUs)
+ - ${APPNAME}
+ - main
+ patterns:
+ - name: active-pattern
+ source:
+ # Absolute path to OKA folder: repo-path + base-path + relative-path
+ repo-path:
+ - ${CATALOG_REPOS_BASE} # Default: `/repos`
+ - sw-catalogs # Repo name
+ base-path: # Absolute path to OKA folder
+ - apps # OKA type
+ - example2 # OKA folder
+ relative-path: # Pattern template folder (default: `templates/`)
+ - templates
+ - main-pattern
+ # Pattern-specific parameters, to be added as needed (optional)
+ parameters:
+ EXAMPLE_PARAMETER1: active-example-value1
+ EXAMPLE_PARAMETER2: active-example-value2
+ bricks:
+ - name: main-brick
+ type: basic
+ # type: HelmReleaseSet
+ kustomization:
+ name: ks-${APPNAME}-${PATTERN_NAME}
+ # OPTIONAL:
+ # namespace: flux-system
+ source: # Absolute path to manifests referenced by the Kustomization (brick folder): base-path + relative-path
+ repo-path:
+ - ${CATALOG_REPOS_BASE} # Default: `/repos`
+ - sw-catalogs # Repo name
+ base-path: # Absolute path to OKA folder
+ - apps # OKA type
+ - example2 # OKA folder
+ relative-path:
+ - manifests
+ - main-pattern
+ - main-brick-manifests
+ - name: database-brick
+ type: HelmReleaseSet
+ kustomization:
+ name: database-kustomization-${APPNAME}-${PATTERN_NAME}
+ # OPTIONAL:
+ # namespace: flux-system
+ source:
+ repo-path:
+ - ${CATALOG_REPOS_BASE} # Default: `/repos`
+ - sw-catalogs # Repo name
+ base-path: # Absolute path to OKA folder
+ - apps # OKA type
+ - example2 # OKA folder
+ relative-path:
+ - manifests
+ - main-pattern
+ - database-manifests
+ # To be inserted
+ public-age-key: age18juyaw9kvzgkpqx8kun2n7qtvqva6ajj7ulse435thkgnlfjhf2qj76h64
+ hrset-values:
+ - name: db-operator
+ HelmRelease:
+ name: postgres-operator-${APPNAME}-${PATTERN_NAME}
+ namespace: ${APPNAMESPACE}
+ inline-values:
+ key1: value1
+ key2: value2
+ valuesFrom:
+ configMapKeyRef:
+ name: postgres-operator-cm-${APPNAME}-${PATTERN_NAME}
+ # OPTIONAL:
+ # key: values.yaml
+ secretKeyRef:
+ name: postgres-operator-secret-${APPNAME}-${PATTERN_NAME}
+ # OPTIONAL:
+ # key: values.yaml
+ create-cm: # If the map is empty, the ConfigMap will not be created
+ cm-key1: cm-value1
+ cm-key2: cm-value2
+ create-secret: # If the map is empty, the Secret will not be created
+ env-values-reference: secret-values-for-postgres-operator-${APPNAME}
+ - name: db-op-user-interface
+ HelmRelease:
+ name: postgres-operator-ui-${APPNAME}-${PATTERN_NAME}
+ namespace: ${APPNAMESPACE}
+ inline-values:
+ key1: value1
+ key2: value2
+ valuesFrom:
+ configMapKeyRef:
+ name: postgres-operator-ui-cm-${APPNAME}-${PATTERN_NAME}
+ # OPTIONAL:
+ # key: values.yaml
+ secretKeyRef:
+ name: postgres-operator-ui-secret-${APPNAME}-${PATTERN_NAME}
+ # OPTIONAL:
+ # key: values.yaml
+ create-cm: # If the map is empty, the ConfigMap will not be created
+ cm-key1: cm-value1
+ cm-key2: cm-value2
+ create-secret: # If the map is empty, the Secret will not be created
+ env-values-reference: secret-values-for-postgres-operator-ui-${APPNAME}
+ - name: standby-pattern
+ source:
+ # Absolute path to OKA folder: repo-path + base-path + relative-path
+ repo-path:
+ - ${CATALOG_REPOS_BASE} # Default: `/repos`
+ - sw-catalogs # Repo name
+ base-path: # Absolute path to OKA folder
+ - apps # OKA type
+ - example2 # OKA folder
+ relative-path: # Pattern template folder (default: `templates/`)
+ - templates
+ - main-pattern
+ # Pattern-specific parameters, to be added as needed (optional)
+ parameters:
+ EXAMPLE_PARAMETER1: standby-example-value1
+ EXAMPLE_PARAMETER2: standby-example-value2
+ bricks:
+ - name: main-brick
+ type: basic
+ # type: HelmReleaseSet
+ kustomization:
+ name: ks-${APPNAME}-${PATTERN_NAME}
+ # OPTIONAL:
+ # namespace: flux-system
+ source: # Absolute path to manifests referenced by the Kustomization (brick folder): base-path + relative-path
+ repo-path:
+ - ${CATALOG_REPOS_BASE} # Default: `/repos`
+ - sw-catalogs # Repo name
+ base-path: # Absolute path to OKA folder
+ - apps # OKA type
+ - example2 # OKA folder
+ relative-path:
+ - manifests
+ - main-pattern
+ - main-brick-manifests
+ - name: database-brick
+ type: HelmReleaseSet
+ kustomization:
+ name: database-kustomization-${APPNAME}-${PATTERN_NAME}
+ # OPTIONAL:
+ # namespace: flux-system
+ source:
+ repo-path:
+ - ${CATALOG_REPOS_BASE} # Default: `/repos`
+ - sw-catalogs # Repo name
+ base-path: # Absolute path to OKA folder
+ - apps # OKA type
+ - example2 # OKA folder
+ relative-path:
+ - manifests
+ - main-pattern
+ - database-manifests
+ public-age-key: age18juyaw9kvzgkpqx8kun2n7qtvqva6ajj7ulse435thkgnlfjhf2qj76h64
+ hrset-values:
+ - name: db-operator
+ HelmRelease:
+ name: postgres-operator-${APPNAME}-${PATTERN_NAME}
+ namespace: ${APPNAMESPACE}
+ inline-values:
+ key1: value1
+ key2: value2
+ valuesFrom:
+ configMapKeyRef:
+ name: postgres-operator-cm-${APPNAME}-${PATTERN_NAME}
+ # OPTIONAL:
+ # key: values.yaml
+ secretKeyRef:
+ name: postgres-operator-secret-${APPNAME}-${PATTERN_NAME}
+ # OPTIONAL:
+ # key: values.yaml
+ create-cm: # If the map is empty, the ConfigMap will not be created
+ cm-key1: cm-value1
+ cm-key2: cm-value2
+ create-secret: # If the map is empty, the Secret will not be created
+ env-values-reference: secret-values-for-postgres-operator-${APPNAME}
+ - name: db-op-user-interface
+ HelmRelease:
+ name: postgres-operator-ui-${APPNAME}-${PATTERN_NAME}
+ namespace: ${APPNAMESPACE}
+ inline-values:
+ key1: value1
+ key2: value2
+ valuesFrom:
+ configMapKeyRef:
+ name: postgres-operator-ui-cm-${APPNAME}-${PATTERN_NAME}
+ # OPTIONAL:
+ # key: values.yaml
+ secretKeyRef:
+ name: postgres-operator-ui-secret-${APPNAME}-${PATTERN_NAME}
+ # OPTIONAL:
+ # key: values.yaml
+ create-cm: # If the map is empty, the ConfigMap will not be created
+ cm-key1: cm-value1
+ cm-key2: cm-value2
+ create-secret: # If the map is empty, the Secret will not be created
+ env-values-reference: secret-values-for-postgres-operator-ui-${APPNAME}
+ # parameters:
+ # - name: replicaCount
+ # description: "Number of replicas"
+ # # default: 1
+ # value: 1
+ # - name: ingressHost
+ # description: "Ingress hostname"
+ # value: "ingress for-${APPNAME}"
+ # readinessChecks:
+ # - resource:
+ # kind: Deployment
+ # name: main-deployment-${APPNAME}
+ # condition:
+ # type: Available
+ # status: "True"
+ # - resource:
+ # kind: Service
+ # name: main-service
+ # condition:
+ # type: Ready
+ # TODO:
+ # outputValues:
+ # - name: apiEndpoint
+ # valueFrom:
+ # serviceIP:
+ # name: main-service
+ # - name: adminPassword
+ # valueFrom:
+ # secretKeyRef:
+ # name: app-secrets
+ # key: admin-password
+ # TODO:
+ # dependencies:
+ # - name: nginx-ingress
+ # type: InfraController
+ # version: ">=1.0.0"
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/expected_result.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/expected_result.yaml
new file mode 100644
index 0000000..464b258
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/expected_result.yaml
@@ -0,0 +1,320 @@
+#######################################################################################
+# 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.
+#######################################################################################
+
+- apiVersion: config.kubernetes.io/v1
+ kind: ResourceList
+ items:
+ - apiVersion: kustomize.toolkit.fluxcd.io/v1
+ kind: Kustomization
+ metadata:
+ name: database-kustomization-myapp02-active-pattern
+ namespace: flux-system
+ annotations:
+ config.kubernetes.io/index: '0'
+ config.kubernetes.io/path: ks-database.yaml
+ internal.config.kubernetes.io/index: '0'
+ internal.config.kubernetes.io/path: ks-database.yaml
+ internal.config.kubernetes.io/seqindent: compact
+ spec:
+ interval: 1h0m0s
+ path: apps/example2/manifests/main-pattern/database-manifests
+ prune: true
+ sourceRef:
+ kind: GitRepository
+ name: sw-catalogs
+ namespace: flux-system
+ targetNamespace: app-namespace
+ postBuild:
+ substitute:
+ appname: myapp02
+ appnamespace: app-namespace
+ wait: true
+ patches:
+ - target:
+ kind: HelmRelease
+ name: postgres-operator-myapp02-active-pattern
+ namespace: app-namespace
+ patch: |
+ - op: add
+ path: /spec/values
+ value:
+ key1: value1
+ key2: value2
+ - target:
+ kind: HelmRelease
+ name: postgres-operator-myapp02-active-pattern
+ namespace: app-namespace
+ patch: |
+ - op: add
+ path: /spec/valuesFrom/-
+ value:
+ kind: ConfigMap
+ name: postgres-operator-cm-myapp02-active-pattern
+ key: values.yaml
+ - target:
+ kind: HelmRelease
+ name: postgres-operator-myapp02-active-pattern
+ namespace: app-namespace
+ patch: |
+ - op: add
+ path: /spec/valuesFrom/-
+ value:
+ kind: Secret
+ name: postgres-operator-secret-myapp02-active-pattern
+ key: values.yaml
+ - target:
+ kind: HelmRelease
+ name: postgres-operator-ui-myapp02-active-pattern
+ namespace: app-namespace
+ patch: |
+ - op: add
+ path: /spec/values
+ value:
+ key1: value1
+ key2: value2
+ - target:
+ kind: HelmRelease
+ name: postgres-operator-ui-myapp02-active-pattern
+ namespace: app-namespace
+ patch: |
+ - op: add
+ path: /spec/valuesFrom/-
+ value:
+ kind: ConfigMap
+ name: postgres-operator-ui-cm-myapp02-active-pattern
+ key: values.yaml
+ - target:
+ kind: HelmRelease
+ name: postgres-operator-ui-myapp02-active-pattern
+ namespace: app-namespace
+ patch: |
+ - op: add
+ path: /spec/valuesFrom/-
+ value:
+ kind: Secret
+ name: postgres-operator-ui-secret-myapp02-active-pattern
+ key: values.yaml
+ - apiVersion: kustomize.toolkit.fluxcd.io/v1
+ kind: Kustomization
+ metadata:
+ name: ks-myapp02-active-pattern
+ namespace: flux-system
+ annotations:
+ config.kubernetes.io/index: '0'
+ config.kubernetes.io/path: ks-main.yaml
+ internal.config.kubernetes.io/index: '0'
+ internal.config.kubernetes.io/path: ks-main.yaml
+ internal.config.kubernetes.io/seqindent: compact
+ spec:
+ interval: 1h0m0s
+ path: apps/example2/manifests/main-pattern/main-brick-manifests
+ prune: true
+ sourceRef:
+ kind: GitRepository
+ name: sw-catalogs
+ namespace: flux-system
+ targetNamespace: app-namespace
+ postBuild:
+ substitute:
+ app_name: myapp02
+ example_parameter1: active-example-value1
+ example_parameter2: active-example-value2
+ wait: true
+ - apiVersion: v1
+ kind: ConfigMap
+ metadata:
+ name: postgres-operator-cm-myapp02-active-pattern
+ namespace: app-namespace
+ annotations:
+ config.kubernetes.io/path: postgres-operator-cm-myapp02-active-pattern.yaml
+ internal.config.kubernetes.io/path: postgres-operator-cm-myapp02-active-pattern.yaml
+ data:
+ values.yaml: |-
+ cm-key1: cm-value1
+ cm-key2: cm-value2
+ - apiVersion: v1
+ kind: Secret
+ metadata:
+ name: postgres-operator-secret-myapp02-active-pattern
+ namespace: app-namespace
+ annotations:
+ config.kubernetes.io/path: postgres-operator-secret-myapp02-active-pattern.yaml
+ internal.config.kubernetes.io/path: postgres-operator-secret-myapp02-active-pattern.yaml
+ data:
+ values.yaml: ENCRYPTED
+ - apiVersion: v1
+ kind: ConfigMap
+ metadata:
+ name: postgres-operator-ui-cm-myapp02-active-pattern
+ namespace: app-namespace
+ annotations:
+ config.kubernetes.io/path: postgres-operator-ui-cm-myapp02-active-pattern.yaml
+ internal.config.kubernetes.io/path: postgres-operator-ui-cm-myapp02-active-pattern.yaml
+ data:
+ values.yaml: |-
+ cm-key1: cm-value1
+ cm-key2: cm-value2
+ - apiVersion: kustomize.toolkit.fluxcd.io/v1
+ kind: Kustomization
+ metadata:
+ name: database-kustomization-myapp02-standby-pattern
+ namespace: flux-system
+ annotations:
+ config.kubernetes.io/index: '0'
+ config.kubernetes.io/path: ks-database.yaml
+ internal.config.kubernetes.io/index: '0'
+ internal.config.kubernetes.io/path: ks-database.yaml
+ internal.config.kubernetes.io/seqindent: compact
+ spec:
+ interval: 1h0m0s
+ path: apps/example2/manifests/main-pattern/database-manifests
+ prune: true
+ sourceRef:
+ kind: GitRepository
+ name: sw-catalogs
+ namespace: flux-system
+ targetNamespace: app-namespace
+ postBuild:
+ substitute:
+ appname: myapp02
+ appnamespace: app-namespace
+ wait: true
+ patches:
+ - target:
+ kind: HelmRelease
+ name: postgres-operator-myapp02-standby-pattern
+ namespace: app-namespace
+ patch: |
+ - op: add
+ path: /spec/values
+ value:
+ key1: value1
+ key2: value2
+ - target:
+ kind: HelmRelease
+ name: postgres-operator-myapp02-standby-pattern
+ namespace: app-namespace
+ patch: |
+ - op: add
+ path: /spec/valuesFrom/-
+ value:
+ kind: ConfigMap
+ name: postgres-operator-cm-myapp02-standby-pattern
+ key: values.yaml
+ - target:
+ kind: HelmRelease
+ name: postgres-operator-myapp02-standby-pattern
+ namespace: app-namespace
+ patch: |
+ - op: add
+ path: /spec/valuesFrom/-
+ value:
+ kind: Secret
+ name: postgres-operator-secret-myapp02-standby-pattern
+ key: values.yaml
+ - target:
+ kind: HelmRelease
+ name: postgres-operator-ui-myapp02-standby-pattern
+ namespace: app-namespace
+ patch: |
+ - op: add
+ path: /spec/values
+ value:
+ key1: value1
+ key2: value2
+ - target:
+ kind: HelmRelease
+ name: postgres-operator-ui-myapp02-standby-pattern
+ namespace: app-namespace
+ patch: |
+ - op: add
+ path: /spec/valuesFrom/-
+ value:
+ kind: ConfigMap
+ name: postgres-operator-ui-cm-myapp02-standby-pattern
+ key: values.yaml
+ - target:
+ kind: HelmRelease
+ name: postgres-operator-ui-myapp02-standby-pattern
+ namespace: app-namespace
+ patch: |
+ - op: add
+ path: /spec/valuesFrom/-
+ value:
+ kind: Secret
+ name: postgres-operator-ui-secret-myapp02-standby-pattern
+ key: values.yaml
+ - apiVersion: kustomize.toolkit.fluxcd.io/v1
+ kind: Kustomization
+ metadata:
+ name: ks-myapp02-standby-pattern
+ namespace: flux-system
+ annotations:
+ config.kubernetes.io/index: '0'
+ config.kubernetes.io/path: ks-main.yaml
+ internal.config.kubernetes.io/index: '0'
+ internal.config.kubernetes.io/path: ks-main.yaml
+ internal.config.kubernetes.io/seqindent: compact
+ spec:
+ interval: 1h0m0s
+ path: apps/example2/manifests/main-pattern/main-brick-manifests
+ prune: true
+ sourceRef:
+ kind: GitRepository
+ name: sw-catalogs
+ namespace: flux-system
+ targetNamespace: app-namespace
+ postBuild:
+ substitute:
+ app_name: myapp02
+ example_parameter1: standby-example-value1
+ example_parameter2: standby-example-value2
+ wait: true
+ - apiVersion: v1
+ kind: ConfigMap
+ metadata:
+ name: postgres-operator-cm-myapp02-standby-pattern
+ namespace: app-namespace
+ annotations:
+ config.kubernetes.io/path: postgres-operator-cm-myapp02-standby-pattern.yaml
+ internal.config.kubernetes.io/path: postgres-operator-cm-myapp02-standby-pattern.yaml
+ data:
+ values.yaml: |-
+ cm-key1: cm-value1
+ cm-key2: cm-value2
+ - apiVersion: v1
+ kind: Secret
+ metadata:
+ name: postgres-operator-secret-myapp02-standby-pattern
+ namespace: app-namespace
+ annotations:
+ config.kubernetes.io/path: postgres-operator-secret-myapp02-standby-pattern.yaml
+ internal.config.kubernetes.io/path: postgres-operator-secret-myapp02-standby-pattern.yaml
+ data:
+ values.yaml: ENCRYPTED
+ - apiVersion: v1
+ kind: ConfigMap
+ metadata:
+ name: postgres-operator-ui-cm-myapp02-standby-pattern
+ namespace: app-namespace
+ annotations:
+ config.kubernetes.io/path: postgres-operator-ui-cm-myapp02-standby-pattern.yaml
+ internal.config.kubernetes.io/path: postgres-operator-ui-cm-myapp02-standby-pattern.yaml
+ data:
+ values.yaml: |-
+ cm-key1: cm-value1
+ cm-key2: cm-value2
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/manifests/main-pattern/database-manifests/hrset-database.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/manifests/main-pattern/database-manifests/hrset-database.yaml
new file mode 100644
index 0000000..8623232
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/manifests/main-pattern/database-manifests/hrset-database.yaml
@@ -0,0 +1,58 @@
+#######################################################################################
+# 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.
+#######################################################################################
+
+---
+apiVersion: helm.toolkit.fluxcd.io/v2beta1
+kind: HelmRelease
+metadata:
+ name: postgres-operator-${appname}
+ namespace: ${appnamespace}
+spec:
+ chart:
+ spec:
+ chart: postgres-operator
+ reconcileStrategy: ChartVersion
+ sourceRef:
+ kind: HelmRepository
+ name: postgres-operator-charts-${appname}
+ namespace: ${appnamespace}
+ interval: 3m0s
+ targetNamespace: ${appnamespace}
+ values: {}
+
+---
+
+apiVersion: helm.toolkit.fluxcd.io/v2beta1
+kind: HelmRelease
+metadata:
+ name: postgres-operator-ui-${appname}
+ namespace: ${appnamespace}
+spec:
+ dependsOn:
+ - name: postgres-operator-${appname}
+ namespace: ${appnamespace}
+ chart:
+ spec:
+ chart: postgres-operator-ui
+ reconcileStrategy: ChartVersion
+ sourceRef:
+ kind: HelmRepository
+ name: postgres-operator-charts-${appname}
+ namespace: ${appnamespace}
+ interval: 3m0s
+ targetNamespace: ${appnamespace}
+ values: {}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/manifests/main-pattern/database-manifests/repo-zalando-postgres.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/manifests/main-pattern/database-manifests/repo-zalando-postgres.yaml
new file mode 100644
index 0000000..eaf2dda
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/manifests/main-pattern/database-manifests/repo-zalando-postgres.yaml
@@ -0,0 +1,29 @@
+#######################################################################################
+# 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.
+#######################################################################################
+
+---
+apiVersion: source.toolkit.fluxcd.io/v1beta2
+kind: HelmRepository
+metadata:
+ name: postgres-operator-charts-${appname}
+ namespace: ${appnamespace}
+spec:
+ interval: 10m0s
+ # type: oci
+ # url: oci://registry-1.docker.io/bitnamicharts
+ type: default
+ url: https://opensource.zalando.com/postgres-operator/charts/postgres-operator
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/manifests/main-pattern/main-brick-manifests/configmap.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/manifests/main-pattern/main-brick-manifests/configmap.yaml
new file mode 100644
index 0000000..5bd5c50
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/manifests/main-pattern/main-brick-manifests/configmap.yaml
@@ -0,0 +1,23 @@
+#######################################################################################
+# 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.
+#######################################################################################
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: my-cm-${appname}
+data:
+ my-key: my-value
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/templates/main-pattern/ks-database.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/templates/main-pattern/ks-database.yaml
new file mode 100644
index 0000000..6ece0e0
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/templates/main-pattern/ks-database.yaml
@@ -0,0 +1,36 @@
+#######################################################################################
+# 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.
+#######################################################################################
+
+---
+apiVersion: kustomize.toolkit.fluxcd.io/v1
+kind: Kustomization
+metadata:
+ name: database-kustomization-${APPNAME}-${PATTERN_NAME}
+ namespace: flux-system
+spec:
+ interval: 1h0m0s
+ path: ./apps/example1/manifests/main-pattern/database-manifests
+ prune: true
+ sourceRef:
+ kind: GitRepository
+ name: sw-catalogs
+ namespace: flux-system
+ targetNamespace: ${APPNAMESPACE}
+ postBuild:
+ substitute:
+ appname: ${APPNAME}
+ appnamespace: ${APPNAMESPACE}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/templates/main-pattern/ks-main.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/templates/main-pattern/ks-main.yaml
new file mode 100644
index 0000000..7a39e0f
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/templates/main-pattern/ks-main.yaml
@@ -0,0 +1,37 @@
+#######################################################################################
+# 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.
+#######################################################################################
+
+---
+apiVersion: kustomize.toolkit.fluxcd.io/v1
+kind: Kustomization
+metadata:
+ name: ks-${APPNAME}-${PATTERN_NAME}
+ namespace: flux-system
+spec:
+ interval: 1h0m0s
+ path: ./apps/example1/manifests/main-pattern/main-brick-manifests
+ prune: true
+ sourceRef:
+ kind: GitRepository
+ name: sw-catalogs
+ namespace: flux-system
+ targetNamespace: ${APPNAMESPACE}
+ postBuild:
+ substitute:
+ app_name: ${APPNAME}
+ example_parameter1: ${EXAMPLE_PARAMETER1}
+ example_parameter2: ${EXAMPLE_PARAMETER2}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/brick.nu b/docker/osm-nushell-krm-functions/operations/tests/brick.nu
new file mode 100644
index 0000000..602a24a
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/brick.nu
@@ -0,0 +1,18 @@
+#######################################################################################
+# 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.
diff --git a/docker/osm-nushell-krm-functions/operations/tests/ksu.nu b/docker/osm-nushell-krm-functions/operations/tests/ksu.nu
new file mode 100644
index 0000000..3180566
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/ksu.nu
@@ -0,0 +1,19 @@
+#######################################################################################
+# 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 KSUs and their renderization of the corresponding ResourceList into a given target folder.
+
diff --git a/docker/osm-nushell-krm-functions/operations/tests/location.nu b/docker/osm-nushell-krm-functions/operations/tests/location.nu
new file mode 100644
index 0000000..6ea15b5
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/location.nu
@@ -0,0 +1,232 @@
+#######################################################################################
+# 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 KSUs and their renderization of the corresponding ResourceList into a given target folder.
+
+
+# Imports
+use std assert
+use ../location.nu *
+
+
+### to path components tests ###
+
+export def "test location to path components repo-path with base-path" [] {
+ let input: record = {
+ "repo-path": ["/custom/repo"],
+ "base-path": ["base/dir"], # Now unused in base construction
+ "relative-path": ["file.txt"]
+ }
+ let actual: list<string> = ($input | to path components)
+ let expected: list<string> = [ "/custom/repo" "base/dir" "file.txt" ]
+ assert equal $actual $expected
+}
+
+
+export def "test location to path components repo-path with base-path with strings" [] {
+ let input: record = {
+ "repo-path": "/custom/repo",
+ "base-path": "base/dir", # Now unused in base construction
+ "relative-path": "file.txt"
+ }
+ let actual: list<string> = ($input | to path components)
+ let expected: list<string> = [ "/custom/repo" "base/dir" "file.txt" ]
+ assert equal $actual $expected
+}
+
+
+export def "test location to path components explicit base-path usage" [] {
+ let input: record = {
+ "repo-name": "my_repo",
+ "base-path": "explicit/base",
+ "relative-path": "data.csv"
+ }
+ let actual: list<string> = ($input | to path components)
+ let expected: list<string> = [ "/repos/my_repo" "explicit/base" "data.csv" ]
+ assert equal $actual $expected
+}
+
+
+export def "test location to path components empty repo-name errors" [] {
+ let input: record = {
+ "repo-name": "",
+ "relative-path": "file.txt"
+ }
+ assert error { $input | to path components }
+}
+
+
+export def "test location to path components partial profile spec errors" [] {
+ let input: record = {
+ "repo-path": "/valid/repo",
+ "profile-type": "dev",
+ "relative-path": "file.txt"
+ }
+ assert error { $input | to path components }
+}
+
+
+export def "test location to path components mixed spec priorities" [] {
+ let input: record = {
+ "repo-path": "/primary/repo",
+ "repo-name": "secondary",
+ "base-path": "explicit_base",
+ "oka-type": "models",
+ "oka-name": "ai",
+ "relative-path": "config.yaml"
+ }
+ let actual: list<string> = ($input | to path components)
+ let expected: list<string> = [ "/primary/repo" "explicit_base" "config.yaml" ]
+ assert equal $actual $expected
+}
+
+
+# Updated existing tests with clearer names
+export def "test location to path components profile-based with normalization" [] {
+ let input: record = {
+ "repo-name": "profile_repo",
+ "profile-type": "PROD",
+ "profile-name": "EU_Cluster",
+ "relative-path": "secrets.env"
+ }
+ let actual: list<string> = ($input | to path components)
+ let expected: list<string> = [ "/repos/profile_repo" "osm_admin/PROD/EU_Cluster" "secrets.env" ]
+ assert equal $actual $expected
+}
+
+
+export def "test location to path components oka-based with normalization" [] {
+ let input: record = {
+ "repo-path": "/repos/oka_repo",
+ "oka-type": "DATA",
+ "oka-name": "Census2025",
+ "relative-path": "demographics.csv"
+ }
+ let actual: list<string> = ($input | to path components)
+ let expected: list<string> = [ "/repos/oka_repo" "DATA/Census2025" "demographics.csv" ]
+ assert equal $actual $expected
+}
+
+
+# TODO:
+
+### to absolute path tests ###
+
+export def "test location to absolute path basic repo-path" [] {
+ let input: record = {
+ "repo-path": ["/main/repo", "sw-catalogs"],
+ "base-path": ["apps", "example1"],
+ "relative-path": ["manifests", "main-pattern", "main-brick-manifests"]
+ }
+ let actual: string = ($input | to absolute path)
+ let expected: string = "/main/repo/sw-catalogs/apps/example1/manifests/main-pattern/main-brick-manifests"
+ assert equal $actual $expected
+}
+
+
+export def "test location to absolute path profile-based with defaults" [] {
+ let input: record = {
+ "repo-name": "fleet",
+ "profile-type": "dev",
+ "profile-name": "TestEnv",
+ "relative-path": ["app_instance01", "main"]
+ }
+ let actual = ($input | to absolute path)
+ let expected = "/repos/fleet/osm_admin/dev/TestEnv/app_instance01/main"
+ assert equal $actual $expected
+}
+
+
+export def "test location to absolute path oka-based with custom defaults" [] {
+ let input: record = {
+ "repo-name": "data_repo",
+ "oka-type": "app", # It should be converted to "apps"
+ "oka-name": "upf",
+ "relative-path": ["2024", "main"]
+ }
+ let actual: string = ($input | to absolute path "geo" "/data")
+ let expected: string = "/data/data_repo/apps/upf/2024/main"
+ assert equal $actual $expected
+}
+
+
+export def "test location to absolute path mixed specifications priority" [] {
+ let input: record = {
+ "repo-name": "fleet",
+ "base-path": ["my_oka"],
+ "relative-path": ["manifests"]
+ }
+ let actual: string = ($input | to absolute path)
+ let expected: string = "/repos/fleet/my_oka/manifests"
+ assert equal $actual $expected
+}
+
+
+export def "test location to absolute path special characters handling" [] {
+ let input: record = {
+ "repo-name": "fleet",
+ "profile-type": "apps", # Should become "app-profiles"
+ "profile-name": "mycluster01",
+ "relative-path": ["configs/prod"]
+ }
+ let actual: string = ($input | to absolute path)
+ let expected: string = "/repos/fleet/osm_admin/app-profiles/mycluster01/configs/prod"
+ assert equal $actual $expected
+}
+
+
+export def "test location to absolute path error missing relative-path" [] {
+ let input: record = {
+ "repo-path": ["/valid/repo"],
+ "base-path": ["valid/base"]
+ }
+ assert error { $input | to absolute path }
+}
+
+
+export def "test location to absolute path nested relative path" [] {
+ let input: record = {
+ "repo-path": ["/repos/core"],
+ "oka-type": "infra-controllers",
+ "oka-name": "predictive",
+ "relative-path": ["mobile", "serverless-web"]
+ }
+ let actual: string = ($input | to absolute path)
+ let expected: string = "/repos/core/infra-controllers/predictive/mobile/serverless-web"
+ assert equal $actual $expected
+}
+
+
+export def "test location to absolute path empty repo-name error" [] {
+ let input: record = {
+ "repo-name": "",
+ "relative-path": ["file.txt"]
+ }
+ assert error { $input | to absolute path }
+}
+
+
+export def "test location to absolute path minimal valid input" [] {
+ let input: record = {
+ "repo-name": "fleet",
+ "base-path": ["apps"],
+ "relative-path": ["test-app"]
+ }
+ let actual: string = ($input | to absolute path)
+ let expected: string = "/repos/fleet/apps/test-app"
+ assert equal $actual $expected
+}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/mod.nu b/docker/osm-nushell-krm-functions/operations/tests/mod.nu
new file mode 100644
index 0000000..21f40d6
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/mod.nu
@@ -0,0 +1,55 @@
+#!/usr/bin/env -S nu --stdin
+#######################################################################################
+# 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.
+#######################################################################################
+
+
+use std assert
+# use ../../krm *
+
+use ./location.nu *
+use ./app.nu *
+# use ./ksu.nu *
+# use ./pattern.nu *
+# use ./brick.nu *
+
+
+# Test launcher
+def main [] {
+ print "Running tests..."
+
+ let test_commands: list<string> = (
+ scope commands
+ | where ($it.type == "custom")
+ and ($it.name | str starts-with "test ")
+ and not ($it.description | str starts-with "ignore")
+ | get name
+ )
+
+ let count_test_commands: int = ($test_commands | length)
+ let test_commands_together: string = (
+ $test_commands
+ | enumerate
+ | each { |test|
+ [$"print '--> [($test.index + 1)/($count_test_commands)] ($test.item)'", $test.item]
+ }
+ | flatten
+ | str join ";"
+ )
+
+ nu --commands $"source `($env.CURRENT_FILE)`; ($test_commands_together)"
+ print $"\n✅ ALL TESTS COMPLETED SUCCESSFULLY"
+}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/pattern.nu b/docker/osm-nushell-krm-functions/operations/tests/pattern.nu
new file mode 100644
index 0000000..68acc80
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/pattern.nu
@@ -0,0 +1,18 @@
+#######################################################################################
+# 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 a Pattern definition, taking into account its corresponding source template and the set of transformations specified for its constituent Bricks.