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/patch.nu b/docker/osm-nushell-krm-functions/krm/patch.nu
new file mode 100644
index 0000000..91403d1
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/krm/patch.nu
@@ -0,0 +1,453 @@
+#######################################################################################
+# 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 utility functions to patch or amend Kubernetes resources (items) enumerated in a ResourceList.
+
+
+# Checks that the ResourceList is an actual ResourceList
+# -- NOT EXPORTED --
+def "check if resourcelist" [
+ name?: string
+]: [
+ record -> nothing
+] {
+
+ $in
+ | if (
+ $in != {}
+ and (
+ ($in | get -i kind) != "ResourceList"
+ or ($in | get -i apiVersion) != "config.kubernetes.io/v1"
+ )
+ ) {
+ if ($name | is-empty) {
+ error make {msg: $"Error: Expected a ResourceList, but received ($in)."}
+ } else {
+ error make {msg: $"Error: Expected a ResourceList, but received ($in) from ($name)."}
+ }
+ }
+}
+
+
+# Keep item in ResourceList
+export def "resource keep" [
+ apiVersion?: string
+ kind?: string,
+ name?: string,
+ namespace?: string,
+]: [
+ record -> record
+] {
+ let in_rl: record = $in
+
+ # If not a valid ResourceList, throws an error; otherwise, continues
+ $in_rl | check if resourcelist
+
+ $in_rl
+ | update items {|items|
+ $items.items | filter {|it| (
+ (($apiVersion | is-empty) or $it.apiVersion == $apiVersion)
+ and (($kind | is-empty) or $it.kind == $kind)
+ and (($name | is-empty) or $it.metadata.name == $name)
+ and (($namespace | is-empty) or $it.metadata.namespace == $namespace)
+ )
+ }
+ }
+}
+
+
+# Delete item in ResourceList
+export def "resource delete" [
+ apiVersion?: string
+ kind?: string,
+ name?: string,
+ namespace?: string,
+]: [
+ record -> record
+] {
+ let in_rl: record = $in
+
+ # If not a valid ResourceList, throws an error; otherwise, continues
+ $in_rl | check if resourcelist
+
+ $in_rl
+ | update items {|items|
+ $items.items | filter {|it| (
+ (not ($name | is-empty) and $it.metadata.name != $name)
+ or (not ($namespace | is-empty) and $it.metadata.namespace != $namespace)
+ or (not ($kind | is-empty) and $it.kind != $kind)
+ or (not ($apiVersion | is-empty) and $it.apiVersion != $apiVersion)
+ )
+ }
+ }
+}
+
+
+# Patch item in ResourceList with a custom closure
+export def "resource custom function" [
+ custom_function: closure, # Custom function to apply to the keypath
+ key_path: cell-path,
+ value: any,
+ apiVersion?: string
+ kind?: string,
+ name?: string,
+ namespace?: string,
+]: [
+ record -> record
+] {
+ let in_rl: record = $in
+
+ # If not a valid ResourceList, throws an error; otherwise, continues
+ $in_rl | check if resourcelist
+
+ $in_rl
+ | update items {|items|
+ $items.items | each {|item|
+ if ((($name | is-empty) or $item.metadata.name == $name) and
+ (($namespace | is-empty) or ($item | get -i metadata.namespace) == $namespace) and
+ (($kind | is-empty) or ($item | get -i kind) == $kind) and
+ (($apiVersion | is-empty) or ($item | get -i apiVersion) == $apiVersion)) {
+ $item | do $custom_function $key_path $value
+ } else {
+ $item
+ }
+ }
+ }
+}
+
+
+# Patch item in ResourceList with an insert. Fails if key already exists.
+export def "resource insert key" [
+ key_path: cell-path,
+ value: any,
+ apiVersion?: string
+ kind?: string,
+ name?: string,
+ namespace?: string,
+]: [
+ record -> record
+] {
+ let in_rl: record = $in
+
+ # If not a valid ResourceList, throws an error; otherwise, continues
+ $in_rl | check if resourcelist
+
+ $in_rl
+ | (resource custom function
+ { |k, v| ($in | insert $k $v) }
+ $key_path
+ $value
+ $apiVersion
+ $kind
+ $name
+ $namespace
+ )
+}
+
+
+# Patch item in ResourceList with an upsert (update if exists, otherwise insert)
+export def "resource upsert key" [
+ key_path: cell-path,
+ value: any,
+ apiVersion?: string
+ kind?: string,
+ name?: string,
+ namespace?: string,
+]: [
+ record -> record
+] {
+ let in_rl: record = $in
+
+ # If not a valid ResourceList, throws an error; otherwise, continues
+ $in_rl | check if resourcelist
+
+ $in_rl
+ | (resource custom function
+ { |k, v| ($in | upsert $k $v) }
+ $key_path
+ $value
+ $apiVersion
+ $kind
+ $name
+ $namespace
+ )
+}
+
+export alias patch_replace = resource upsert key
+
+
+# Patch item in ResourceList with an update. Fails if key does not exist.
+export def "resource update key" [
+ key_path: cell-path,
+ value: any,
+ apiVersion?: string
+ kind?: string,
+ name?: string,
+ namespace?: string,
+]: [
+ record -> record
+] {
+ let in_rl: record = $in
+
+ # If not a valid ResourceList, throws an error; otherwise, continues
+ $in_rl | check if resourcelist
+
+ $in_rl
+ | (resource custom function
+ { |k, v| ($in | update $k $v) }
+ $key_path
+ $value
+ $apiVersion
+ $kind
+ $name
+ $namespace
+ )
+}
+
+
+# Patch item in ResourceList by deleting a key.
+export def "resource reject key" [
+ key_path: cell-path,
+ apiVersion?: string
+ kind?: string,
+ name?: string,
+ namespace?: string,
+]: [
+ record -> record
+] {
+ let in_rl: record = $in
+
+ # If not a valid ResourceList, throws an error; otherwise, continues
+ $in_rl | check if resourcelist
+
+ $in_rl
+ | (resource custom function
+ { |k, v| ($in | reject $k) }
+ $key_path
+ ""
+ $apiVersion
+ $kind
+ $name
+ $namespace
+ )
+}
+
+export alias "resource delete key" = resource reject key
+
+
+# Patch item in ResourceList to add a file name (and, optionally, order in the file) for an eventual conversion to a folder of manifests
+export def "resource filename set" [
+ --index: int, # Number of the index in the file, for multi-resource manifests
+ filename: string,
+ apiVersion?: string
+ kind?: string,
+ name?: string,
+ namespace?: string,
+]: [
+ record -> record
+] {
+ let in_rl: record = $in
+
+ # If not a valid ResourceList, throws an error; otherwise, continues
+ $in_rl | check if resourcelist
+
+ # Adds index in file if specified; otherwise, keeps the input
+ let input_rl: record = if ($index | is-empty) {
+ $in_rl
+ } else {
+ $in_rl
+ | (resource upsert key
+ $.metadata.annotations."config.kubernetes.io/index"
+ ($index | into string)
+ $apiVersion
+ $kind
+ $name
+ $namespace
+ )
+ | (resource upsert key
+ $.metadata.annotations."internal.config.kubernetes.io/index"
+ ($index | into string)
+ $apiVersion
+ $kind
+ $name
+ $namespace
+ )
+ }
+
+ # Finally, adds file name to the items in the ResourceList
+ $input_rl
+ | (resource upsert key
+ $.metadata.annotations."config.kubernetes.io/path"
+ $filename
+ $apiVersion
+ $kind
+ $name
+ $namespace
+ )
+ | (resource upsert key
+ $.metadata.annotations."internal.config.kubernetes.io/path"
+ $filename
+ $apiVersion
+ $kind
+ $name
+ $namespace
+ )
+}
+
+export alias set_filename_to_items = resource filename set
+
+
+# Patch item in ResourceList to append/upsert element to a list at a given key.
+#
+# The expected behaviour should be as follows:
+#
+# 1. If the key already exists, the value should be a list, and the item should be appended to the list.
+# 2. If the key does not exist, the value should be created as a list with the new item.
+# 3. If the key already exists but the value is not a list, it should throw an error.
+#
+export def "list append item" [
+ key_path: cell-path,
+ value: any,
+ apiVersion?: string
+ kind?: string,
+ name?: string,
+ namespace?: string,
+]: [
+ record -> record
+] {
+ let in_rl: record = $in
+
+ # If not a valid ResourceList, throws an error; otherwise, continues
+ $in_rl | check if resourcelist
+
+ # Checks if all the preexisting values at the matching keys are lists; otherwise, throws an error
+ let non_conformant: list<any> = (
+ $in_rl
+ # Only the resources that match the input criteria
+ | (resource keep
+ $apiVersion
+ $kind
+ $name
+ $namespace
+ )
+ # Only keeps the resources in a regular list
+ | get -i items
+ # Removes the resources where the key does not exist
+ | filter { |it| ($it | get -i $key_path) != null }
+ # Keeps only the resources where the key is not a list
+ | filter { |it| not (
+ $it
+ | get -i $key_path
+ | describe
+ | ($in | str starts-with "list") or ($in | str starts-with "table")
+ )
+ }
+ )
+
+ if not ($non_conformant | is-empty) {
+ error make { msg: $"Error: Some matching keys are not lists. Non conformant:\n($non_conformant | to yaml)"}
+ }
+
+ # Actual processing
+ $in_rl
+ | (resource custom function
+ { |k, v| ($in | upsert $k {
+ |row|
+ let existing = ($row | get $k -i)
+ if $existing == null {
+ [$v]
+ } else {
+ $existing | append $value
+ }
+ }
+ )
+ }
+ $key_path
+ $value
+ $apiVersion
+ $kind
+ $name
+ $namespace
+ )
+}
+
+export alias patch_add_to_list = list append item
+export alias "list upsert item" = list append item
+
+
+# Patch item in ResourceList to drop/delete element from a list at a given key with a given value.
+export def "list drop item" [
+ --keep-empty-list,
+ key_path: cell-path,
+ value: any,
+ apiVersion?: string
+ kind?: string,
+ name?: string,
+ namespace?: string,
+]: [
+ record -> record
+] {
+ let in_rl: record = $in
+
+ # If not a valid ResourceList, throws an error; otherwise, continues
+ $in_rl | check if resourcelist
+
+ # Checks if all the preexisting values at the matching keys are lists; otherwise, throws an error
+ let non_conformant: list<any> = (
+ $in_rl
+ # Only the resources that match the input criteria
+ | (resource keep
+ $apiVersion
+ $kind
+ $name
+ $namespace
+ )
+ # Only keeps the resources in a regular list
+ | get -i items
+ # Removes the resources where the key does not exist
+ | filter { |it| ($it | get -i $key_path) != null }
+ # Keeps only the resources where the key is not a list
+ | filter { |it| not ($it | get -i $key_path | describe | str starts-with "list") }
+ )
+
+ if not ($non_conformant | is-empty) {
+ error make { msg: $"Error: Some matching keys are not lists. Non conformant:\n($non_conformant | to yaml)"}
+ }
+
+ # Actual processing
+ $in_rl
+ | (resource custom function
+ { |k, v| ($in
+ | update $k {|row|
+ $row | get $k -i | filter {|value| ($value != $v)}
+ }
+ # Delete the key in case the list at the key is now empty and the flag is disabled
+ | if (not $keep_empty_list) and ($in | get $k -i | is-empty) {
+ $in | reject $k
+ } else { $in }
+ )
+ }
+ $key_path
+ $value
+ $apiVersion
+ $kind
+ $name
+ $namespace
+ )
+}
+
+export alias patch_delete_from_list = list drop item