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/krm/generator.nu b/docker/osm-nushell-krm-functions/krm/generator.nu
new file mode 100644
index 0000000..98d52b5
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/krm/generator.nu
@@ -0,0 +1,252 @@
+#######################################################################################
+# 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 KRM generator functions for various kinds of Kubernetes resources.
+
+
+use ./convert.nu
+use ./concatenate.nu
+use ./patch.nu
+use ./keypair.nu
+
+
+# KRM generator function with input from a ResourceList.
+# Works as generic generator function to insert any resource from a ResourceList, passed as parameter, into another ResourceList, passed from stdin.
+export def "from resourcelist" [
+    rl: record
+]: [
+    record -> record
+] {
+    # Regularizes ResourceList from stdin
+    let list1: record = if $in == null { {} } else { $in }
+
+    # Regularizes the second ResourceList
+    let list2: record = if $rl == null { {} } else { $rl }
+
+    # Checks that both ResourceLists are actual ResourceLists
+    {stdin: $list1, "input parameter": $list2} | items { |name, rl|
+        if (
+            $rl != {}
+            and (
+                ($rl | get -i kind) != "ResourceList"
+                or ($rl | get -i apiVersion) != "config.kubernetes.io/v1"
+            )
+        ) {
+            error make {msg: $"Error: Expected a ResourceList, but received ($rl) from ($name)."}
+        }
+    }
+
+    # Merges both ResourceLists
+    $list1
+    | concatenate resourcelists $list2
+}
+
+
+# KRM generator function with input from a manifest
+#
+# Example of use: Generator from an encrypted secret:
+#
+# use ./keypair.nu
+#
+# let secret_value: string = "my_secret_value"
+# let secret_name: string = "mysecret"
+# let secret_key: string = "mykey"
+# let public_key: string = "age1s236gmpr7myjjyqfrl6hwz0npqjgxa9t6tjj46yq28j2c4nk653saqreav"
+#
+# {}
+# | generator from manifest (
+#     $secret_value
+#     | (^kubectl create secret generic ($secret_name)
+#         --from-file=($secret_key)=/dev/stdin
+#         --dry-run=client
+#         -o yaml)
+#     | keypair encrypt_secret_from_stdin $public_key
+#     | from yaml
+# )
+# | to yaml
+#
+# RESULT:
+#
+# apiVersion: config.kubernetes.io/v1
+# kind: ResourceList
+# items:
+# - apiVersion: v1
+#   data:
+#     mykey: ENC[AES256_GCM,data:XKTW8X5ZI6c3yWYtyOPUP/UskKc=,iv:ZOkqLmSgXNCNCQrsMUq7iDL05rklDBuTaVS6E5Bgyl8=,tag:/2rLYqnh+RJWWH4OmEHJBA==,type:str]
+#   kind: Secret
+#   metadata:
+#     creationTimestamp: null
+#     name: mysecret
+#   sops:
+#     kms: []
+#     gcp_kms: []
+#     azure_kv: []
+#     hc_vault: []
+#     age:
+#     - recipient: age1s236gmpr7myjjyqfrl6hwz0npqjgxa9t6tjj46yq28j2c4nk653saqreav
+#       enc: |
+#         -----BEGIN AGE ENCRYPTED FILE-----
+#         YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByNk9KWnhBa2xiMFpyT1Fj
+#         QWw3aGxRZnhHbUNOcXUvc05zZDZIckdPWWtNCm91VzUwU2l5NnVSajJyQkhBMldK
+#         ZkJYWXFTd1J5Q2Z1cTJ6MExkeVBWVXcKLS0tIHpKQ0EvdmpzNS9nZFFHK0JoV0Rx
+#         NXRyMXROK2p3bkpnOXowQ1RYdFk2blkKzsJiw31EA7hZbcRaHe0RkjsrSs7GQjXc
+#         YNAtoPquu0xaocX3pEUV/aojG/WejNY7peDXVDI43yfv8eJlO072Sw==
+#         -----END AGE ENCRYPTED FILE-----
+#     lastmodified: 2025-03-10T17:47:08Z
+#     mac: ENC[AES256_GCM,data:JZttY7AvtRmVaJpCIdJc4Tve7EykKpR7SETQoR7fSiFOVfm4EX+ZcwYoxQYiMsNWXnx/K/IAo8VKoT1+x/lsyFTFucP3YsZ35cfXtAPt43d+gi+IEYS9hfjDQL4BmLAlIiwmij0QGOzcWFFSDhatD717zIBzEDbs2qNGHTqc68E=,iv:Dtiwbvb7LPTyShw2DrnpM/EAWdLyxSDimh7Kk15Jox4=,tag:1VBGnQbotN5KDSmznvNPdg==,type:str]
+#     pgp: []
+#     encrypted_regex: ^(data|stringData)$
+#     version: 3.8.1
+export def "from manifest" [
+    manifest: any
+]: [
+    record -> record
+    nothing -> record
+] {
+    # Keeps prior ResourceList, with regularization if needed
+    let in_rl: record = if $in == null { {} } else { $in }
+
+    # Regularizes the manifest in the parameter so that is is a list
+    let manifest1: list<any> = (if $manifest == null { [] }
+        else if ($manifest | describe | str starts-with "record") { [ $manifest ] }
+        else if ($manifest | describe | str starts-with "list") or ($manifest | describe | str starts-with "table") { $manifest }
+        else { error make {msg: $"Error: Expected a record or a list of records, but received ($manifest | describe)."}})
+
+    # Creates a ResourceList from the manifest and merges with ResourceList from stdin
+    $in_rl
+    | concatenate resourcelists ($manifest1 | convert manifest to resourcelist)
+}
+
+
+# KRM generator function for a ConfigMap
+export def "configmap" [
+    --filename: string, # File name to keep the manifest
+    --index: int,       # Number of the index in the file, for multi-resource manifests
+    key_pairs: record,  # Key-value pairs to add to the ConfigMap
+    name: string,
+    namespace?: string = "default"
+]: [
+    record -> record
+    nothing -> record
+] {
+    # Regularizes ResourceList from stdin
+    let in_rl: record = if $in == null { {} } else { $in }
+
+    $in_rl
+    | ( from manifest
+        # ConfigMap manifest structure 
+        {
+            apiVersion: v1,
+            kind: ConfigMap,
+            metadata: {
+                name: $name,
+                namespace: $namespace,
+            },
+            data: $key_pairs
+        }
+    )
+    # Add file name if required
+    | if ($filename | is-empty) {
+        $in
+    } else {
+        $in
+        | (patch resource filename set
+            --index $index
+            $filename
+            "v1"
+            "ConfigMap"
+            $name
+            $namespace
+        )
+    }
+}
+
+
+# KRM generator function for a Secret
+export def "secret" [
+    --filename: string, # File name to keep the manifest
+    --index: int,       # Number of the index in the file, for multi-resource manifests
+    --public-age-key: string # Age key to encrypt the contents of the Secret manifest
+    --type: string      # Type of Kubernetes secret. Built-in types: `Opaque` (default), `kubernetes.io/service-account-token`, `kubernetes.io/dockercfg`, `kubernetes.io/dockerconfigjson`, `kubernetes.io/basic-auth`, `kubernetes.io/ssh-auth`, `kubernetes.io/tls`, `bootstrap.kubernetes.io/token`
+    key_pairs: record,  # Key-value pairs to add to the Secret
+    name: string,
+    namespace?: string = "default"
+]: [
+    record -> record
+    nothing -> record
+] {
+    # Regularizes ResourceList from stdin
+    let in_rl: record = if $in == null { {} } else { $in }
+
+    # Encode the values with base64
+    let encoded_key_pairs: record = (
+        ($key_pairs | columns)
+        | zip (
+            $key_pairs
+            | values
+            | each {$in | encode base64}
+        )
+        | reduce -f {} {|it, acc| $acc | upsert $it.0 $it.1 }
+    )
+
+    # Generate the secret
+    $in_rl
+    | ( from manifest
+        # ConfigMap manifest structure 
+        (
+            {
+                apiVersion: v1,
+                kind: Secret,
+                metadata: {
+                    name: $name,
+                    namespace: $namespace,
+                },
+                data: $encoded_key_pairs
+            }
+            # Add Secret type if specified
+            | if ($type | is-empty) {
+                $in
+            } else {
+                $in
+                | insert type $type
+            }
+            # Encode if an age key was supplied
+            | if ($public_age_key | is-empty) {
+                $in
+            } else {
+                $in
+                | to yaml
+                | keypair encrypt_secret_from_stdin $public_age_key
+                | from yaml
+            }
+        )
+    )
+    # Add file name if required
+    | if ($filename | is-empty) {
+        $in
+    } else {
+        $in
+        | (patch resource filename set
+            --index $index
+            $filename
+            "v1"
+            "Secret"
+            $name
+            $namespace
+        )
+    }
+}