blob: 98d52b5c04a66480968eee14254a108d96e9d71e [file] [log] [blame]
garciadeblas83775ba2025-07-23 18:35:24 +02001#######################################################################################
2# Copyright ETSI Contributors and Others.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13# implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#######################################################################################
17
18# Module with KRM generator functions for various kinds of Kubernetes resources.
19
20
21use ./convert.nu
22use ./concatenate.nu
23use ./patch.nu
24use ./keypair.nu
25
26
27# KRM generator function with input from a ResourceList.
28# Works as generic generator function to insert any resource from a ResourceList, passed as parameter, into another ResourceList, passed from stdin.
29export def "from resourcelist" [
30 rl: record
31]: [
32 record -> record
33] {
34 # Regularizes ResourceList from stdin
35 let list1: record = if $in == null { {} } else { $in }
36
37 # Regularizes the second ResourceList
38 let list2: record = if $rl == null { {} } else { $rl }
39
40 # Checks that both ResourceLists are actual ResourceLists
41 {stdin: $list1, "input parameter": $list2} | items { |name, rl|
42 if (
43 $rl != {}
44 and (
45 ($rl | get -i kind) != "ResourceList"
46 or ($rl | get -i apiVersion) != "config.kubernetes.io/v1"
47 )
48 ) {
49 error make {msg: $"Error: Expected a ResourceList, but received ($rl) from ($name)."}
50 }
51 }
52
53 # Merges both ResourceLists
54 $list1
55 | concatenate resourcelists $list2
56}
57
58
59# KRM generator function with input from a manifest
60#
61# Example of use: Generator from an encrypted secret:
62#
63# use ./keypair.nu
64#
65# let secret_value: string = "my_secret_value"
66# let secret_name: string = "mysecret"
67# let secret_key: string = "mykey"
68# let public_key: string = "age1s236gmpr7myjjyqfrl6hwz0npqjgxa9t6tjj46yq28j2c4nk653saqreav"
69#
70# {}
71# | generator from manifest (
72# $secret_value
73# | (^kubectl create secret generic ($secret_name)
74# --from-file=($secret_key)=/dev/stdin
75# --dry-run=client
76# -o yaml)
77# | keypair encrypt_secret_from_stdin $public_key
78# | from yaml
79# )
80# | to yaml
81#
82# RESULT:
83#
84# apiVersion: config.kubernetes.io/v1
85# kind: ResourceList
86# items:
87# - apiVersion: v1
88# data:
89# mykey: ENC[AES256_GCM,data:XKTW8X5ZI6c3yWYtyOPUP/UskKc=,iv:ZOkqLmSgXNCNCQrsMUq7iDL05rklDBuTaVS6E5Bgyl8=,tag:/2rLYqnh+RJWWH4OmEHJBA==,type:str]
90# kind: Secret
91# metadata:
92# creationTimestamp: null
93# name: mysecret
94# sops:
95# kms: []
96# gcp_kms: []
97# azure_kv: []
98# hc_vault: []
99# age:
100# - recipient: age1s236gmpr7myjjyqfrl6hwz0npqjgxa9t6tjj46yq28j2c4nk653saqreav
101# enc: |
102# -----BEGIN AGE ENCRYPTED FILE-----
103# YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByNk9KWnhBa2xiMFpyT1Fj
104# QWw3aGxRZnhHbUNOcXUvc05zZDZIckdPWWtNCm91VzUwU2l5NnVSajJyQkhBMldK
105# ZkJYWXFTd1J5Q2Z1cTJ6MExkeVBWVXcKLS0tIHpKQ0EvdmpzNS9nZFFHK0JoV0Rx
106# NXRyMXROK2p3bkpnOXowQ1RYdFk2blkKzsJiw31EA7hZbcRaHe0RkjsrSs7GQjXc
107# YNAtoPquu0xaocX3pEUV/aojG/WejNY7peDXVDI43yfv8eJlO072Sw==
108# -----END AGE ENCRYPTED FILE-----
109# lastmodified: 2025-03-10T17:47:08Z
110# mac: ENC[AES256_GCM,data:JZttY7AvtRmVaJpCIdJc4Tve7EykKpR7SETQoR7fSiFOVfm4EX+ZcwYoxQYiMsNWXnx/K/IAo8VKoT1+x/lsyFTFucP3YsZ35cfXtAPt43d+gi+IEYS9hfjDQL4BmLAlIiwmij0QGOzcWFFSDhatD717zIBzEDbs2qNGHTqc68E=,iv:Dtiwbvb7LPTyShw2DrnpM/EAWdLyxSDimh7Kk15Jox4=,tag:1VBGnQbotN5KDSmznvNPdg==,type:str]
111# pgp: []
112# encrypted_regex: ^(data|stringData)$
113# version: 3.8.1
114export def "from manifest" [
115 manifest: any
116]: [
117 record -> record
118 nothing -> record
119] {
120 # Keeps prior ResourceList, with regularization if needed
121 let in_rl: record = if $in == null { {} } else { $in }
122
123 # Regularizes the manifest in the parameter so that is is a list
124 let manifest1: list<any> = (if $manifest == null { [] }
125 else if ($manifest | describe | str starts-with "record") { [ $manifest ] }
126 else if ($manifest | describe | str starts-with "list") or ($manifest | describe | str starts-with "table") { $manifest }
127 else { error make {msg: $"Error: Expected a record or a list of records, but received ($manifest | describe)."}})
128
129 # Creates a ResourceList from the manifest and merges with ResourceList from stdin
130 $in_rl
131 | concatenate resourcelists ($manifest1 | convert manifest to resourcelist)
132}
133
134
135# KRM generator function for a ConfigMap
136export def "configmap" [
137 --filename: string, # File name to keep the manifest
138 --index: int, # Number of the index in the file, for multi-resource manifests
139 key_pairs: record, # Key-value pairs to add to the ConfigMap
140 name: string,
141 namespace?: string = "default"
142]: [
143 record -> record
144 nothing -> record
145] {
146 # Regularizes ResourceList from stdin
147 let in_rl: record = if $in == null { {} } else { $in }
148
149 $in_rl
150 | ( from manifest
151 # ConfigMap manifest structure
152 {
153 apiVersion: v1,
154 kind: ConfigMap,
155 metadata: {
156 name: $name,
157 namespace: $namespace,
158 },
159 data: $key_pairs
160 }
161 )
162 # Add file name if required
163 | if ($filename | is-empty) {
164 $in
165 } else {
166 $in
167 | (patch resource filename set
168 --index $index
169 $filename
170 "v1"
171 "ConfigMap"
172 $name
173 $namespace
174 )
175 }
176}
177
178
179# KRM generator function for a Secret
180export def "secret" [
181 --filename: string, # File name to keep the manifest
182 --index: int, # Number of the index in the file, for multi-resource manifests
183 --public-age-key: string # Age key to encrypt the contents of the Secret manifest
184 --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`
185 key_pairs: record, # Key-value pairs to add to the Secret
186 name: string,
187 namespace?: string = "default"
188]: [
189 record -> record
190 nothing -> record
191] {
192 # Regularizes ResourceList from stdin
193 let in_rl: record = if $in == null { {} } else { $in }
194
195 # Encode the values with base64
196 let encoded_key_pairs: record = (
197 ($key_pairs | columns)
198 | zip (
199 $key_pairs
200 | values
201 | each {$in | encode base64}
202 )
203 | reduce -f {} {|it, acc| $acc | upsert $it.0 $it.1 }
204 )
205
206 # Generate the secret
207 $in_rl
208 | ( from manifest
209 # ConfigMap manifest structure
210 (
211 {
212 apiVersion: v1,
213 kind: Secret,
214 metadata: {
215 name: $name,
216 namespace: $namespace,
217 },
218 data: $encoded_key_pairs
219 }
220 # Add Secret type if specified
221 | if ($type | is-empty) {
222 $in
223 } else {
224 $in
225 | insert type $type
226 }
227 # Encode if an age key was supplied
228 | if ($public_age_key | is-empty) {
229 $in
230 } else {
231 $in
232 | to yaml
233 | keypair encrypt_secret_from_stdin $public_age_key
234 | from yaml
235 }
236 )
237 )
238 # Add file name if required
239 | if ($filename | is-empty) {
240 $in
241 } else {
242 $in
243 | (patch resource filename set
244 --index $index
245 $filename
246 "v1"
247 "Secret"
248 $name
249 $namespace
250 )
251 }
252}