blob: 61f6b6961c769b983fac01d700eece62073aeafa [file] [log] [blame]
garciadeblas70461c52024-07-03 09:17:56 +02001#!/bin/bash
garciadeblas36da51d2024-11-13 14:58:53 +01002#######################################################################################
3# Copyright ETSI Contributors and Others.
garciadeblas70461c52024-07-03 09:17:56 +02004#
garciadeblas36da51d2024-11-13 14:58:53 +01005# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
garciadeblas70461c52024-07-03 09:17:56 +02008#
garciadeblas36da51d2024-11-13 14:58:53 +01009# http://www.apache.org/licenses/LICENSE-2.0
garciadeblas70461c52024-07-03 09:17:56 +020010#
garciadeblas36da51d2024-11-13 14:58:53 +010011# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14# implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#######################################################################################
18
19
garciadeblas70461c52024-07-03 09:17:56 +020020
21function generator_encrypted_secret_cloud_credentials() {
22 local CLOUD_CREDENTIALS_FILENAME="$1"
23 local SECRET_NAME="$2"
24 local PUBLIC_KEY="$3"
25 local SECRET_MANIFEST_FILENAME="${4:-secret-${SECRET_NAME}.yaml}"
26
27 join_lists \
28 <(cat) \
29 <(cat "${CREDENTIALS_DIR}/${CLOUD_CREDENTIALS_FILENAME}" | \
30 kubectl create secret generic ${SECRET_NAME} \
31 --namespace crossplane-system \
32 --from-file creds=/dev/stdin \
33 -o yaml --dry-run=client | \
34 encrypt_secret_from_stdin "${PUBLIC_KEY_MGMT}" | \
35 manifest2list | \
36 set_filename_to_items "${SECRET_MANIFEST_FILENAME}")
37}
38
39
40# Create ProviderConfig for Azure
41function add_providerconfig_for_azure() {
42 # Inputs
43 local CLOUD_CREDENTIALS="$1"
44 local NEW_SECRET_NAME="$2"
45 local PROVIDERCONFIG_NAME="${3:-default}"
46 local PUBLIC_KEY="${4:-${PUBLIC_KEY_MGMT}}"
47 local TARGET_FOLDER="${5:-${MGMT_ADDON_CONFIG_DIR}}"
48
49 # Path to folder with base templates
50 local TEMPLATES="${SW_CATALOGS_REPO_DIR}/infra-configs/crossplane/providers/azure/templates/"
51
52 # Pipeline
53 folder2list \
54 "${TEMPLATES}" | \
55 patch_replace \
56 ".metadata.name" \
57 "${PROVIDERCONFIG_NAME}" \
58 "| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")" | \
59 patch_replace \
60 ".spec.credentials.secretRef.name" \
61 "${NEW_SECRET_NAME}" \
62 "| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")" | \
63 rename_file_in_items \
64 "crossplane-providerconfig-azure.yaml" \
65 "crossplane-providerconfig-azure-${PROVIDERCONFIG_NAME}.yaml" | \
66 generator_encrypted_secret_cloud_credentials \
67 "${CLOUD_CREDENTIALS}" \
68 "${NEW_SECRET_NAME}" \
69 "${PUBLIC_KEY}" | \
70 list2folder_cp_over \
71 "${TARGET_FOLDER}"
72}
73
74
75# Create ProviderConfig for GCP
76function add_providerconfig_for_gcp() {
77 # Inputs
78 local CLOUD_CREDENTIALS="$1"
79 local NEW_SECRET_NAME="$2"
80 local GCP_PROJECT="$3"
81 local PROVIDERCONFIG_NAME="${4:-default}"
82 local PUBLIC_KEY="${5:-${PUBLIC_KEY_MGMT}}"
83 local TARGET_FOLDER="${6:-${MGMT_ADDON_CONFIG_DIR}}"
84
85 # Path to folder with base templates
86 local TEMPLATES="${SW_CATALOGS_REPO_DIR}/infra-configs/crossplane/providers/gcp/templates/"
87
88 # Pipeline
89 folder2list \
90 "${TEMPLATES}" | \
91 patch_replace \
92 ".metadata.name" \
93 "${PROVIDERCONFIG_NAME}" \
94 "| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")" | \
95 patch_replace \
96 ".spec.credentials.secretRef.name" \
97 "${NEW_SECRET_NAME}" \
98 "| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")" | \
99 patch_replace \
100 ".spec.projectID" \
101 "${GCP_PROJECT}" \
102 "| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")" | \
103 rename_file_in_items \
104 "crossplane-providerconfig-gcp.yaml" \
105 "crossplane-providerconfig-gcp-${PROVIDERCONFIG_NAME}.yaml" | \
106 generator_encrypted_secret_cloud_credentials \
107 "${CLOUD_CREDENTIALS}" \
108 "${NEW_SECRET_NAME}" \
109 "${PUBLIC_KEY}" | \
110 list2folder_cp_over \
111 "${TARGET_FOLDER}"
112}
113
114
115# TODO: Deprecated
116# Create AKS cluster (without bootstrap)
117function create_cluster_aks() {
118 local CLUSTER_NAME="$1"
119 local VM_SIZE="$2"
120 local NODE_COUNT="$3"
121 local CLUSTER_LOCATION="$4"
122 local RG_NAME="$5"
123 local K8S_VERSION="${6:-"'1.28'"}"
124 local PROVIDERCONFIG_NAME="${7:-default}"
125 local CLUSTER_KUSTOMIZATION_NAME="${8:$(safe_name ${CLUSTER_NAME})}"
126 local TARGET_FOLDER="${9:-${MGMT_RESOURCES_DIR}}"
127 local MANIFEST_FILENAME="${10:-"${CLUSTER_NAME}.yaml"}"
128 local TEMPLATES="${11:-"${SW_CATALOGS_REPO_DIR}/cloud-resources/aks/templates/"}"
129 local TEMPLATE_MANIFEST_FILENAME="${12:-"aks01.yaml"}"
130
131 export CLUSTER_KUSTOMIZATION_NAME
132 folder2list \
133 "${TEMPLATES}" | \
134 replace_env_vars \
135 '${CLUSTER_KUSTOMIZATION_NAME}' | \
136 patch_replace \
137 ".spec.postBuild.substitute.cluster_name" \
138 "${CLUSTER_NAME}" \
139 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
140 patch_replace \
141 ".spec.postBuild.substitute.cluster_name" \
142 "${CLUSTER_NAME}" \
143 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
144 patch_replace \
145 ".spec.postBuild.substitute.vm_size" \
146 "${VM_SIZE}" \
147 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
148 patch_replace \
149 ".spec.postBuild.substitute.node_count" \
150 "${NODE_COUNT}" \
151 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
152 patch_replace \
153 ".spec.postBuild.substitute.cluster_location" \
154 "${CLUSTER_LOCATION}" \
155 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
156 patch_replace \
157 ".spec.postBuild.substitute.rg_name" \
158 "${RG_NAME}" \
159 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
160 patch_replace \
161 ".spec.postBuild.substitute.k8s_version" \
162 "${K8S_VERSION}" \
163 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
164 patch_replace \
165 ".spec.postBuild.substitute.providerconfig_name" \
166 "${PROVIDERCONFIG_NAME}" \
167 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
168 rename_file_in_items \
169 "${TEMPLATE_MANIFEST_FILENAME}" \
170 "${MANIFEST_FILENAME}" | \
171 prepend_folder_path "${CLUSTER_KUSTOMIZATION_NAME}/" | \
172 list2folder_cp_over \
173 "${TARGET_FOLDER}"
174}
175
176
177# Generator to create a profile folder
178function generator_profile_folder() {
179 local CONFIGMAP_NAME="$1"
180 local PROFILE_PATH="$2"
181 local PROFILE_TYPE="$3"
182 local REPO_URL="${4:-${FLEET_REPO_URL}}"
183 local PROFILE_LOCAL_DIR="${5:-"${PROFILE_PATH}"}"
184
185 join_lists \
186 <(cat) \
187 <(kubectl create configmap $(safe_name "${CONFIGMAP_NAME}") \
188 --namespace flux-system \
189 --from-literal=repo="${REPO_URL}" \
190 --from-literal=path="${PROFILE_PATH}" \
191 -o yaml \
192 --dry-run=client | \
193 manifest2list | \
194 set_label \
195 "osm_profile_type" \
196 "${PROFILE_TYPE}" | \
197 set_filename_to_items "profile-configmap.yaml" | \
198 prepend_folder_path "${PROFILE_LOCAL_DIR}/")
199}
200
201
202# Helper function to return the relative path of a profile
203function path_to_profile() {
204 local PROFILE_NAME="$1"
205 local PROFILE_TYPE="$2"
206 local PROJECT_NAME="${3:-"${MGMT_PROJECT_NAME}"}"
207
208 case "${PROFILE_TYPE,,}" in
209
210 "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers")
211 echo -n "${PROJECT_NAME}/infra-controller-profiles/${PROFILE_NAME}"
212 return 0
213 ;;
214
215 "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs")
216 echo -n "${PROJECT_NAME}/infra-config-profiles/${PROFILE_NAME}"
217 return 0
218 ;;
219
220 "managed" | "resources" | "managed-resources" | "managed_resources")
221 echo -n "${PROJECT_NAME}/managed-resources/${PROFILE_NAME}"
222 return 0
223 ;;
224
225 "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs")
226 echo -n "${PROJECT_NAME}/app-profiles/${PROFILE_NAME}"
227 return 0
228 ;;
229
230 *)
231 echo -n "------------ ERROR ------------"
232 return 1
233 ;;
234 esac
235}
236
237
238# Function to create a new profile
239function create_profile() {
240 local PROFILE_NAME="$1"
241 local PROFILE_TYPE="$2"
242 local PROJECT_NAME="${3:-"${MGMT_PROJECT_NAME}"}"
243 local FLEET_REPO_URL="${4:-"${FLEET_REPO_URL}"}"
244 local FLEET_REPO_DIR="${5:-"${FLEET_REPO_DIR}"}"
245
246 local TARGET_PROFILE_PATH="$(
247 path_to_profile \
248 "${PROFILE_NAME}" \
249 "${PROFILE_TYPE}" \
250 "${PROJECT_NAME}" \
251 )"
252
253 # Generate profile as `ResourceList` and render to target folder.
254 echo "" | \
255 generator_profile_folder \
256 "${PROFILE_NAME}-${PROFILE_TYPE}" \
257 "${TARGET_PROFILE_PATH}" \
258 "${PROFILE_TYPE}" \
259 "${FLEET_REPO_URL}" \
260 "." | \
261 list2folder_cp_over \
262 "${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
263}
264
265
266# Function to delete a profile
267function delete_profile() {
268 local PROFILE_NAME="$1"
269 local PROFILE_TYPE="$2"
270 local PROJECT_NAME="${3:-"${MGMT_PROJECT_NAME}"}"
271 local FLEET_REPO_DIR="${4:-"${FLEET_REPO_DIR}"}"
272
273 local TARGET_PROFILE_PATH="$(
274 path_to_profile \
275 "${PROFILE_NAME}" \
276 "${PROFILE_TYPE}" \
277 "${PROJECT_NAME}" \
278 )"
279
280 # Delete the profile folder
281 rm -rf "${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
282}
283
284
285# ----- BEGIN of Helper functions for remote cluster bootstrap -----
286
287# Generate structure of profile folders prior to bootstrap
288function generator_profile_folders_new_cluster() {
289 # Inputs
290 local PROFILE_NAME="$1"
291 local FLEET_REPO_URL="$2"
292 local PROJECT_NAME="${3:-"${MGMT_PROJECT_NAME}"}"
293 # Optional inputs: Paths for each profile in the Git repo
294 local INFRA_CONTROLLERS_PATH="${4:-"${PROJECT_NAME}/infra-controller-profiles/${PROFILE_NAME}"}"
295 local INFRA_CONFIGS_PATH="${5:-"${PROJECT_NAME}/infra-config-profiles/${PROFILE_NAME}"}"
296 local MANAGED_RESOURCES_PATH="${6:-"${PROJECT_NAME}/managed-resources/${PROFILE_NAME}"}"
297 local APPS_PATH="${7:-"${PROJECT_NAME}/app-profiles/${PROFILE_NAME}"}"
298
299 # Generate profiles as `ResourceList`. merging with inputs
300 join_lists \
301 <(cat) \
302 <(
303 echo "" | \
304 generator_profile_folder \
305 "${PROFILE_NAME}-profile-infra-controllers" \
306 "${INFRA_CONTROLLERS_PATH}" \
307 "infra-controllers" \
308 "${FLEET_REPO_URL}" | \
309 generator_profile_folder \
310 "${PROFILE_NAME}-profile-infra-configs" \
311 "${INFRA_CONFIGS_PATH}" \
312 "infra-configs" \
313 "${FLEET_REPO_URL}" | \
314 generator_profile_folder \
315 "${PROFILE_NAME}-profile-managed-resources" \
316 "${MANAGED_RESOURCES_PATH}" \
317 "managed-resources" \
318 "${FLEET_REPO_URL}" | \
319 generator_profile_folder \
320 "${PROFILE_NAME}-profile-apps" \
321 "${APPS_PATH}" \
322 "apps" \
323 "${FLEET_REPO_URL}"
324 )
325}
326
327
328# Generate base Flux Kustomizations for the new cluster prior to bootstrap
329function generator_base_kustomizations_new_cluster() {
330 local CLUSTER_KUSTOMIZATION_NAME="$1"
331 local FLEET_REPO_URL="$2"
332 local SW_CATALOGS_REPO_URL="$3"
333 local PROJECT_NAME="${4:-"${MGMT_PROJECT_NAME}"}"
334 local SW_CATALOGS_REPO_DIR="${5:-"${SW_CATALOGS_REPO_DIR}"}"
335
336 # Optional inputs:
337 # Paths for each profile in the Git repo
338 local INFRA_CONTROLLERS_PATH="${6:-"${PROJECT_NAME}/infra-controller-profiles/${CLUSTER_KUSTOMIZATION_NAME}"}"
339 local INFRA_CONFIGS_PATH="${7:-"${PROJECT_NAME}/infra-config-profiles/${CLUSTER_KUSTOMIZATION_NAME}"}"
340 local MANAGED_RESOURCES_PATH="${8:-"${PROJECT_NAME}/managed-resources/${CLUSTER_KUSTOMIZATION_NAME}"}"
341 local APPS_PATH="${9:-"${PROJECT_NAME}/app-profiles/${CLUSTER_KUSTOMIZATION_NAME}"}"
342
343 # Path for the source templates
344 local TEMPLATES="${SW_CATALOGS_REPO_DIR}/cloud-resources/flux-remote-bootstrap/cluster-base/templates"
345
346 # Generate
347 export CLUSTER_KUSTOMIZATION_NAME
348 export FLEET_REPO_URL
349 export SW_CATALOGS_REPO_URL
350 export INFRA_CONTROLLERS_PATH
351 export INFRA_CONFIGS_PATH
352 export MANAGED_RESOURCES_PATH
353 export APPS_PATH
354 join_lists \
355 <(cat) \
356 <(
357 folder2list \
358 "${TEMPLATES}" | \
359 replace_env_vars \
360 '${CLUSTER_KUSTOMIZATION_NAME},${FLEET_REPO_URL},${SW_CATALOGS_REPO_URL},${INFRA_CONTROLLERS_PATH},${INFRA_CONFIGS_PATH},${MANAGED_RESOURCES_PATH},${APPS_PATH}'
361 )
362}
363
364
365# Create SOPS configuration file for the root folder of the cluster
366function create_sops_configuration_file_new_cluster() {
367 local PUBLIC_KEY="$1"
368
369 MANIFEST="creation_rules:
370 - encrypted_regex: ^(data|stringData)$
371 age: ${PUBLIC_KEY}
372 # - path_regex: .*.yaml
373 # encrypted_regex: ^(data|stringData)$
374 # age: ${PUBLIC_KEY}"
375
376 # Generate SOPS configuration file for the root folder
377 echo "${MANIFEST}"
378}
379
380
381# Generate K8s secret for management cluster storing secret age key for the new cluster
382function generator_k8s_age_secret_new_cluster() {
383 local PRIVATE_KEY_NEW_CLUSTER="$1"
384 local PUBLIC_KEY_MGMT="$2"
385 local CLUSTER_AGE_SECRET_NAME="${3:-$(safe_name "sops-age-${CLUSTER_KUSTOMIZATION_NAME}")}"
386
387 join_lists \
388 <(cat) \
389 <(
390 echo "${PRIVATE_KEY_NEW_CLUSTER}" | \
391 grep -v '^#' | \
392 kubectl create secret generic "${CLUSTER_AGE_SECRET_NAME}" \
393 --namespace=managed-resources \
394 --from-file=agekey=/dev/stdin \
395 -o yaml --dry-run=client | \
396 encrypt_secret_from_stdin \
397 "${PUBLIC_KEY_MGMT}" |
398 manifest2list | \
399 set_filename_to_items "${CLUSTER_AGE_SECRET_NAME}.yaml"
400 )
401}
402
403
404# Generate bootstrap manifests for new cluster from the management cluster
405function generator_bootstrap_new_cluster() {
406 local CLUSTER_NAME="$1"
407 local CLUSTER_KUSTOMIZATION_NAME="${2:$(safe_name ${CLUSTER_NAME})}"
408 local CLUSTER_AGE_SECRET_NAME="${3:-$(safe_name "sops-age-${CLUSTER_KUSTOMIZATION_NAME}")}"
409 local SW_CATALOGS_REPO_DIR="${4:-"${SW_CATALOGS_REPO_DIR}"}"
410
411 # Paths and names for the templates
412 local MANIFEST_FILENAME="${5:-"cluster-bootstrap-${CLUSTER_KUSTOMIZATION_NAME}.yaml"}"
413 local TEMPLATES="${6:-"${SW_CATALOGS_REPO_DIR}/cloud-resources/flux-remote-bootstrap/bootstrap/templates"}"
414 local TEMPLATE_MANIFEST_FILENAME="${7:-"remote-cluster-bootstrap.yaml"}"
415
416 # Generate manifests
417 export CLUSTER_KUSTOMIZATION_NAME
418 export CLUSTER_NAME
419 export CLUSTER_AGE_SECRET_NAME
420
421 join_lists \
422 <(cat) \
423 <(
424 folder2list \
425 "${TEMPLATES}" | \
426 rename_file_in_items \
427 "${TEMPLATE_MANIFEST_FILENAME}" \
428 "${MANIFEST_FILENAME}" | \
429 replace_env_vars \
430 '${CLUSTER_KUSTOMIZATION_NAME},${CLUSTER_NAME},${CLUSTER_AGE_SECRET_NAME}'
431 )
432}
433
434
435# Auxiliary function to create kustomization manifests
436function manifest_kustomization() {
437 local KS_NAME="$1"
438 local KS_NS="$2"
439 local SOURCE_REPO="$3"
440 local MANIFESTS_PATH="$4"
441 local SOURCE_SYNC_INTERVAL="$5"
442 local HEALTH_CHECK_TO="$6"
443 local DEPENDS_ON="${7:-""}"
444 local OPTIONS="${8:-""}"
445
446 # Calculated inputs
447 local OPTION_FOR_DEPENDS_ON="$(
448 if [[ -z "${DEPENDS_ON}" ]];
449 then
450 echo ""
451 else
452 echo "--depends-on=${DEPENDS_ON}"
453 fi
454 )"
455 local OPTIONS="\
456 "${OPTIONS}" \
457 "${OPTION_FOR_DEPENDS_ON}" \
458 "
459
460 # Create Kustomization manifest
461 flux create kustomization "${KS_NAME}" \
462 --namespace="${KS_NS}" \
463 --source="${SOURCE_REPO}" \
464 --path="${MANIFESTS_PATH}" \
465 --interval="${SOURCE_SYNC_INTERVAL}" \
466 --health-check-timeout="${HEALTH_CHECK_TO}" \
467 ${OPTIONS} --export
468}
469
470
471# Helper function to generate a Kustomization
472function generator_kustomization() {
473 local MANIFEST_FILENAME="$1"
474 local ALL_PARAMS=( "${@}" )
475 local PARAMS=( "${ALL_PARAMS[@]:1}" )
476
477 # Use manifest creator to become a generator
478 make_generator \
479 "${MANIFEST_FILENAME}" \
480 manifest_kustomization \
481 "${PARAMS[@]}"
482}
483
484# ----- END of Helper functions for remote cluster bootstrap -----
485
486
487# Create bootstrap for remote cluster
488function create_bootstrap_for_remote_cluster() {
489 local CLUSTER_NAME="$1"
490 local CLUSTER_KUSTOMIZATION_NAME="$2"
491 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
492 local SW_CATALOGS_REPO_DIR="${4:-"${SW_CATALOGS_REPO_DIR}"}"
493 local FLEET_REPO_URL="${5:-""}"
494 local SW_CATALOGS_REPO_URL="${6:-""}"
495 local MGMT_PROJECT_NAME="${7:-${MGMT_PROJECT_NAME}}"
496 local PUBLIC_KEY_MGMT="${8:-"${PUBLIC_KEY_MGMT}"}"
497 local PUBLIC_KEY_NEW_CLUSTER="$9"
498 local PRIVATE_KEY_NEW_CLUSTER="${10:-${PRIVATE_KEY_NEW_CLUSTER}}"
garciadeblas36da51d2024-11-13 14:58:53 +0100499 local IMPORTED_CLUSTER="${11:-"false"}"
500
garciadeblas70461c52024-07-03 09:17:56 +0200501
502 # Calculates the folder where managed resources area defined
503 local MGMT_RESOURCES_DIR="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/_management"
504
garciadeblas70461c52024-07-03 09:17:56 +0200505 # Create profile folders
506 echo "" | \
507 generator_profile_folders_new_cluster \
508 "${CLUSTER_KUSTOMIZATION_NAME}" \
509 "${FLEET_REPO_URL}" \
510 "${MGMT_PROJECT_NAME}" | \
511 list2folder_cp_over \
512 "${FLEET_REPO_DIR}"
513
514 # Create base Kustomizations for the new cluster
515 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
516 echo "" | \
517 generator_base_kustomizations_new_cluster \
518 "${CLUSTER_KUSTOMIZATION_NAME}" \
519 "${FLEET_REPO_URL}" \
520 "${SW_CATALOGS_REPO_URL}" \
521 "${MGMT_PROJECT_NAME}" \
522 "${SW_CATALOGS_REPO_DIR}" | \
523 list2folder_cp_over \
524 "${CLUSTER_FOLDER}"
525
526 # Add SOPS configuration at the root folder of the cluster
527 # NOTE: This file cannot be generated by pure KRM functions since it begins by a dot ('.')
528 create_sops_configuration_file_new_cluster \
529 "${PUBLIC_KEY_NEW_CLUSTER}" \
530 > "${CLUSTER_FOLDER}/.sops.yaml"
531
532 # Add also the public SOPS key to the repository so that others who clone the repo can encrypt new files
533 # NOTE: This file cannot be generated by pure KRM functions since it begins by a dot ('.')
534 echo "${PUBLIC_KEY_NEW_CLUSTER}" \
535 > "${CLUSTER_FOLDER}/.sops.pub.asc"
536
537 # Prepare everything to perform a Flux bootstrap of the new remote cluster from the management cluster.
538 # Here we also add the `age` private key to the **management cluster** as secret. This one will be used during bootstrap to inject the key into the new cluster
539 local CLUSTER_AGE_SECRET_NAME=$(safe_name "sops-age-${CLUSTER_KUSTOMIZATION_NAME}")
540 echo "" |
541 generator_bootstrap_new_cluster \
542 "${CLUSTER_NAME}" \
543 "${CLUSTER_KUSTOMIZATION_NAME}" \
544 "${CLUSTER_AGE_SECRET_NAME}" \
545 "${SW_CATALOGS_REPO_DIR}" | \
546 generator_k8s_age_secret_new_cluster \
547 "${PRIVATE_KEY_NEW_CLUSTER}" \
548 "${PUBLIC_KEY_MGMT}" \
549 "${CLUSTER_AGE_SECRET_NAME}" | \
550 prepend_folder_path "${CLUSTER_KUSTOMIZATION_NAME}/" | \
551 list2folder_cp_over \
552 "${MGMT_RESOURCES_DIR}"
garciadeblas36da51d2024-11-13 14:58:53 +0100553
554 # If it is an imported cluster, we must create a placeholder Kustomization
555 if [[ "${IMPORTED_CLUSTER,,}" == "true" ]];
556 then
557 TEMPLATES_DIR="${SW_CATALOGS_REPO_DIR}/cloud-resources/flux-remote-bootstrap/empty-kustomization/templates"
558
559 export CLUSTER_KUSTOMIZATION_NAME
560 folder2list \
561 "${TEMPLATES_DIR}" | \
562 replace_env_vars \
563 '${CLUSTER_KUSTOMIZATION_NAME}' | \
564 prepend_folder_path "${CLUSTER_KUSTOMIZATION_NAME}/" | \
565 list2folder_cp_over \
566 "${MGMT_RESOURCES_DIR}"
567 fi
568}
569
570
571# Disconnect Flux of remote cluster
572function disconnect_flux_remote_cluster() {
573 local CLUSTER_KUSTOMIZATION_NAME="$1"
574 local FLEET_REPO_DIR="${2:-"${FLEET_REPO_DIR}"}"
575 local MGMT_PROJECT_NAME="${3:-${MGMT_PROJECT_NAME}}"
576
577
578 # Calculates key folders
579 ## Base folder with Kustomizations for the new cluster
garciadeblasc4afd542025-06-18 17:37:59 +0200580 # local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
garciadeblas36da51d2024-11-13 14:58:53 +0100581 ## Folder where managed resources are defined in the management cluster
582 local MGMT_RESOURCES_CLUSTER_DIR="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/_management/${CLUSTER_KUSTOMIZATION_NAME}"
583
garciadeblasc4afd542025-06-18 17:37:59 +0200584 # Delete Flux resources synchronized directly from remote cluster
585 # rm -rf "${CLUSTER_FOLDER}/flux-system"
garciadeblas36da51d2024-11-13 14:58:53 +0100586
587 # Delete Flux resources bootstraped remotely
588 rm -rf "${MGMT_RESOURCES_CLUSTER_DIR}/cluster-bootstrap-${CLUSTER_KUSTOMIZATION_NAME}.yaml"
garciadeblas70461c52024-07-03 09:17:56 +0200589}
590
591
592# Create remote CrossPlane cluster (generic for any cloud)
593function create_crossplane_cluster() {
594 local CLUSTER_KUSTOMIZATION_NAME="$1"
595 local CLUSTER_NAME="$2"
596 # As of today, one among `aks`, `eks` or `gke`:
597 local CLUSTER_TYPE="$3"
598 local PROVIDERCONFIG_NAME="${4:-default}"
599 local VM_SIZE="$5"
600 local NODE_COUNT="$6"
601 local CLUSTER_LOCATION="$7"
602 local K8S_VERSION="${8:-"'1.28'"}"
603 local PUBLIC_KEY_MGMT="${9:-"${PUBLIC_KEY_MGMT}"}"
604 local PUBLIC_KEY_NEW_CLUSTER="${10}"
605 local PRIVATE_KEY_NEW_CLUSTER="${11:-"${PRIVATE_KEY_NEW_CLUSTER}"}"
606 # AKS only
607 local AKS_RG_NAME="${12:-""}"
608 # GKE only
609 local GKE_PREEMPTIBLE_NODES="${13:-""}"
610 ## `FLEET_REPO_DIR` is the result of:
611 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
612 local FLEET_REPO_DIR="${14:-"${FLEET_REPO_DIR}"}"
613 local FLEET_REPO_URL="${15:-""}"
614 ## `SW_CATALOGS_REPO_DIR` is the result of:
615 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
616 local SW_CATALOGS_REPO_DIR="${16:-"${SW_CATALOGS_REPO_DIR}"}"
617 local SW_CATALOGS_REPO_URL="${17:-""}"
618 # Perform bootstrap unless asked otherwise
619 local SKIP_BOOTSTRAP="${18:"false"}"
620 # Only change if absolutely needeed
621 local MGMT_PROJECT_NAME="${19:-"osm_admin"}"
622 local MGMT_CLUSTER_NAME="${20:-"_management"}"
623 local BASE_TEMPLATES_PATH="${21:-"cloud-resources"}"
624 local TEMPLATE_MANIFEST_FILENAME="${22:-"${CLUSTER_TYPE,,}01.yaml"}"
625 local MANIFEST_FILENAME="${23:-"${CLUSTER_TYPE,,}-${CLUSTER_NAME}.yaml"}"
626
627
628 # Is the provider type supported?
629 local VALID_PROVIDERS=("eks" "aks" "gke")
630 CLUSTER_TYPE="${CLUSTER_TYPE,,}"
631 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${CLUSTER_TYPE}")) ]] && return 1
632
633 # Determines the source dir for the templates and the target folder in Fleet
634 local TEMPLATES_DIR="${SW_CATALOGS_REPO_DIR}/${BASE_TEMPLATES_PATH}/${CLUSTER_TYPE}/templates"
635 local TARGET_FOLDER="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}"
636
637 # Determine which optional steps may be needed
638 local IS_AKS=$([[ "${CLUSTER_TYPE}" == "aks" ]]; echo $?)
639 local IS_GCP=$([[ "${CLUSTER_TYPE}" == "gcp" ]]; echo $?)
640
641 # Pipeline of transformations to create the cluster resource
642 export CLUSTER_KUSTOMIZATION_NAME
643 folder2list \
644 "${TEMPLATES_DIR}" | \
645 replace_env_vars \
646 '${CLUSTER_KUSTOMIZATION_NAME}' | \
647 patch_replace \
648 ".spec.postBuild.substitute.cluster_name" \
649 "${CLUSTER_NAME}" \
650 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
651 patch_replace \
652 ".spec.postBuild.substitute.vm_size" \
653 "${VM_SIZE}" \
654 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
655 patch_replace \
656 ".spec.postBuild.substitute.node_count" \
657 "${NODE_COUNT}" \
658 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
659 patch_replace \
660 ".spec.postBuild.substitute.cluster_location" \
661 "${CLUSTER_LOCATION}" \
662 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
663 patch_replace \
664 ".spec.postBuild.substitute.k8s_version" \
665 "${K8S_VERSION}" \
666 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
667 patch_replace \
668 ".spec.postBuild.substitute.providerconfig_name" \
669 "${PROVIDERCONFIG_NAME}" \
670 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
671 transform_if \
672 "${IS_AKS}" \
673 patch_replace \
674 ".spec.postBuild.substitute.rg_name" \
675 "${AKS_RG_NAME}" \
676 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
677 transform_if \
678 "${IS_GKE}" \
679 patch_replace \
680 ".spec.postBuild.substitute.preemptible_nodes" \
681 "${GKE_PREEMPTIBLE_NODES}" \
682 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
683 rename_file_in_items \
684 "${TEMPLATE_MANIFEST_FILENAME}" \
685 "${MANIFEST_FILENAME}" | \
686 prepend_folder_path "${CLUSTER_KUSTOMIZATION_NAME}/" | \
687 list2folder_cp_over \
688 "${TARGET_FOLDER}"
689
690 # Bootstrap (unless asked to skip)
691 if [[ "${SKIP_BOOTSTRAP,,}" == "true" ]]; then
692 return 0
693 fi
694 create_bootstrap_for_remote_cluster \
695 "${CLUSTER_NAME}" \
696 "${CLUSTER_KUSTOMIZATION_NAME}" \
697 "${FLEET_REPO_DIR}" \
698 "${SW_CATALOGS_REPO_DIR}" \
699 "${FLEET_REPO_URL}" \
700 "${SW_CATALOGS_REPO_URL}" \
701 "${MGMT_PROJECT_NAME}" \
702 "${PUBLIC_KEY_MGMT}" \
703 "${PUBLIC_KEY_NEW_CLUSTER}" \
704 "${PRIVATE_KEY_NEW_CLUSTER}"
705}
706
707
708# Delete remote cluster (generic for any cloud)
709function delete_remote_cluster() {
710 local CLUSTER_KUSTOMIZATION_NAME="$1"
711 local PROJECT_NAME="${2:-"${MGMT_PROJECT_NAME}"}"
712 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
713 local MGMT_RESOURCES_DIR="${4:-"${MGMT_RESOURCES_DIR}"}"
714
715 # Optional inputs: Paths for each profile in the Git repo
716 local INFRA_CONTROLLERS_DIR="${5:-"${FLEET_REPO_DIR}/${PROJECT_NAME}/infra-controller-profiles/${CLUSTER_KUSTOMIZATION_NAME}"}"
717 local INFRA_CONFIGS_DIR="${6:-"${FLEET_REPO_DIR}/${PROJECT_NAME}/infra-config-profiles/${CLUSTER_KUSTOMIZATION_NAME}"}"
718 local MANAGED_RESOURCES_DIR="${7:-"${FLEET_REPO_DIR}/${PROJECT_NAME}/managed-resources/${CLUSTER_KUSTOMIZATION_NAME}"}"
719 local APPS_DIR="${8:-"${FLEET_REPO_DIR}/${PROJECT_NAME}/app-profiles/${CLUSTER_KUSTOMIZATION_NAME}"}"
720 local CLUSTER_DIR="${9:-"${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"}"
721
garciadeblasc4afd542025-06-18 17:37:59 +0200722 # Optional input: Do I need a purge operation first?
723 local PURGE="${10:-"false"}"
724
725
726 # Perform the purge if needed
727 if [[ "${PURGE,,}" == "true" ]]; then
728 echo "Purging the remote Flux instalation..."
729 flux uninstall -s --namespace=flux-system
730 fi
731
732 echo "Deleting cluster profiles and (when applicable) its cloud resources..."
733
garciadeblas70461c52024-07-03 09:17:56 +0200734 # Delete profile folders
735 rm -rf "${INFRA_CONTROLLERS_DIR}"
736 rm -rf "${INFRA_CONFIGS_DIR}"
737 rm -rf "${MANAGED_RESOURCES_DIR}"
738 rm -rf "${APPS_DIR}"
739
740 # Delete base cluster Kustomizations
741 rm -rf "${CLUSTER_DIR}"
742
garciadeblasc4afd542025-06-18 17:37:59 +0200743 # Delete cluster resources if managed by OSM (otherwise, this will be ignored)
garciadeblas70461c52024-07-03 09:17:56 +0200744 rm -rf "${MGMT_RESOURCES_DIR}/${CLUSTER_KUSTOMIZATION_NAME}"
745}
746
747
748# Update remote CrossPlane cluster (generic for any cloud)
749function update_crossplane_cluster() {
750 local CLUSTER_KUSTOMIZATION_NAME="$1"
751 local CLUSTER_NAME="$2"
752 # As of today, one among `aks`, `eks` or `gke`:
753 local CLUSTER_TYPE="$3"
754 local PROVIDERCONFIG_NAME="${4:-default}"
755 local VM_SIZE="$5"
756 local NODE_COUNT="$6"
757 local CLUSTER_LOCATION="$7"
758 local K8S_VERSION="${8:-"'1.28'"}"
759 local PUBLIC_KEY_MGMT="${9:-"${PUBLIC_KEY_MGMT}"}"
760 local PUBLIC_KEY_NEW_CLUSTER="${10}"
761 local PRIVATE_KEY_NEW_CLUSTER="${11:-"${PRIVATE_KEY_NEW_CLUSTER}"}"
762 # AKS only
763 local AKS_RG_NAME="${12:-""}"
764 # GKE only
765 local GKE_PREEMPTIBLE_NODES="${13:-""}"
766 ## `FLEET_REPO_DIR` is the result of:
767 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
768 local FLEET_REPO_DIR="${14:-"${FLEET_REPO_DIR}"}"
769 local FLEET_REPO_URL="${15:-""}"
770 ## `SW_CATALOGS_REPO_DIR` is the result of:
771 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
772 local SW_CATALOGS_REPO_DIR="${16:-"${SW_CATALOGS_REPO_DIR}"}"
773 local SW_CATALOGS_REPO_URL="${17:-""}"
774 # Prevent a new bootstrap by default
775 local SKIP_BOOTSTRAP="${18:"true"}"
776 # Only change if absolutely needeed
777 local MGMT_PROJECT_NAME="${19:-"osm_admin"}"
778 local MGMT_CLUSTER_NAME="${20:-"_management"}"
779 local BASE_TEMPLATES_PATH="${21:-"cloud-resources"}"
780 local TEMPLATE_MANIFEST_FILENAME="${22:-"${CLUSTER_TYPE,,}01.yaml"}"
781 local MANIFEST_FILENAME="${23:-"${CLUSTER_TYPE,,}-${CLUSTER_NAME}.yaml"}"
782
783
784 # Is the provider type supported?
785 local VALID_PROVIDERS=("eks" "aks" "gke")
786 CLUSTER_TYPE="${CLUSTER_TYPE,,}"
787 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${CLUSTER_TYPE}")) ]] && return 1
788
789 # Determine key folders in Fleet
790 local MGMT_RESOURCES_DIR="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}"
791
792 # First, delete cluster's CrossPlane resources
793 # NOTE: We only delete de Kustomization referring to CrossPlane resources,
794 # not the bootstrap resources or the profiles. Thus we avoid that KSUs are
795 # affected or a potential second unnecesary bootstrap.
796 rm -rf "${MGMT_RESOURCES_DIR}/${CLUSTER_KUSTOMIZATION_NAME}/${MANIFEST_FILENAME}"
797
798 # Then, recreate the manifests with updated values
799 create_crossplane_cluster \
800 "${CLUSTER_KUSTOMIZATION_NAME}" \
801 "${CLUSTER_NAME}" \
802 "${CLUSTER_TYPE}" \
803 "${PROVIDERCONFIG_NAME}" \
804 "${VM_SIZE}" \
805 "${NODE_COUNT}" \
806 "${CLUSTER_LOCATION}" \
807 "${K8S_VERSION}" \
808 "${PUBLIC_KEY_MGMT}" \
809 "${PUBLIC_KEY_NEW_CLUSTER}" \
810 "${PRIVATE_KEY_NEW_CLUSTER}" \
811 "${AKS_RG_NAME}" \
812 "${GKE_PREEMPTIBLE_NODES}" \
813 "${FLEET_REPO_DIR}" \
814 "${FLEET_REPO_URL}" \
815 "${SW_CATALOGS_REPO_DIR}" \
816 "${SW_CATALOGS_REPO_URL}" \
817 "${SKIP_BOOTSTRAP}" \
818 "${MGMT_PROJECT_NAME}" \
819 "${MGMT_CLUSTER_NAME}" \
820 "${BASE_TEMPLATES_PATH}" \
821 "${TEMPLATE_MANIFEST_FILENAME}" \
822 "${MANIFEST_FILENAME}"
823}
824
825
826# ----- Helper functions for adding/removing a profile from a cluster -----
827
828# Helper function to find profiles of a given type already used in the cluster
829function profiles_of_type_in_cluster() {
830 local CLUSTER_KUSTOMIZATION_NAME="$1"
831 local RELEVANT_PROFILE_TYPE="$2"
832 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
833
834 # Calculated fields
835 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
836
837 # Processing (echoes the list)
838 folder2list \
839 "${CLUSTER_FOLDER}" | \
840 get_value_from_resourcelist \
841 ".metadata.name" \
842 "| select(.kind == \"Kustomization\")
843 | select(.metadata.labels.osm_profile_type == \"${RELEVANT_PROFILE_TYPE}\")" | \
844 multiline2commalist
845}
846
847
848# Function to list the profiles **this profile depends on**
849function profiles_this_one_depends_on() {
850 local CLUSTER_KUSTOMIZATION_NAME="$1"
851 local PROFILE_TYPE="$2"
852 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
853
854 case "${PROFILE_TYPE,,}" in
855
856 "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers")
857 # Controllers do not depend on any other type of profiles
858 echo ""
859 return 0
860 ;;
861
862 "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs")
863 # Infra configs depend on controllers
864 profiles_of_type_in_cluster \
865 "${CLUSTER_KUSTOMIZATION_NAME}" \
866 "infra-controllers" \
867 "${FLEET_REPO_DIR}"
868 return 0
869 ;;
870
871 "managed" | "resources" | "managed-resources" | "managed_resources")
872 # Managed resources depend on infra configs
873 profiles_of_type_in_cluster \
874 "${CLUSTER_KUSTOMIZATION_NAME}" \
875 "infra-configs" \
876 "${FLEET_REPO_DIR}"
877 return 0
878 ;;
879
880 "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs")
881 # Apps (also) depend on infra configs
882 profiles_of_type_in_cluster \
883 "${CLUSTER_KUSTOMIZATION_NAME}" \
884 "infra-configs" \
885 "${FLEET_REPO_DIR}"
886 return 0
887 ;;
888
889 *)
890 echo -n "------------ ERROR ------------"
891 return 1
892 ;;
893 esac
894}
895
896
897# Function to list the profiles that **depend on this profile**
898function profiles_depend_on_this_one() {
899 local CLUSTER_KUSTOMIZATION_NAME="$1"
900 local PROFILE_TYPE="$2"
901 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
902
903 case "${PROFILE_TYPE,,}" in
904
905 "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers")
906 # Infra configs depend on infra controllers
907 profiles_of_type_in_cluster \
908 "${CLUSTER_KUSTOMIZATION_NAME}" \
909 "infra-configs" \
910 "${FLEET_REPO_DIR}"
911 return 0
912 ;;
913
914 "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs")
915 # Both managed resources and apps depend on configs
916 local PROFILES=(
917 $(
918 profiles_of_type_in_cluster \
919 "${CLUSTER_KUSTOMIZATION_NAME}" \
920 "managed-resources" \
921 "${FLEET_REPO_DIR}"
922 ) \
923 $(
924 profiles_of_type_in_cluster \
925 "${CLUSTER_KUSTOMIZATION_NAME}" \
926 "apps" \
927 "${FLEET_REPO_DIR}"
928 )
929 )
930 printf '%s,' "${PROFILES[@]}" | sed 's/,$//g'
931 return 0
932 ;;
933
934 "managed" | "resources" | "managed-resources" | "managed_resources")
935 # No other profiles depend on managed resources
936 echo ""
937 return 0
938 ;;
939
940 "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs")
941 # No other profiles depend on apps
942 echo ""
943 return 0
944 ;;
945
946 *)
947 echo -n "------------ ERROR ------------"
948 return 1
949 ;;
950 esac
951}
952
953
954# Helper function to add a dependency to a Kustomization only if it does not exist already
955function add_dependency_to_kustomization_safely() {
956 local KUSTOMIZATION_NAME="$1"
957 local KUSTOMIZATION_TO_ADD_AS_DEP="$2"
958
959 local INPUT=$(cat)
960 local FILTER="| select(.kind == \"Kustomization\") | select(.metadata.name == \"${KUSTOMIZATION_NAME}\")"
961
962 # Check if the dependency was added already
963 local TEST_RESULT=$(
964 echo "${INPUT}" | \
965 is_element_on_list \
966 ".spec.dependsOn[].name" \
967 "${KUSTOMIZATION_TO_ADD_AS_DEP}" \
968 "${FILTER}"
969 )
970
971 # If it existed already, returns the stream as is
972 if [[ "${TEST_RESULT}" == "true" ]]
973 then
974 echo "${INPUT}"
975 # Otherwise, processes the stream to add it
976 else
977 echo "${INPUT}" | \
978 patch_add_to_list \
979 ".spec.dependsOn" \
980 "{name: ${KUSTOMIZATION_TO_ADD_AS_DEP}}" \
981 "${FILTER}"
982 fi
983}
984
985
986# Helper function to remove a dependency from a Kustomization
987function remove_dependency_from_kustomization_safely() {
988 local KUSTOMIZATION_NAME="$1"
989 local KUSTOMIZATION_TO_REMOVE_AS_DEP="$2"
990
991 # Calculated inputs
992 local KEY_PATH=".spec.dependsOn[] | select(.name == \"${KUSTOMIZATION_TO_REMOVE_AS_DEP}\")"
993 local FILTER="| select(.kind == \"Kustomization\") | select(.metadata.name == \"${KUSTOMIZATION_NAME}\")"
994
995 # Remove the entry from the dependency list (if it exists)
996 yq "del((.items[]${FILTER})${KEY_PATH})"
997}
998
999
1000# Ensure list of Kustomizations depend on a given Kustomization
1001function add_dependency_to_set_of_kustomizations_safely() {
1002 local KS_NAME="$1"
1003 local THEY_DEPEND_ON_THIS="$2"
1004
1005 local INPUT="$(cat)"
1006 local OUTPUT=""
1007
1008 # For each of the Kustomizations on the comma-separated list, adds `KS_NAME` as one of their dependencies
1009 for KUST in ${THEY_DEPEND_ON_THIS//,/ }
1010 do
1011 local OUTPUT="$(
1012 echo "${INPUT}" | \
1013 add_dependency_to_kustomization_safely \
1014 "${KUST}" \
1015 "${KS_NAME}"
1016 )"
1017 local INPUT="${OUTPUT}"
1018 done
1019
1020 # Return the final `ResultList`, after all iterations
1021 echo "${OUTPUT}"
1022}
1023
1024
1025# Ensure list of Kustomizations no longer depend on a given Kustomization
1026function remove_dependency_from_set_of_kustomizations_safely() {
1027 local KS_NAME="$1"
1028 local THEY_NO_LONGER_DEPEND_ON_THIS="$2"
1029
1030 local INPUT="$(cat)"
1031 local OUTPUT=""
1032
1033 # For each of the Kustomizations on the comma-separated list, removes `KS_NAME` from their dependencies
1034 for KUST in ${THEY_NO_LONGER_DEPEND_ON_THIS//,/ }
1035 do
1036 local OUTPUT="$(
1037 echo "${INPUT}" | \
1038 remove_dependency_from_kustomization_safely \
1039 "${KUST}" \
1040 "${KS_NAME}"
1041 )"
1042 local INPUT="${OUTPUT}"
1043 done
1044
1045 # Return the final `ResultList`, after all iterations
1046 echo "${OUTPUT}"
1047}
1048
1049# ----- END of Helper functions for adding/removing a profile from a cluster -----
1050
1051
1052# Add an existing profile to a cluster
1053function attach_profile_to_cluster() {
1054 local PROFILE_NAME="$1"
1055 local PROFILE_TYPE="$2"
1056 local PROJECT_NAME="$3"
1057 local CLUSTER_KUSTOMIZATION_NAME="$4"
1058 local FLEET_REPO_DIR="${5:-"${FLEET_REPO_DIR}"}"
1059
1060 # Calculated inputs
1061 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
1062 local TARGET_PROFILE_PATH="$(
1063 path_to_profile \
1064 "${PROFILE_NAME}" \
1065 "${PROFILE_TYPE}" \
1066 "${PROJECT_NAME}"
1067 )"
1068
1069 # Finds out which profiles it should depend on... and which profiles should depend on it
1070 local DEPENDS_ON=$(
1071 profiles_this_one_depends_on \
1072 "${CLUSTER_KUSTOMIZATION_NAME}" \
1073 "${PROFILE_TYPE}" \
1074 "${FLEET_REPO_DIR}"
1075 )
1076
1077 local THEY_DEPEND_ON_THIS=$(
1078 profiles_depend_on_this_one \
1079 "${CLUSTER_KUSTOMIZATION_NAME}" \
1080 "${PROFILE_TYPE}" \
1081 "${FLEET_REPO_DIR}"
1082 )
1083
1084 # Parameters for the new Kustomization object to point to the profile
1085 local KS_NAME="$(safe_name "${PROFILE_TYPE}-${PROFILE_NAME}")"
1086 local MANIFEST_FILENAME="${KS_NAME}.yaml"
1087 local KS_NS=flux-system
1088 local MANIFESTS_PATH="${TARGET_PROFILE_PATH}"
1089 local SOURCE_REPO=GitRepository/fleet-repo.flux-system
1090 local SOURCE_SYNC_INTERVAL="60m"
1091 local HEALTH_CHECK_TO="3m"
1092 local RETRY_INTERVAL="1m"
1093 local TIMEOUT="5m"
1094 local OPTIONS="\
1095 --decryption-provider=sops \
1096 --decryption-secret=sops-age \
1097 --prune=true \
1098 --timeout="${TIMEOUT}" \
1099 --retry-interval="${RETRY_INTERVAL}" \
1100 --label osm_profile_type="${PROFILE_TYPE}"
1101 "
1102
1103 # Finally, we update the folder with all the required changes:
1104 # - Update pre-existing Kustomizations that should depend on the new profile (besides others).
1105 # - Create a new Kustomization pointing to the profile.
1106 # - Update Kustomize's `kustomization.yaml` at the root of the cluster folder to take into account the new Kustomization pointing to the profile.
1107 # - Update the cluster folder accordingly.
1108 folder2list \
1109 "${CLUSTER_FOLDER}" |
1110 add_dependency_to_set_of_kustomizations_safely \
1111 "${KS_NAME}" \
1112 "${THEY_DEPEND_ON_THIS}" | \
1113 generator_kustomization \
1114 "${MANIFEST_FILENAME}" \
1115 "${KS_NAME}" \
1116 "${KS_NS}" \
1117 "${SOURCE_REPO}" \
1118 "${MANIFESTS_PATH}" \
1119 "${SOURCE_SYNC_INTERVAL}" \
1120 "${HEALTH_CHECK_TO}" \
1121 "${DEPENDS_ON}" \
1122 "${OPTIONS}" | \
1123 patch_add_to_list \
1124 ".resources" \
1125 "${MANIFEST_FILENAME}" \
1126 "| select(.kind == \"Kustomization\") | select(.apiVersion == \"kustomize.config.k8s.io/v1beta1\") | select(.metadata.annotations.\"config.kubernetes.io/path\" == \"kustomization.yaml\")" | \
1127 list2folder_sync_replace \
1128 "${CLUSTER_FOLDER}"
1129}
1130
1131
1132# Remove an existing profile from a cluster
1133function detach_profile_from_cluster() {
1134 local PROFILE_NAME="$1"
1135 local PROFILE_TYPE="$2"
1136 local PROJECT_NAME="$3"
1137 local CLUSTER_KUSTOMIZATION_NAME="$4"
1138 local FLEET_REPO_DIR="${5:-"${FLEET_REPO_DIR}"}"
1139
1140 # Calculated inputs
1141 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
1142 local TARGET_PROFILE_PATH="$(
1143 path_to_profile \
1144 "${PROFILE_NAME}" \
1145 "${PROFILE_TYPE}" \
1146 "${PROJECT_NAME}"
1147 )"
1148
1149 # Finds out which profiles still depend on it
1150 local THEY_DEPEND_ON_THIS=$(
1151 profiles_depend_on_this_one \
1152 "${CLUSTER_KUSTOMIZATION_NAME}" \
1153 "${PROFILE_TYPE}" \
1154 "${FLEET_REPO_DIR}"
1155 )
1156
1157 # Parameters for the new Kustomization object to point to the profile
1158 local KS_NAME="$(safe_name "${PROFILE_TYPE}-${PROFILE_NAME}")"
1159
1160 # Finally, we update the folder with all the required changes:
1161 # - Update pre-existing Kustomizations that should depend on the new profile (besides others).
1162 # - Create a new Kustomization pointing to the profile.
1163 # - Update Kustomize's `kustomization.yaml` at the root of the cluster folder so that it no longer tries to gather the Kustomization pointing to the profile.
1164 # - Update the cluster folder accordingly.
1165 folder2list \
1166 "${CLUSTER_FOLDER}" |
1167 remove_dependency_from_set_of_kustomizations_safely \
1168 "${KS_NAME}" \
1169 "${THEY_DEPEND_ON_THIS}" | \
1170 delete_object \
1171 "${KS_NAME}" \
1172 "Kustomization" \
1173 "kustomize.toolkit.fluxcd.io/v1" | \
1174 patch_delete_from_list \
1175 ".resources[] | select(. == \"${MANIFEST_FILENAME}\") " \
1176 "| select(.kind == \"Kustomization\") | select(.apiVersion == \"kustomize.config.k8s.io/v1beta1\") | select(.metadata.annotations.\"config.kubernetes.io/path\" == \"kustomization.yaml\")" | \
1177 list2folder_sync_replace \
1178 "${CLUSTER_FOLDER}"
1179}
1180
1181
1182# Low-level function to add a KSU into a profile
1183function create_ksu_into_profile() {
1184 local KSU_NAME="$1"
1185 local TARGET_PROFILE_FOLDER="$2"
1186 local TEMPLATES_PATH="$3"
1187 local SW_CATALOGS_REPO_DIR="$4"
1188 local TRANSFORMER="${5:-noop_transformer}"
1189
1190 # Gathers all optional parameters for transformer funcion (if any) and puts them into an array for further use
1191 local ALL_PARAMS=( "${@}" )
1192 local TRANSFORMER_ARGS=( "${ALL_PARAMS[@]:5}" )
1193
1194 # Composes the route to the local templates folder
1195 local TEMPLATES_FOLDER="${SW_CATALOGS_REPO_DIR}/${TEMPLATES_PATH}"
1196
1197 folder2list \
1198 "${TEMPLATES_FOLDER}" | \
1199 "${TRANSFORMER}" \
1200 "${TRANSFORMER_ARGS[@]}" | \
1201 prepend_folder_path "${KSU_NAME}/" | \
1202 list2folder_cp_over \
1203 "${TARGET_PROFILE_FOLDER}"
1204}
1205
1206
1207# Function to render a KSU from a `ResourceList` into a profile
1208function render_ksu_into_profile() {
1209 local KSU_NAME="$1"
1210 local PROFILE_NAME="$2"
1211 local PROFILE_TYPE="$3"
1212 local PROJECT_NAME="${4:-"${MGMT_PROJECT_NAME}"}"
1213 local FLEET_REPO_DIR="$5"
1214 local SYNC="${6:-"false"}"
1215
1216 local TARGET_PROFILE_PATH=$(
1217 path_to_profile \
1218 "${PROFILE_NAME}" \
1219 "${PROFILE_TYPE}" \
1220 "${PROJECT_NAME}"
1221 )
1222
1223 local TARGET_PROFILE_FOLDER="${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
1224
1225 # Determines the appropriate function depending on rendering strategy
1226 # - Sync (and potentially delete files in target folder)
1227 # - Copy over (only overwrite changed files, keep the rest)
1228 RENDERER=""
1229 if [[ ${SYNC,,} == "true" ]];
1230 then
1231 RENDERER="list2folder_sync_replace"
1232 else
1233 RENDERER="list2folder_cp_over"
1234 fi
1235
1236 # Render with the selected strategy
1237 [[ "${DRY_RUN,,}" != "true" ]] && mkdir -p "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
1238 "${RENDERER}" \
1239 "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
1240 ## This is improves the behaviour of the following code,
1241 ## since avoids unintented deletions in parent folder due to sync
1242 # prepend_folder_path "${KSU_NAME}/" | \
1243 # "${RENDERER}" \
1244 # "${TARGET_PROFILE_FOLDER}"
1245}
1246
1247
1248# High-level function to add a KSU into a profile for the case where
1249# 1. It is originated from an OKA, and
1250# 2. It is based on a HelmRelease.
1251function create_hr_ksu_into_profile() {
1252 # Base KSU generation from template
1253 ## `TEMPLATES_DIR` is the result of:
1254 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}/{{inputs.parameters.templates_path}}"
1255 local TEMPLATES_DIR="$1"
1256 local SUBSTITUTE_ENVIRONMENT="${2:-"false"}"
1257 local SUBSTITUTION_FILTER="${3:-""}"
1258 local CUSTOM_ENV_VARS="${4:-""}"
1259 # Patch HelmRelease in KSU with inline values
1260 local KUSTOMIZATION_NAME="$5"
1261 local HELMRELEASE_NAME="$6"
1262 local INLINE_VALUES="${7:-""}"
1263 # Secret reference and generation (if required)
1264 local IS_PREEXISTING_SECRET="${8:-"false"}"
1265 local TARGET_NS="$9"
1266 local VALUES_SECRET_NAME="${10}"
1267 local SECRET_KEY="${11:-"values.yaml"}"
1268 local AGE_PUBLIC_KEY="${12}"
1269 ## `SECRET_VALUES` will be obtained from the
1270 ## secret named after the input parameter `reference_secret_for_values`,
1271 ## and from the key named after the input parameter `reference_key_for_values`
1272 local LOCAL_SECRET_VALUES="${13:-"${SECRET_VALUES}"}"
1273 # ConfigMap reference and generation (if required)
1274 local IS_PREEXISTING_CM="${14:-"false"}"
1275 local VALUES_CM_NAME="${15:-""}"
1276 local CM_KEY="${16:-""}"
1277 local CM_VALUES="${17:-""}"
1278 # KSU rendering
1279 local KSU_NAME="${18}"
1280 local PROFILE_NAME="${19}"
1281 local PROFILE_TYPE="${20}"
1282 local PROJECT_NAME="${21:-"osm_admin"}"
1283 ## `FLEET_REPO_DIR` is the result of:
1284 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1285 local FLEET_REPO_DIR="${22:-"/fleet/fleet-osm/"}"
1286 local SYNC="${23:-"true"}"
1287
1288 # Decides which steps may be skipped
1289 HAS_INLINE_VALUES=$([[ -n "${INLINE_VALUES}" ]]; echo $?)
1290 HAS_REFERENCES=$([[ ( -n "${VALUES_SECRET_NAME}" ) || ( -n "${VALUES_CM_NAME}" ) ]]; echo $?)
1291 NEEDS_NEW_SECRET=$([[ ( -n "${VALUES_SECRET_NAME}" ) && ( "${IS_PREEXISTING_SECRET,,}" == "false" ) ]]; echo $?)
1292 NEEDS_NEW_CM=$([[ ( -n "${VALUES_CM_NAME}" ) && ( "${IS_PREEXISTING_CM,,}" == "false" ) ]]; echo $?)
1293 ECHO_RESOURCELIST=$([[ "${DEBUG,,}" == "true" ]]; echo $?)
1294
1295 # If applicable, loads additional environment variables
1296 if [[ -n "${CUSTOM_ENV_VARS}" ]];
1297 then
1298 set -a
1299 source <(echo "${CUSTOM_ENV_VARS}")
1300 set +a
1301 fi
1302
1303 # Runs workflow
1304 folder2list_generator \
1305 "${TEMPLATES_DIR}" \
1306 "${SUBSTITUTE_ENVIRONMENT}" \
1307 "${SUBSTITUTION_FILTER}" | \
1308 transform_if \
1309 "${HAS_INLINE_VALUES}" \
1310 add_values_to_helmrelease_via_ks \
1311 "${KUSTOMIZATION_NAME}" \
1312 "${HELMRELEASE_NAME}" \
1313 "${INLINE_VALUES}" | \
1314 transform_if \
1315 "${HAS_REFERENCES}" \
1316 add_ref_values_to_hr_via_ks \
1317 "${KUSTOMIZATION_NAME}" \
1318 "${HELMRELEASE_NAME}" \
1319 "${VALUES_SECRET_NAME}" \
1320 "${VALUES_CM_NAME}" | \
1321 transform_if \
1322 "${NEEDS_NEW_SECRET}" \
1323 make_generator \
1324 "hr-values-secret.yaml" \
1325 kubectl_encrypt \
1326 "${AGE_PUBLIC_KEY}" \
1327 create \
1328 secret \
1329 generic \
1330 "${VALUES_SECRET_NAME}" \
1331 --namespace="${TARGET_NS}" \
1332 --from-file="${SECRET_KEY}"=<(echo "${LOCAL_SECRET_VALUES}") \
1333 -o=yaml \
1334 --dry-run=client | \
1335 transform_if \
1336 "${NEEDS_NEW_CM}" \
1337 make_generator \
1338 "hr-values-configmap.yaml" \
1339 kubectl \
1340 create \
1341 configmap \
1342 "${VALUES_CM_NAME}" \
1343 --namespace="${TARGET_NS}" \
1344 --from-file="${SECRET_KEY}"=<(echo "${CM_VALUES}") \
1345 -o=yaml \
1346 --dry-run=client | \
1347 transform_if \
1348 "${ECHO_RESOURCELIST}" \
1349 tee /dev/stderr | \
1350 render_ksu_into_profile \
1351 "${KSU_NAME}" \
1352 "${PROFILE_NAME}" \
1353 "${PROFILE_TYPE}" \
1354 "${PROJECT_NAME}" \
1355 "${FLEET_REPO_DIR}" \
1356 "${SYNC}"
1357}
1358
1359
1360# High-level function to update a KSU for the case where
1361# 1. It is originated from an OKA, and
1362# 2. It is based on a HelmRelease.
1363# NOTE: It is an alias of `create_hr_ksu_into_profile`, setting `sync` to true
1364function update_hr_ksu_into_profile() {
1365 # Base KSU generation from template
1366 ## `TEMPLATES_DIR` is the result of:
1367 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}/{{inputs.parameters.templates_path}}"
1368 local TEMPLATES_DIR="$1"
1369 local SUBSTITUTE_ENVIRONMENT="${2:-"false"}"
1370 local SUBSTITUTION_FILTER="${3:-""}"
1371 local CUSTOM_ENV_VARS="${4:-""}"
1372 # Patch HelmRelease in KSU with inline values
1373 local KUSTOMIZATION_NAME="$5"
1374 local HELMRELEASE_NAME="$6"
1375 local INLINE_VALUES="${7:-""}"
1376 # Secret reference and generation (if required)
1377 local IS_PREEXISTING_SECRET="${8:-"false"}"
1378 local TARGET_NS="$9"
1379 local VALUES_SECRET_NAME="${10}"
1380 local SECRET_KEY="${11:-"values.yaml"}"
1381 local AGE_PUBLIC_KEY="${12}"
1382 ## `SECRET_VALUES` will be obtained from the
1383 ## secret named after the input parameter `reference_secret_for_values`,
1384 ## and from the key named after the input parameter `reference_key_for_values`
1385 local LOCAL_SECRET_VALUES="${13:-"${SECRET_VALUES}"}"
1386 # ConfigMap reference and generation (if required)
1387 local IS_PREEXISTING_CM="${14:-"false"}"
1388 local VALUES_CM_NAME="${15:-""}"
1389 local CM_KEY="${16:-""}"
1390 local CM_VALUES="${17:-""}"
1391 # KSU rendering
1392 local KSU_NAME="${18}"
1393 local PROFILE_NAME="${19}"
1394 local PROFILE_TYPE="${20}"
1395 local PROJECT_NAME="${21:-"osm_admin"}"
1396 ## `FLEET_REPO_DIR` is the result of:
1397 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1398 local FLEET_REPO_DIR="${22:-"/fleet/fleet-osm/"}"
1399 # local SYNC="${23:-"true"}"
1400
1401
1402 # This function is just an alias of `create_hr_ksu_into_profile`
1403 # forcing synchronization over the KSU folder
1404 create_hr_ksu_into_profile \
1405 "${TEMPLATES_DIR}" \
1406 "${SUBSTITUTE_ENVIRONMENT}" \
1407 "${SUBSTITUTION_FILTER}" \
1408 "${CUSTOM_ENV_VARS}" \
1409 "${KUSTOMIZATION_NAME}" \
1410 "${HELMRELEASE_NAME}" \
1411 "${INLINE_VALUES}" \
1412 "${IS_PREEXISTING_SECRET}" \
1413 "${TARGET_NS}" \
1414 "${VALUES_SECRET_NAME}" \
1415 "${SECRET_KEY}" \
1416 "${AGE_PUBLIC_KEY}" \
1417 "${LOCAL_SECRET_VALUES}" \
1418 "${IS_PREEXISTING_CM}" \
1419 "${VALUES_CM_NAME}" \
1420 "${CM_KEY}" \
1421 "${CM_VALUES}" \
1422 "${KSU_NAME}" \
1423 "${PROFILE_NAME}" \
1424 "${PROFILE_TYPE}" \
1425 "${PROJECT_NAME}" \
1426 "${FLEET_REPO_DIR}" \
1427 "true"
1428}
1429
1430
1431# High-level function to create a "generated" KSU into a profile when:
1432# 1. There is no template (OKA) available.
1433# 2. The SW is based on a Helm Chart that we want to deploy.
1434function create_generated_ksu_from_helm_into_profile() {
1435 # HelmRelease generation
1436 local HELMRELEASE_NAME="$1"
1437 local CHART_NAME="$2"
1438 local CHART_VERSION="$3"
1439 local TARGET_NS="$4"
1440 local CREATE_NS="${5:-"true"}"
1441 # Repo source generation
1442 local IS_PREEXISTING_REPO="${6:-"false"}"
1443 local HELMREPO_NAME="$7"
1444 local HELMREPO_URL="${8:-""}"
1445 local HELMREPO_NS="${9:-"${TARGET_NS}"}"
1446 local HELMREPO_SECRET_REF="${10:-""}"
1447 # HelmRelease inline values (if any)
1448 local INLINE_VALUES="${11:-""}"
1449 # Secret reference and generation (if required)
1450 local IS_PREEXISTING_SECRET="${12:-"false"}"
1451 local VALUES_SECRET_NAME="${13}"
1452 local SECRET_KEY="${14:-"values.yaml"}"
1453 local AGE_PUBLIC_KEY="${15}"
1454 ## `SECRET_VALUES` will be obtained from the
1455 ## secret named after the input parameter `reference_secret_for_values`,
1456 ## and from the key named after the input parameter `reference_key_for_values`
1457 local LOCAL_SECRET_VALUES="${16:-"${SECRET_VALUES}"}"
1458 # ConfigMap reference and generation (if required)
1459 local IS_PREEXISTING_CM="${17:-"false"}"
1460 local VALUES_CM_NAME="${18:-""}"
1461 local CM_KEY="${19:-""}"
1462 local CM_VALUES="${20:-""}"
1463 # KSU rendering
1464 local KSU_NAME="${21}"
1465 local PROFILE_NAME="${22}"
1466 local PROFILE_TYPE="${23}"
1467 local PROJECT_NAME="${24:-"osm_admin"}"
1468 ## `FLEET_REPO_DIR` is the result of:
1469 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1470 local FLEET_REPO_DIR="${25:-"/fleet/fleet-osm/"}"
1471 # By default, it will not syncronize, so that we can easily accumulate more than
1472 # one Helm chart into the same KSU if desired
1473 local SYNC="${26:-"false"}"
1474
1475 # Decides which steps may be skipped
1476 local NEEDS_NEW_NS=$([[ "${CREATE_NS,,}" == "true" ]]; echo $?)
1477 local NEEDS_NEW_REPO_SOURCE=$([[ "${IS_PREEXISTING_REPO,,}" == "false" ]]; echo $?)
1478 local NEEDS_NEW_SECRET=$([[ ( -n "${VALUES_SECRET_NAME}" ) && ( "${IS_PREEXISTING_SECRET,,}" == "false" ) ]]; echo $?)
1479 local NEEDS_NEW_CM=$([[ ( -n "${VALUES_CM_NAME}" ) && ( "${IS_PREEXISTING_CM,,}" == "false" ) ]]; echo $?)
1480 local ECHO_RESOURCELIST=$([[ "${DEBUG,,}" == "true" ]]; echo $?)
1481
1482 # Determine extra options for HelmRelease creation and define full command
1483 OPTION_CHART_VERSION=""
1484 [[ -n "${CHART_VERSION}" ]] && OPTION_CHART_VERSION='--chart-version=${CHART_VERSION}'
1485 OPTION_INLINE_VALUES=""
1486 [[ -n "${INLINE_VALUES}" ]] && OPTION_INLINE_VALUES='--values=<(
1487 echo "${INLINE_VALUES}"
1488 )'
1489 OPTION_REFERENCE_SECRET=""
1490 [[ -n "${VALUES_SECRET_NAME}" ]] && OPTION_REFERENCE_SECRET='--values-from=Secret/${VALUES_SECRET_NAME}'
1491 OPTION_REFERENCE_CM=""
1492 [[ -n "${VALUES_CM_NAME}" ]] && OPTION_REFERENCE_CM='--values-from=ConfigMap/${VALUES_CM_NAME}'
1493
1494 export HR_COMMAND="\
1495 flux \
1496 -n "${TARGET_NS}" \
1497 create hr "${HELMRELEASE_NAME}" \
1498 --chart="${CHART_NAME}" \
1499 --source=HelmRepository/"${HELMREPO_NAME}.${HELMREPO_NS}" \
1500 "${OPTION_CHART_VERSION}" \
1501 "${OPTION_INLINE_VALUES}" \
1502 "${OPTION_REFERENCE_SECRET}" \
1503 "${OPTION_REFERENCE_CM}" \
1504 --export
1505 "
1506
1507 # Determine extra options for Helm source repo creation and define full command
1508 OPTION_REPO_SECRET=""
1509 [[ -n "${HELMREPO_SECRET_REF}" ]] && OPTION_REPO_SECRET='--secret-ref=${HELMREPO_SECRET_REF}'
1510
1511 export REPO_COMMAND="\
1512 flux \
1513 -n "${HELMREPO_NS}" \
1514 create source helm "${HELMREPO_NAME}" \
1515 --url="${HELMREPO_URL}" \
1516 "${OPTION_REPO_SECRET}" \
1517 --export
1518 "
1519
1520 # Runs workflow
1521 echo "" | \
1522 make_generator \
1523 "helm-release.yaml" \
1524 eval "${HR_COMMAND}" | \
1525 transform_if \
1526 "${NEEDS_NEW_NS}" \
1527 make_generator \
1528 "ns-for-hr.yaml" \
1529 kubectl \
1530 create \
1531 namespace \
1532 "${TARGET_NS}" \
1533 -o=yaml \
1534 --dry-run=client | \
1535 transform_if \
1536 "${NEEDS_NEW_REPO_SOURCE}" \
1537 make_generator \
1538 "helm-repo.yaml" \
1539 eval "${REPO_COMMAND}" | \
1540 transform_if \
1541 "${NEEDS_NEW_SECRET}" \
1542 make_generator \
1543 "hr-values-secret.yaml" \
1544 kubectl_encrypt \
1545 "${AGE_PUBLIC_KEY}" \
1546 create \
1547 secret \
1548 generic \
1549 "${VALUES_SECRET_NAME}" \
1550 --namespace="${TARGET_NS}" \
1551 --from-file="${SECRET_KEY}"=<(echo "${LOCAL_SECRET_VALUES}") \
1552 -o=yaml \
1553 --dry-run=client | \
1554 transform_if \
1555 "${NEEDS_NEW_CM}" \
1556 make_generator \
1557 "hr-values-configmap.yaml" \
1558 kubectl \
1559 create \
1560 configmap \
1561 "${VALUES_CM_NAME}" \
1562 --namespace="${TARGET_NS}" \
1563 --from-file="${SECRET_KEY}"=<(echo "${CM_VALUES}") \
1564 -o=yaml \
1565 --dry-run=client | \
1566 transform_if \
1567 "${ECHO_RESOURCELIST}" \
1568 tee /dev/stderr | \
1569 render_ksu_into_profile \
1570 "${KSU_NAME}" \
1571 "${PROFILE_NAME}" \
1572 "${PROFILE_TYPE}" \
1573 "${PROJECT_NAME}" \
1574 "${FLEET_REPO_DIR}" \
1575 "${SYNC}"
1576}
1577
1578
1579# High-level function to update a "generated" KSU:
1580# 1. There is no template (OKA) available.
1581# 2. The SW is based on a Helm Chart that we want to deploy.
1582# NOTE: It is an alias of `create_generated_ksu_from_helm_into_profile`, setting `sync` to true
1583function update_generated_ksu_from_helm_into_profile() {
1584 # HelmRelease generation
1585 local HELMRELEASE_NAME="$1"
1586 local CHART_NAME="$2"
1587 local CHART_VERSION="$3"
1588 local TARGET_NS="$4"
1589 local CREATE_NS="${5:-"true"}"
1590 # Repo source generation
1591 local IS_PREEXISTING_REPO="${6:-"false"}"
1592 local HELMREPO_NAME="$7"
1593 local HELMREPO_URL="${8:-""}"
1594 local HELMREPO_NS="${9:-"${TARGET_NS}"}"
1595 local HELMREPO_SECRET_REF="${10:-""}"
1596 # HelmRelease inline values (if any)
1597 local INLINE_VALUES="${11:-""}"
1598 # Secret reference and generation (if required)
1599 local IS_PREEXISTING_SECRET="${12:-"false"}"
1600 local VALUES_SECRET_NAME="${13}"
1601 local SECRET_KEY="${14:-"values.yaml"}"
1602 local AGE_PUBLIC_KEY="${15}"
1603 ## `SECRET_VALUES` will be obtained from the
1604 ## secret named after the input parameter `reference_secret_for_values`,
1605 ## and from the key named after the input parameter `reference_key_for_values`
1606 local LOCAL_SECRET_VALUES="${16:-"${SECRET_VALUES}"}"
1607 # ConfigMap reference and generation (if required)
1608 local IS_PREEXISTING_CM="${17:-"false"}"
1609 local VALUES_CM_NAME="${18:-""}"
1610 local CM_KEY="${19:-""}"
1611 local CM_VALUES="${20:-""}"
1612 # KSU rendering
1613 local KSU_NAME="${21}"
1614 local PROFILE_NAME="${22}"
1615 local PROFILE_TYPE="${23}"
1616 local PROJECT_NAME="${24:-"osm_admin"}"
1617 ## `FLEET_REPO_DIR` is the result of:
1618 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1619 local FLEET_REPO_DIR="${25:-"/fleet/fleet-osm/"}"
1620 # By default, it will not syncronize, so that we can easily accumulate more than
1621 # one Helm chart into the same KSU if desired
1622 # local SYNC="${26:-"false"}"
1623
1624 # Decides which steps may be skipped
1625 local NEEDS_NEW_NS=$([[ "${CREATE_NS,,}" == "true" ]]; echo $?)
1626 local NEEDS_NEW_REPO_SOURCE=$([[ "${IS_PREEXISTING_REPO,,}" == "false" ]]; echo $?)
1627 local NEEDS_NEW_SECRET=$([[ ( -n "${VALUES_SECRET_NAME}" ) && ( "${IS_PREEXISTING_SECRET,,}" == "false" ) ]]; echo $?)
1628 local NEEDS_NEW_CM=$([[ ( -n "${VALUES_CM_NAME}" ) && ( "${IS_PREEXISTING_CM,,}" == "false" ) ]]; echo $?)
1629 local ECHO_RESOURCELIST=$([[ "${DEBUG,,}" == "true" ]]; echo $?)
1630
1631
1632 # This function is just an alias of `create_generated_ksu_from_helm_into_profile`
1633 # forcing synchronization over the KSU folder
1634 create_generated_ksu_from_helm_into_profile \
1635 "${HELMRELEASE_NAME}" \
1636 "${CHART_NAME}" \
1637 "${CHART_VERSION}" \
1638 "${TARGET_NS}" \
1639 "${CREATE_NS}" \
1640 "${IS_PREEXISTING_REPO}" \
1641 "${HELMREPO_NAME}" \
1642 "${HELMREPO_URL}" \
1643 "${HELMREPO_NS}" \
1644 "${HELMREPO_SECRET_REF}" \
1645 "${INLINE_VALUES}" \
1646 "${IS_PREEXISTING_SECRET}" \
1647 "${VALUES_SECRET_NAME}" \
1648 "${SECRET_KEY}" \
1649 "${AGE_PUBLIC_KEY}" \
1650 "${LOCAL_SECRET_VALUES}" \
1651 "${IS_PREEXISTING_CM}" \
1652 "${VALUES_CM_NAME}" \
1653 "${CM_KEY}" \
1654 "${CM_VALUES}" \
1655 "${KSU_NAME}" \
1656 "${PROFILE_NAME}" \
1657 "${PROFILE_TYPE}" \
1658 "${PROJECT_NAME}" \
1659 "${FLEET_REPO_DIR}" \
1660 "true"
1661}
1662
1663
1664# Low-level function to delete a KSU from a profile
1665function delete_ksu_from_profile_path() {
1666 local KSU_NAME="$1"
1667 local TARGET_PROFILE_PATH="$2"
1668 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
1669
1670 # Calculate profile folder
1671 TARGET_PROFILE_FOLDER="${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
1672
1673 # Delete the KSU folder
1674 rm -rf "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
1675}
1676
1677
1678# High-level function to delete a KSU from a profile
1679function delete_ksu_from_profile() {
1680 local KSU_NAME="$1"
1681 local PROFILE_NAME="$2"
1682 local PROFILE_TYPE="$3"
1683 local PROJECT_NAME="${4:-"osm_admin"}"
1684 ## `FLEET_REPO_DIR` is the result of:
1685 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1686 local FLEET_REPO_DIR="$5"
1687
1688 # Calculate profile folder
1689 local TARGET_PROFILE_PATH=$(
1690 path_to_profile \
1691 "${PROFILE_NAME}" \
1692 "${PROFILE_TYPE}" \
1693 "${PROJECT_NAME}"
1694 )
1695 TARGET_PROFILE_FOLDER="${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
1696
1697 # Delete the KSU folder
1698 rm -rf "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
1699}
1700
1701
1702# High-level function to clone a KSU from a profile to another
1703function clone_ksu() {
1704 local SOURCE_KSU_NAME="$1"
1705 local SOURCE_PROFILE_NAME="$2"
1706 local SOURCE_PROFILE_TYPE="$3"
1707 local SOURCE_PROJECT_NAME="${4:-"osm_admin"}"
1708 local DESTINATION_KSU_NAME="${5:-"${SOURCE_KSU_NAME}"}"
1709 local DESTINATION_PROFILE_NAME="${6:-"${SOURCE_PROFILE_NAME}"}"
1710 local DESTINATION_PROFILE_TYPE="${7:-"${SOURCE_PROFILE_TYPE}"}"
1711 local DESTINATION_PROJECT_NAME="${8:-"${SOURCE_PROJECT_NAME}"}"
1712 ## `FLEET_REPO_DIR` is the result of:
1713 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1714 local FLEET_REPO_DIR="$9"
1715
1716
1717 # If source and destination are identical, aborts
1718 if [[
1719 ("${SOURCE_KSU_NAME}" == "${DESTINATION_KSU_NAME}") && \
1720 ("${SOURCE_PROFILE_NAME}" == "${DESTINATION_PROFILE_NAME}") && \
1721 ("${SOURCE_PROFILE_TYPE}" == "${DESTINATION_PROFILE_TYPE}") && \
1722 ("${SOURCE_PROJECT_NAME}" == "${DESTINATION_PROJECT_NAME}") \
1723 ]];
1724 then
1725 return 1
1726 fi
1727
1728 # Calculate profile folders
1729 local SOURCE_PROFILE_PATH=$(
1730 path_to_profile \
1731 "${SOURCE_PROFILE_NAME}" \
1732 "${SOURCE_PROFILE_TYPE}" \
1733 "${SOURCE_PROJECT_NAME}"
1734 )
1735 local SOURCE_PROFILE_FOLDER="${FLEET_REPO_DIR}/${SOURCE_PROFILE_PATH}"
1736 local DESTINATION_PROFILE_PATH=$(
1737 path_to_profile \
1738 "${DESTINATION_PROFILE_NAME}" \
1739 "${DESTINATION_PROFILE_TYPE}" \
1740 "${DESTINATION_PROJECT_NAME}"
1741 )
1742 local DESTINATION_PROFILE_FOLDER="${FLEET_REPO_DIR}/${DESTINATION_PROFILE_PATH}"
1743
1744 # Clone KSU folder
1745 cp -ar \
1746 "${SOURCE_PROFILE_FOLDER}/${SOURCE_KSU_NAME}" \
1747 "${DESTINATION_PROFILE_FOLDER}/${DESTINATION_KSU_NAME}"
1748}
1749
1750
1751# Create a `ProviderConfig` for a CrossPlane provider
1752function create_crossplane_providerconfig() {
1753 local PROVIDERCONFIG_NAME="$1"
1754 # As of today, one among `azure`, `aws` or `gcp`:
1755 local PROVIDER_TYPE="$2"
1756 local CRED_SECRET_NAME="$3"
1757 local CRED_SECRET_KEY="${4:-"creds"}"
1758 local CRED_SECRET_NS="${5:-"crossplane-system"}"
1759 # If empty, it assumes the secret already exists
1760 local CRED_SECRET_CONTENT="${6:-"${CRED_SECRET_CONTENT:-""}"}"
1761 local AGE_PUBLIC_KEY_MGMT="$7"
1762 ## `FLEET_REPO_DIR` is the result of:
1763 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1764 local FLEET_REPO_DIR="${8:-"${FLEET_REPO_DIR}"}"
1765 ## `SW_CATALOGS_REPO_DIR` is the result of:
1766 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
1767 local SW_CATALOGS_REPO_DIR="${9:-"${SW_CATALOGS_REPO_DIR}"}"
1768 # Only when applicable
1769 local TARGET_GCP_PROJECT="${10:-""}"
1770 # Do not touch unless strictly needed
1771 local BASE_TEMPLATES_PATH="${11:-"infra-configs/crossplane/providers"}"
1772 local OSM_PROJECT_NAME="${12:-"osm_admin"}"
1773 local MGMT_CLUSTER_NAME="${13:-"_management"}"
1774
1775
1776 # Is the provider type supported?
1777 local VALID_PROVIDERS=("aws" "azure" "gcp")
1778 PROVIDER_TYPE="${PROVIDER_TYPE,,}"
1779 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${PROVIDER_TYPE}")) ]] && return 1
1780
1781 # Determines the source dir for the templates and the target folder in Fleet
1782 local TEMPLATES_DIR="${SW_CATALOGS_REPO_DIR}/${BASE_TEMPLATES_PATH}/${PROVIDER_TYPE}/templates"
1783 local TARGET_FOLDER="${FLEET_REPO_DIR}/${OSM_PROJECT_NAME}/infra-config-profiles/${MGMT_CLUSTER_NAME}/crossplane-providerconfigs/${PROVIDER_TYPE}"
1784
1785 # Determine which optional steps may be needed
1786 local NEEDS_NEW_SECRET=$([[ -n "${CRED_SECRET_CONTENT}" ]]; echo $?)
1787 local NEEDS_PROJECT_NAME=$([[ "${PROVIDER_TYPE}" == "gcp" ]]; echo $?)
1788
1789 # Renders the `ProviderConfig` manifest and the encrypted secret (if applicable)
1790 echo "" | \
1791 folder2list_generator \
1792 "${TEMPLATES_DIR}" | \
1793 patch_replace \
1794 ".metadata.name" \
1795 "${PROVIDERCONFIG_NAME}" \
1796 "| select(.kind == \"ProviderConfig\")" | \
1797 patch_replace \
1798 ".spec.credentials.secretRef.name" \
1799 "${CRED_SECRET_NAME}" \
1800 "| select(.kind == \"ProviderConfig\")" | \
1801 patch_replace \
1802 ".spec.credentials.secretRef.key" \
1803 "${CRED_SECRET_KEY}" \
1804 "| select(.kind == \"ProviderConfig\")" | \
1805 patch_replace \
1806 ".spec.credentials.secretRef.namespace" \
1807 "${CRED_SECRET_NS}" \
1808 "| select(.kind == \"ProviderConfig\")" | \
1809 transform_if \
1810 "${NEEDS_PROJECT_NAME}" \
1811 patch_replace \
1812 ".spec.projectID" \
1813 "${TARGET_GCP_PROJECT}" \
1814 "| select(.kind == \"ProviderConfig\")" | \
1815 transform_if \
1816 "${NEEDS_NEW_SECRET}" \
1817 make_generator \
1818 "credentials-secret.yaml" \
1819 kubectl_encrypt \
1820 "${AGE_PUBLIC_KEY_MGMT}" \
1821 create \
1822 secret \
1823 generic \
1824 "${CRED_SECRET_NAME}" \
1825 --namespace="${CRED_SECRET_NS}" \
1826 --from-file="${CRED_SECRET_KEY}"=<(echo "${CRED_SECRET_CONTENT}") \
1827 -o=yaml \
1828 --dry-run=client | \
1829 prepend_folder_path \
1830 "${PROVIDERCONFIG_NAME}/" | \
1831 list2folder_cp_over \
1832 "${TARGET_FOLDER}"
1833}
1834
1835
1836# Delete a `ProviderConfig` for a CrossPlane provider
1837function delete_crossplane_providerconfig() {
1838 local PROVIDERCONFIG_NAME="$1"
1839 # As of today, one among `azure`, `aws` or `gcp`:
1840 local PROVIDER_TYPE="$2"
1841 ## `FLEET_REPO_DIR` is the result of:
1842 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1843 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
1844 # Do not touch unless strictly needed
1845 local OSM_PROJECT_NAME="${4:-"osm_admin"}"
1846 local MGMT_CLUSTER_NAME="${5:-"_management"}"
1847
1848
1849 # Is the provider type supported?
1850 local VALID_PROVIDERS=("aws" "azure" "gcp")
1851 PROVIDER_TYPE="${PROVIDER_TYPE,,}"
1852 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${PROVIDER_TYPE}")) ]] && return 1
1853
1854 # Determines the target folder in Fleet
1855 local PROVIDERCONFIG_FOLDER="${FLEET_REPO_DIR}/${OSM_PROJECT_NAME}/infra-config-profiles/${MGMT_CLUSTER_NAME}/crossplane-providerconfigs/${PROVIDER_TYPE}/${PROVIDERCONFIG_NAME}"
1856
1857 # Delete the folder
1858 rm -rf "${PROVIDERCONFIG_FOLDER}"
1859}
1860
1861
1862# Update a `ProviderConfig` for a CrossPlane provider
1863function update_crossplane_providerconfig() {
1864 local PROVIDERCONFIG_NAME="$1"
1865 # As of today, one among `azure`, `aws` or `gcp`:
1866 local PROVIDER_TYPE="$2"
1867 local CRED_SECRET_NAME="$3"
1868 local CRED_SECRET_KEY="${4:-"creds"}"
1869 local CRED_SECRET_NS="${5:-"crossplane-system"}"
1870 # If empty, it assumes the secret already exists
1871 local CRED_SECRET_CONTENT="${6:-"${CRED_SECRET_CONTENT:-""}"}"
1872 local AGE_PUBLIC_KEY_MGMT="$7"
1873 ## `FLEET_REPO_DIR` is the result of:
1874 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1875 local FLEET_REPO_DIR="${8:-"${FLEET_REPO_DIR}"}"
1876 ## `SW_CATALOGS_REPO_DIR` is the result of:
1877 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
1878 local SW_CATALOGS_REPO_DIR="${9:-"${SW_CATALOGS_REPO_DIR}"}"
1879 # Only when applicable
1880 local TARGET_GCP_PROJECT="${10:-""}"
1881 # Do not touch unless strictly needed
1882 local BASE_TEMPLATES_PATH="${11:-"infra-configs/crossplane/providers"}"
1883 local OSM_PROJECT_NAME="${12:-"osm_admin"}"
1884 local MGMT_CLUSTER_NAME="${13:-"_management"}"
1885
1886
1887 # Is the provider type supported?
1888 local VALID_PROVIDERS=("aws" "azure" "gcp")
1889 PROVIDER_TYPE="${PROVIDER_TYPE,,}"
1890 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${PROVIDER_TYPE}")) ]] && return 1
1891
1892 # First, delete; then, re-create
1893 delete_crossplane_providerconfig \
1894 "${PROVIDERCONFIG_NAME}" \
1895 "${PROVIDER_TYPE}" \
1896 "${FLEET_REPO_DIR}" \
1897 "${OSM_PROJECT_NAME}" \
1898 "${MGMT_CLUSTER_NAME}"
1899
1900 create_crossplane_providerconfig \
1901 "${PROVIDERCONFIG_NAME}" \
1902 "${PROVIDER_TYPE}" \
1903 "${CRED_SECRET_NAME}" \
1904 "${CRED_SECRET_KEY}" \
1905 "${CRED_SECRET_NS}" \
1906 "${CRED_SECRET_CONTENT}" \
1907 "${AGE_PUBLIC_KEY_MGMT}" \
1908 "${FLEET_REPO_DIR}" \
1909 "${SW_CATALOGS_REPO_DIR}" \
1910 "${TARGET_GCP_PROJECT}" \
1911 "${BASE_TEMPLATES_PATH}" \
1912 "${OSM_PROJECT_NAME}" \
1913 "${MGMT_CLUSTER_NAME}"
1914}
1915
1916
1917# Helper function to return the relative path of a location in SW Catalogs for an OKA
1918function path_to_catalog() {
1919 local OKA_TYPE="$1"
1920 local PROJECT_NAME="${2:-"osm_admin"}"
1921
1922 # Corrects `osm_admin` project, since it uses the root folder
1923 PROJECT_NAME="${PROJECT_NAME}"
1924 [[ "${PROJECT_NAME}" == "osm_admin" ]] && PROJECT_NAME="."
1925
1926 # Echoes the relate path from the SW-Catalogs root
1927 case "${OKA_TYPE,,}" in
1928
1929 "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers")
1930 echo -n "${PROJECT_NAME}/infra-controllers"
1931 return 0
1932 ;;
1933
1934 "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs")
1935 echo -n "${PROJECT_NAME}/infra-configs"
1936 return 0
1937 ;;
1938
1939 "managed" | "resources" | "managed-resources" | "managed_resources" | "cloud-resources" | "cloud_resources")
1940 echo -n "${PROJECT_NAME}/cloud-resources"
1941 return 0
1942 ;;
1943
1944 "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs")
1945 echo -n "${PROJECT_NAME}/apps"
1946 return 0
1947 ;;
1948
1949 *)
1950 echo -n "------------ ERROR ------------"
1951 return 1
1952 ;;
1953 esac
1954}
1955
1956
1957# Create OKA of a specific kind
1958function create_oka() {
1959 local OKA_NAME="$1"
1960 local OKA_TYPE="$2"
1961 local PROJECT_NAME="${3:-"."}"
1962 ## `SW_CATALOGS_REPO_DIR` is the result of:
1963 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
1964 local SW_CATALOGS_REPO_DIR="$4"
1965 local OKA_LOCATION="${5:-"."}"
1966 local TARBALL_FILE="${6:-"true"}"
1967
1968
1969 # Finds the corresponding catalog path from the SW-Catalogs root
1970 # and create the destination
1971 local CATALOG_PATH=$(\
1972 path_to_catalog \
1973 "${OKA_TYPE}" \
1974 "${PROJECT_NAME}"
1975 )
1976 local DESTINATION="${SW_CATALOGS_REPO_DIR}/${CATALOG_PATH}/${OKA_NAME}"
1977 mkdir -p "${DESTINATION}"
1978
1979 # When the OKA comes as a `tar.gz`
1980 if [[ "${TARBALL_FILE,,}" == "true" ]];
1981 then
1982 tar xvfz "${OKA_LOCATION}/${OKA_NAME}.tar.gz" -C "${DESTINATION}"
1983 else
1984 # Otherwise it must be a folder structure
1985 cp -var "${OKA_LOCATION}/${OKA_NAME}/*" "${DESTINATION}/"
1986 fi
1987}
1988
1989
1990# Delete OKA of a specific kind
1991function delete_oka() {
1992 local OKA_NAME="$1"
1993 local OKA_TYPE="$2"
1994 local PROJECT_NAME="${3:-"."}"
1995 ## `SW_CATALOGS_REPO_DIR` is the result of:
1996 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
1997 local SW_CATALOGS_REPO_DIR="$4"
1998
1999
2000 # Finds the corresponding catalog path from the SW-Catalogs root
2001 # and determine the destination
2002 local CATALOG_PATH=$(\
2003 path_to_catalog \
2004 "${OKA_TYPE}" \
2005 "${PROJECT_NAME}"
2006 )
2007 local DESTINATION="${SW_CATALOGS_REPO_DIR}/${CATALOG_PATH}/${OKA_NAME}"
2008
2009 # Remove the folder
2010 rm -rf "${DESTINATION}"
2011}
2012
2013
2014# Update OKA of a specific kind
2015function update_oka() {
2016 local OKA_NAME="$1"
2017 local OKA_TYPE="$2"
2018 local PROJECT_NAME="${3:-"."}"
2019 ## `SW_CATALOGS_REPO_DIR` is the result of:
2020 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
2021 local SW_CATALOGS_REPO_DIR="$4"
2022 local OKA_LOCATION="${5:-"."}"
2023 local TARBALL_FILE="${6:-"true"}"
2024
2025
2026 # Finds the corresponding catalog path from the SW-Catalogs root
2027 # and determine the destination
2028 local CATALOG_PATH=$(\
2029 path_to_catalog \
2030 "${OKA_TYPE}" \
2031 "${PROJECT_NAME}"
2032 )
2033 local DESTINATION="${SW_CATALOGS_REPO_DIR}/${CATALOG_PATH}/${OKA_NAME}"
2034
2035 # Remove and re-create
2036 rm -rf "${DESTINATION}"
2037 create_oka \
2038 "${OKA_NAME}" \
2039 "${OKA_TYPE}" \
2040 "${PROJECT_NAME}" \
2041 "${SW_CATALOGS_REPO_DIR}" \
2042 "${OKA_LOCATION}" \
2043 "${TARBALL_FILE}"
2044}