blob: 91403d10c78ca16b6f416972cc31bf03de9a62a3 [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 utility functions to patch or amend Kubernetes resources (items) enumerated in a ResourceList.
19
20
21# Checks that the ResourceList is an actual ResourceList
22# -- NOT EXPORTED --
23def "check if resourcelist" [
24 name?: string
25]: [
26 record -> nothing
27] {
28
29 $in
30 | if (
31 $in != {}
32 and (
33 ($in | get -i kind) != "ResourceList"
34 or ($in | get -i apiVersion) != "config.kubernetes.io/v1"
35 )
36 ) {
37 if ($name | is-empty) {
38 error make {msg: $"Error: Expected a ResourceList, but received ($in)."}
39 } else {
40 error make {msg: $"Error: Expected a ResourceList, but received ($in) from ($name)."}
41 }
42 }
43}
44
45
46# Keep item in ResourceList
47export def "resource keep" [
48 apiVersion?: string
49 kind?: string,
50 name?: string,
51 namespace?: string,
52]: [
53 record -> record
54] {
55 let in_rl: record = $in
56
57 # If not a valid ResourceList, throws an error; otherwise, continues
58 $in_rl | check if resourcelist
59
60 $in_rl
61 | update items {|items|
62 $items.items | filter {|it| (
63 (($apiVersion | is-empty) or $it.apiVersion == $apiVersion)
64 and (($kind | is-empty) or $it.kind == $kind)
65 and (($name | is-empty) or $it.metadata.name == $name)
66 and (($namespace | is-empty) or $it.metadata.namespace == $namespace)
67 )
68 }
69 }
70}
71
72
73# Delete item in ResourceList
74export def "resource delete" [
75 apiVersion?: string
76 kind?: string,
77 name?: string,
78 namespace?: string,
79]: [
80 record -> record
81] {
82 let in_rl: record = $in
83
84 # If not a valid ResourceList, throws an error; otherwise, continues
85 $in_rl | check if resourcelist
86
87 $in_rl
88 | update items {|items|
89 $items.items | filter {|it| (
90 (not ($name | is-empty) and $it.metadata.name != $name)
91 or (not ($namespace | is-empty) and $it.metadata.namespace != $namespace)
92 or (not ($kind | is-empty) and $it.kind != $kind)
93 or (not ($apiVersion | is-empty) and $it.apiVersion != $apiVersion)
94 )
95 }
96 }
97}
98
99
100# Patch item in ResourceList with a custom closure
101export def "resource custom function" [
102 custom_function: closure, # Custom function to apply to the keypath
103 key_path: cell-path,
104 value: any,
105 apiVersion?: string
106 kind?: string,
107 name?: string,
108 namespace?: string,
109]: [
110 record -> record
111] {
112 let in_rl: record = $in
113
114 # If not a valid ResourceList, throws an error; otherwise, continues
115 $in_rl | check if resourcelist
116
117 $in_rl
118 | update items {|items|
119 $items.items | each {|item|
120 if ((($name | is-empty) or $item.metadata.name == $name) and
121 (($namespace | is-empty) or ($item | get -i metadata.namespace) == $namespace) and
122 (($kind | is-empty) or ($item | get -i kind) == $kind) and
123 (($apiVersion | is-empty) or ($item | get -i apiVersion) == $apiVersion)) {
124 $item | do $custom_function $key_path $value
125 } else {
126 $item
127 }
128 }
129 }
130}
131
132
133# Patch item in ResourceList with an insert. Fails if key already exists.
134export def "resource insert key" [
135 key_path: cell-path,
136 value: any,
137 apiVersion?: string
138 kind?: string,
139 name?: string,
140 namespace?: string,
141]: [
142 record -> record
143] {
144 let in_rl: record = $in
145
146 # If not a valid ResourceList, throws an error; otherwise, continues
147 $in_rl | check if resourcelist
148
149 $in_rl
150 | (resource custom function
151 { |k, v| ($in | insert $k $v) }
152 $key_path
153 $value
154 $apiVersion
155 $kind
156 $name
157 $namespace
158 )
159}
160
161
162# Patch item in ResourceList with an upsert (update if exists, otherwise insert)
163export def "resource upsert key" [
164 key_path: cell-path,
165 value: any,
166 apiVersion?: string
167 kind?: string,
168 name?: string,
169 namespace?: string,
170]: [
171 record -> record
172] {
173 let in_rl: record = $in
174
175 # If not a valid ResourceList, throws an error; otherwise, continues
176 $in_rl | check if resourcelist
177
178 $in_rl
179 | (resource custom function
180 { |k, v| ($in | upsert $k $v) }
181 $key_path
182 $value
183 $apiVersion
184 $kind
185 $name
186 $namespace
187 )
188}
189
190export alias patch_replace = resource upsert key
191
192
193# Patch item in ResourceList with an update. Fails if key does not exist.
194export def "resource update key" [
195 key_path: cell-path,
196 value: any,
197 apiVersion?: string
198 kind?: string,
199 name?: string,
200 namespace?: string,
201]: [
202 record -> record
203] {
204 let in_rl: record = $in
205
206 # If not a valid ResourceList, throws an error; otherwise, continues
207 $in_rl | check if resourcelist
208
209 $in_rl
210 | (resource custom function
211 { |k, v| ($in | update $k $v) }
212 $key_path
213 $value
214 $apiVersion
215 $kind
216 $name
217 $namespace
218 )
219}
220
221
222# Patch item in ResourceList by deleting a key.
223export def "resource reject key" [
224 key_path: cell-path,
225 apiVersion?: string
226 kind?: string,
227 name?: string,
228 namespace?: string,
229]: [
230 record -> record
231] {
232 let in_rl: record = $in
233
234 # If not a valid ResourceList, throws an error; otherwise, continues
235 $in_rl | check if resourcelist
236
237 $in_rl
238 | (resource custom function
239 { |k, v| ($in | reject $k) }
240 $key_path
241 ""
242 $apiVersion
243 $kind
244 $name
245 $namespace
246 )
247}
248
249export alias "resource delete key" = resource reject key
250
251
252# Patch item in ResourceList to add a file name (and, optionally, order in the file) for an eventual conversion to a folder of manifests
253export def "resource filename set" [
254 --index: int, # Number of the index in the file, for multi-resource manifests
255 filename: string,
256 apiVersion?: string
257 kind?: string,
258 name?: string,
259 namespace?: string,
260]: [
261 record -> record
262] {
263 let in_rl: record = $in
264
265 # If not a valid ResourceList, throws an error; otherwise, continues
266 $in_rl | check if resourcelist
267
268 # Adds index in file if specified; otherwise, keeps the input
269 let input_rl: record = if ($index | is-empty) {
270 $in_rl
271 } else {
272 $in_rl
273 | (resource upsert key
274 $.metadata.annotations."config.kubernetes.io/index"
275 ($index | into string)
276 $apiVersion
277 $kind
278 $name
279 $namespace
280 )
281 | (resource upsert key
282 $.metadata.annotations."internal.config.kubernetes.io/index"
283 ($index | into string)
284 $apiVersion
285 $kind
286 $name
287 $namespace
288 )
289 }
290
291 # Finally, adds file name to the items in the ResourceList
292 $input_rl
293 | (resource upsert key
294 $.metadata.annotations."config.kubernetes.io/path"
295 $filename
296 $apiVersion
297 $kind
298 $name
299 $namespace
300 )
301 | (resource upsert key
302 $.metadata.annotations."internal.config.kubernetes.io/path"
303 $filename
304 $apiVersion
305 $kind
306 $name
307 $namespace
308 )
309}
310
311export alias set_filename_to_items = resource filename set
312
313
314# Patch item in ResourceList to append/upsert element to a list at a given key.
315#
316# The expected behaviour should be as follows:
317#
318# 1. If the key already exists, the value should be a list, and the item should be appended to the list.
319# 2. If the key does not exist, the value should be created as a list with the new item.
320# 3. If the key already exists but the value is not a list, it should throw an error.
321#
322export def "list append item" [
323 key_path: cell-path,
324 value: any,
325 apiVersion?: string
326 kind?: string,
327 name?: string,
328 namespace?: string,
329]: [
330 record -> record
331] {
332 let in_rl: record = $in
333
334 # If not a valid ResourceList, throws an error; otherwise, continues
335 $in_rl | check if resourcelist
336
337 # Checks if all the preexisting values at the matching keys are lists; otherwise, throws an error
338 let non_conformant: list<any> = (
339 $in_rl
340 # Only the resources that match the input criteria
341 | (resource keep
342 $apiVersion
343 $kind
344 $name
345 $namespace
346 )
347 # Only keeps the resources in a regular list
348 | get -i items
349 # Removes the resources where the key does not exist
350 | filter { |it| ($it | get -i $key_path) != null }
351 # Keeps only the resources where the key is not a list
352 | filter { |it| not (
353 $it
354 | get -i $key_path
355 | describe
356 | ($in | str starts-with "list") or ($in | str starts-with "table")
357 )
358 }
359 )
360
361 if not ($non_conformant | is-empty) {
362 error make { msg: $"Error: Some matching keys are not lists. Non conformant:\n($non_conformant | to yaml)"}
363 }
364
365 # Actual processing
366 $in_rl
367 | (resource custom function
368 { |k, v| ($in | upsert $k {
369 |row|
370 let existing = ($row | get $k -i)
371 if $existing == null {
372 [$v]
373 } else {
374 $existing | append $value
375 }
376 }
377 )
378 }
379 $key_path
380 $value
381 $apiVersion
382 $kind
383 $name
384 $namespace
385 )
386}
387
388export alias patch_add_to_list = list append item
389export alias "list upsert item" = list append item
390
391
392# Patch item in ResourceList to drop/delete element from a list at a given key with a given value.
393export def "list drop item" [
394 --keep-empty-list,
395 key_path: cell-path,
396 value: any,
397 apiVersion?: string
398 kind?: string,
399 name?: string,
400 namespace?: string,
401]: [
402 record -> record
403] {
404 let in_rl: record = $in
405
406 # If not a valid ResourceList, throws an error; otherwise, continues
407 $in_rl | check if resourcelist
408
409 # Checks if all the preexisting values at the matching keys are lists; otherwise, throws an error
410 let non_conformant: list<any> = (
411 $in_rl
412 # Only the resources that match the input criteria
413 | (resource keep
414 $apiVersion
415 $kind
416 $name
417 $namespace
418 )
419 # Only keeps the resources in a regular list
420 | get -i items
421 # Removes the resources where the key does not exist
422 | filter { |it| ($it | get -i $key_path) != null }
423 # Keeps only the resources where the key is not a list
424 | filter { |it| not ($it | get -i $key_path | describe | str starts-with "list") }
425 )
426
427 if not ($non_conformant | is-empty) {
428 error make { msg: $"Error: Some matching keys are not lists. Non conformant:\n($non_conformant | to yaml)"}
429 }
430
431 # Actual processing
432 $in_rl
433 | (resource custom function
434 { |k, v| ($in
435 | update $k {|row|
436 $row | get $k -i | filter {|value| ($value != $v)}
437 }
438 # Delete the key in case the list at the key is now empty and the flag is disabled
439 | if (not $keep_empty_list) and ($in | get $k -i | is-empty) {
440 $in | reject $k
441 } else { $in }
442 )
443 }
444 $key_path
445 $value
446 $apiVersion
447 $kind
448 $name
449 $namespace
450 )
451}
452
453export alias patch_delete_from_list = list drop item