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/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
+    }
+}