blob: 07a47f86caa0323eb721b4982b5ca8deafeab6e2 [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
580 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
581 ## 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
584 # Delete Flux resources syncronized directly from remote cluster
585 rm -rf "${CLUSTER_FOLDER}/flux-system"
586
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
722 # Delete profile folders
723 rm -rf "${INFRA_CONTROLLERS_DIR}"
724 rm -rf "${INFRA_CONFIGS_DIR}"
725 rm -rf "${MANAGED_RESOURCES_DIR}"
726 rm -rf "${APPS_DIR}"
727
728 # Delete base cluster Kustomizations
729 rm -rf "${CLUSTER_DIR}"
730
731 # Delete cluster resources
732 rm -rf "${MGMT_RESOURCES_DIR}/${CLUSTER_KUSTOMIZATION_NAME}"
733}
734
735
736# Update remote CrossPlane cluster (generic for any cloud)
737function update_crossplane_cluster() {
738 local CLUSTER_KUSTOMIZATION_NAME="$1"
739 local CLUSTER_NAME="$2"
740 # As of today, one among `aks`, `eks` or `gke`:
741 local CLUSTER_TYPE="$3"
742 local PROVIDERCONFIG_NAME="${4:-default}"
743 local VM_SIZE="$5"
744 local NODE_COUNT="$6"
745 local CLUSTER_LOCATION="$7"
746 local K8S_VERSION="${8:-"'1.28'"}"
747 local PUBLIC_KEY_MGMT="${9:-"${PUBLIC_KEY_MGMT}"}"
748 local PUBLIC_KEY_NEW_CLUSTER="${10}"
749 local PRIVATE_KEY_NEW_CLUSTER="${11:-"${PRIVATE_KEY_NEW_CLUSTER}"}"
750 # AKS only
751 local AKS_RG_NAME="${12:-""}"
752 # GKE only
753 local GKE_PREEMPTIBLE_NODES="${13:-""}"
754 ## `FLEET_REPO_DIR` is the result of:
755 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
756 local FLEET_REPO_DIR="${14:-"${FLEET_REPO_DIR}"}"
757 local FLEET_REPO_URL="${15:-""}"
758 ## `SW_CATALOGS_REPO_DIR` is the result of:
759 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
760 local SW_CATALOGS_REPO_DIR="${16:-"${SW_CATALOGS_REPO_DIR}"}"
761 local SW_CATALOGS_REPO_URL="${17:-""}"
762 # Prevent a new bootstrap by default
763 local SKIP_BOOTSTRAP="${18:"true"}"
764 # Only change if absolutely needeed
765 local MGMT_PROJECT_NAME="${19:-"osm_admin"}"
766 local MGMT_CLUSTER_NAME="${20:-"_management"}"
767 local BASE_TEMPLATES_PATH="${21:-"cloud-resources"}"
768 local TEMPLATE_MANIFEST_FILENAME="${22:-"${CLUSTER_TYPE,,}01.yaml"}"
769 local MANIFEST_FILENAME="${23:-"${CLUSTER_TYPE,,}-${CLUSTER_NAME}.yaml"}"
770
771
772 # Is the provider type supported?
773 local VALID_PROVIDERS=("eks" "aks" "gke")
774 CLUSTER_TYPE="${CLUSTER_TYPE,,}"
775 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${CLUSTER_TYPE}")) ]] && return 1
776
777 # Determine key folders in Fleet
778 local MGMT_RESOURCES_DIR="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}"
779
780 # First, delete cluster's CrossPlane resources
781 # NOTE: We only delete de Kustomization referring to CrossPlane resources,
782 # not the bootstrap resources or the profiles. Thus we avoid that KSUs are
783 # affected or a potential second unnecesary bootstrap.
784 rm -rf "${MGMT_RESOURCES_DIR}/${CLUSTER_KUSTOMIZATION_NAME}/${MANIFEST_FILENAME}"
785
786 # Then, recreate the manifests with updated values
787 create_crossplane_cluster \
788 "${CLUSTER_KUSTOMIZATION_NAME}" \
789 "${CLUSTER_NAME}" \
790 "${CLUSTER_TYPE}" \
791 "${PROVIDERCONFIG_NAME}" \
792 "${VM_SIZE}" \
793 "${NODE_COUNT}" \
794 "${CLUSTER_LOCATION}" \
795 "${K8S_VERSION}" \
796 "${PUBLIC_KEY_MGMT}" \
797 "${PUBLIC_KEY_NEW_CLUSTER}" \
798 "${PRIVATE_KEY_NEW_CLUSTER}" \
799 "${AKS_RG_NAME}" \
800 "${GKE_PREEMPTIBLE_NODES}" \
801 "${FLEET_REPO_DIR}" \
802 "${FLEET_REPO_URL}" \
803 "${SW_CATALOGS_REPO_DIR}" \
804 "${SW_CATALOGS_REPO_URL}" \
805 "${SKIP_BOOTSTRAP}" \
806 "${MGMT_PROJECT_NAME}" \
807 "${MGMT_CLUSTER_NAME}" \
808 "${BASE_TEMPLATES_PATH}" \
809 "${TEMPLATE_MANIFEST_FILENAME}" \
810 "${MANIFEST_FILENAME}"
811}
812
813
814# ----- Helper functions for adding/removing a profile from a cluster -----
815
816# Helper function to find profiles of a given type already used in the cluster
817function profiles_of_type_in_cluster() {
818 local CLUSTER_KUSTOMIZATION_NAME="$1"
819 local RELEVANT_PROFILE_TYPE="$2"
820 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
821
822 # Calculated fields
823 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
824
825 # Processing (echoes the list)
826 folder2list \
827 "${CLUSTER_FOLDER}" | \
828 get_value_from_resourcelist \
829 ".metadata.name" \
830 "| select(.kind == \"Kustomization\")
831 | select(.metadata.labels.osm_profile_type == \"${RELEVANT_PROFILE_TYPE}\")" | \
832 multiline2commalist
833}
834
835
836# Function to list the profiles **this profile depends on**
837function profiles_this_one_depends_on() {
838 local CLUSTER_KUSTOMIZATION_NAME="$1"
839 local PROFILE_TYPE="$2"
840 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
841
842 case "${PROFILE_TYPE,,}" in
843
844 "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers")
845 # Controllers do not depend on any other type of profiles
846 echo ""
847 return 0
848 ;;
849
850 "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs")
851 # Infra configs depend on controllers
852 profiles_of_type_in_cluster \
853 "${CLUSTER_KUSTOMIZATION_NAME}" \
854 "infra-controllers" \
855 "${FLEET_REPO_DIR}"
856 return 0
857 ;;
858
859 "managed" | "resources" | "managed-resources" | "managed_resources")
860 # Managed resources depend on infra configs
861 profiles_of_type_in_cluster \
862 "${CLUSTER_KUSTOMIZATION_NAME}" \
863 "infra-configs" \
864 "${FLEET_REPO_DIR}"
865 return 0
866 ;;
867
868 "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs")
869 # Apps (also) depend on infra configs
870 profiles_of_type_in_cluster \
871 "${CLUSTER_KUSTOMIZATION_NAME}" \
872 "infra-configs" \
873 "${FLEET_REPO_DIR}"
874 return 0
875 ;;
876
877 *)
878 echo -n "------------ ERROR ------------"
879 return 1
880 ;;
881 esac
882}
883
884
885# Function to list the profiles that **depend on this profile**
886function profiles_depend_on_this_one() {
887 local CLUSTER_KUSTOMIZATION_NAME="$1"
888 local PROFILE_TYPE="$2"
889 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
890
891 case "${PROFILE_TYPE,,}" in
892
893 "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers")
894 # Infra configs depend on infra controllers
895 profiles_of_type_in_cluster \
896 "${CLUSTER_KUSTOMIZATION_NAME}" \
897 "infra-configs" \
898 "${FLEET_REPO_DIR}"
899 return 0
900 ;;
901
902 "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs")
903 # Both managed resources and apps depend on configs
904 local PROFILES=(
905 $(
906 profiles_of_type_in_cluster \
907 "${CLUSTER_KUSTOMIZATION_NAME}" \
908 "managed-resources" \
909 "${FLEET_REPO_DIR}"
910 ) \
911 $(
912 profiles_of_type_in_cluster \
913 "${CLUSTER_KUSTOMIZATION_NAME}" \
914 "apps" \
915 "${FLEET_REPO_DIR}"
916 )
917 )
918 printf '%s,' "${PROFILES[@]}" | sed 's/,$//g'
919 return 0
920 ;;
921
922 "managed" | "resources" | "managed-resources" | "managed_resources")
923 # No other profiles depend on managed resources
924 echo ""
925 return 0
926 ;;
927
928 "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs")
929 # No other profiles depend on apps
930 echo ""
931 return 0
932 ;;
933
934 *)
935 echo -n "------------ ERROR ------------"
936 return 1
937 ;;
938 esac
939}
940
941
942# Helper function to add a dependency to a Kustomization only if it does not exist already
943function add_dependency_to_kustomization_safely() {
944 local KUSTOMIZATION_NAME="$1"
945 local KUSTOMIZATION_TO_ADD_AS_DEP="$2"
946
947 local INPUT=$(cat)
948 local FILTER="| select(.kind == \"Kustomization\") | select(.metadata.name == \"${KUSTOMIZATION_NAME}\")"
949
950 # Check if the dependency was added already
951 local TEST_RESULT=$(
952 echo "${INPUT}" | \
953 is_element_on_list \
954 ".spec.dependsOn[].name" \
955 "${KUSTOMIZATION_TO_ADD_AS_DEP}" \
956 "${FILTER}"
957 )
958
959 # If it existed already, returns the stream as is
960 if [[ "${TEST_RESULT}" == "true" ]]
961 then
962 echo "${INPUT}"
963 # Otherwise, processes the stream to add it
964 else
965 echo "${INPUT}" | \
966 patch_add_to_list \
967 ".spec.dependsOn" \
968 "{name: ${KUSTOMIZATION_TO_ADD_AS_DEP}}" \
969 "${FILTER}"
970 fi
971}
972
973
974# Helper function to remove a dependency from a Kustomization
975function remove_dependency_from_kustomization_safely() {
976 local KUSTOMIZATION_NAME="$1"
977 local KUSTOMIZATION_TO_REMOVE_AS_DEP="$2"
978
979 # Calculated inputs
980 local KEY_PATH=".spec.dependsOn[] | select(.name == \"${KUSTOMIZATION_TO_REMOVE_AS_DEP}\")"
981 local FILTER="| select(.kind == \"Kustomization\") | select(.metadata.name == \"${KUSTOMIZATION_NAME}\")"
982
983 # Remove the entry from the dependency list (if it exists)
984 yq "del((.items[]${FILTER})${KEY_PATH})"
985}
986
987
988# Ensure list of Kustomizations depend on a given Kustomization
989function add_dependency_to_set_of_kustomizations_safely() {
990 local KS_NAME="$1"
991 local THEY_DEPEND_ON_THIS="$2"
992
993 local INPUT="$(cat)"
994 local OUTPUT=""
995
996 # For each of the Kustomizations on the comma-separated list, adds `KS_NAME` as one of their dependencies
997 for KUST in ${THEY_DEPEND_ON_THIS//,/ }
998 do
999 local OUTPUT="$(
1000 echo "${INPUT}" | \
1001 add_dependency_to_kustomization_safely \
1002 "${KUST}" \
1003 "${KS_NAME}"
1004 )"
1005 local INPUT="${OUTPUT}"
1006 done
1007
1008 # Return the final `ResultList`, after all iterations
1009 echo "${OUTPUT}"
1010}
1011
1012
1013# Ensure list of Kustomizations no longer depend on a given Kustomization
1014function remove_dependency_from_set_of_kustomizations_safely() {
1015 local KS_NAME="$1"
1016 local THEY_NO_LONGER_DEPEND_ON_THIS="$2"
1017
1018 local INPUT="$(cat)"
1019 local OUTPUT=""
1020
1021 # For each of the Kustomizations on the comma-separated list, removes `KS_NAME` from their dependencies
1022 for KUST in ${THEY_NO_LONGER_DEPEND_ON_THIS//,/ }
1023 do
1024 local OUTPUT="$(
1025 echo "${INPUT}" | \
1026 remove_dependency_from_kustomization_safely \
1027 "${KUST}" \
1028 "${KS_NAME}"
1029 )"
1030 local INPUT="${OUTPUT}"
1031 done
1032
1033 # Return the final `ResultList`, after all iterations
1034 echo "${OUTPUT}"
1035}
1036
1037# ----- END of Helper functions for adding/removing a profile from a cluster -----
1038
1039
1040# Add an existing profile to a cluster
1041function attach_profile_to_cluster() {
1042 local PROFILE_NAME="$1"
1043 local PROFILE_TYPE="$2"
1044 local PROJECT_NAME="$3"
1045 local CLUSTER_KUSTOMIZATION_NAME="$4"
1046 local FLEET_REPO_DIR="${5:-"${FLEET_REPO_DIR}"}"
1047
1048 # Calculated inputs
1049 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
1050 local TARGET_PROFILE_PATH="$(
1051 path_to_profile \
1052 "${PROFILE_NAME}" \
1053 "${PROFILE_TYPE}" \
1054 "${PROJECT_NAME}"
1055 )"
1056
1057 # Finds out which profiles it should depend on... and which profiles should depend on it
1058 local DEPENDS_ON=$(
1059 profiles_this_one_depends_on \
1060 "${CLUSTER_KUSTOMIZATION_NAME}" \
1061 "${PROFILE_TYPE}" \
1062 "${FLEET_REPO_DIR}"
1063 )
1064
1065 local THEY_DEPEND_ON_THIS=$(
1066 profiles_depend_on_this_one \
1067 "${CLUSTER_KUSTOMIZATION_NAME}" \
1068 "${PROFILE_TYPE}" \
1069 "${FLEET_REPO_DIR}"
1070 )
1071
1072 # Parameters for the new Kustomization object to point to the profile
1073 local KS_NAME="$(safe_name "${PROFILE_TYPE}-${PROFILE_NAME}")"
1074 local MANIFEST_FILENAME="${KS_NAME}.yaml"
1075 local KS_NS=flux-system
1076 local MANIFESTS_PATH="${TARGET_PROFILE_PATH}"
1077 local SOURCE_REPO=GitRepository/fleet-repo.flux-system
1078 local SOURCE_SYNC_INTERVAL="60m"
1079 local HEALTH_CHECK_TO="3m"
1080 local RETRY_INTERVAL="1m"
1081 local TIMEOUT="5m"
1082 local OPTIONS="\
1083 --decryption-provider=sops \
1084 --decryption-secret=sops-age \
1085 --prune=true \
1086 --timeout="${TIMEOUT}" \
1087 --retry-interval="${RETRY_INTERVAL}" \
1088 --label osm_profile_type="${PROFILE_TYPE}"
1089 "
1090
1091 # Finally, we update the folder with all the required changes:
1092 # - Update pre-existing Kustomizations that should depend on the new profile (besides others).
1093 # - Create a new Kustomization pointing to the profile.
1094 # - Update Kustomize's `kustomization.yaml` at the root of the cluster folder to take into account the new Kustomization pointing to the profile.
1095 # - Update the cluster folder accordingly.
1096 folder2list \
1097 "${CLUSTER_FOLDER}" |
1098 add_dependency_to_set_of_kustomizations_safely \
1099 "${KS_NAME}" \
1100 "${THEY_DEPEND_ON_THIS}" | \
1101 generator_kustomization \
1102 "${MANIFEST_FILENAME}" \
1103 "${KS_NAME}" \
1104 "${KS_NS}" \
1105 "${SOURCE_REPO}" \
1106 "${MANIFESTS_PATH}" \
1107 "${SOURCE_SYNC_INTERVAL}" \
1108 "${HEALTH_CHECK_TO}" \
1109 "${DEPENDS_ON}" \
1110 "${OPTIONS}" | \
1111 patch_add_to_list \
1112 ".resources" \
1113 "${MANIFEST_FILENAME}" \
1114 "| select(.kind == \"Kustomization\") | select(.apiVersion == \"kustomize.config.k8s.io/v1beta1\") | select(.metadata.annotations.\"config.kubernetes.io/path\" == \"kustomization.yaml\")" | \
1115 list2folder_sync_replace \
1116 "${CLUSTER_FOLDER}"
1117}
1118
1119
1120# Remove an existing profile from a cluster
1121function detach_profile_from_cluster() {
1122 local PROFILE_NAME="$1"
1123 local PROFILE_TYPE="$2"
1124 local PROJECT_NAME="$3"
1125 local CLUSTER_KUSTOMIZATION_NAME="$4"
1126 local FLEET_REPO_DIR="${5:-"${FLEET_REPO_DIR}"}"
1127
1128 # Calculated inputs
1129 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
1130 local TARGET_PROFILE_PATH="$(
1131 path_to_profile \
1132 "${PROFILE_NAME}" \
1133 "${PROFILE_TYPE}" \
1134 "${PROJECT_NAME}"
1135 )"
1136
1137 # Finds out which profiles still depend on it
1138 local THEY_DEPEND_ON_THIS=$(
1139 profiles_depend_on_this_one \
1140 "${CLUSTER_KUSTOMIZATION_NAME}" \
1141 "${PROFILE_TYPE}" \
1142 "${FLEET_REPO_DIR}"
1143 )
1144
1145 # Parameters for the new Kustomization object to point to the profile
1146 local KS_NAME="$(safe_name "${PROFILE_TYPE}-${PROFILE_NAME}")"
1147
1148 # Finally, we update the folder with all the required changes:
1149 # - Update pre-existing Kustomizations that should depend on the new profile (besides others).
1150 # - Create a new Kustomization pointing to the profile.
1151 # - 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.
1152 # - Update the cluster folder accordingly.
1153 folder2list \
1154 "${CLUSTER_FOLDER}" |
1155 remove_dependency_from_set_of_kustomizations_safely \
1156 "${KS_NAME}" \
1157 "${THEY_DEPEND_ON_THIS}" | \
1158 delete_object \
1159 "${KS_NAME}" \
1160 "Kustomization" \
1161 "kustomize.toolkit.fluxcd.io/v1" | \
1162 patch_delete_from_list \
1163 ".resources[] | select(. == \"${MANIFEST_FILENAME}\") " \
1164 "| select(.kind == \"Kustomization\") | select(.apiVersion == \"kustomize.config.k8s.io/v1beta1\") | select(.metadata.annotations.\"config.kubernetes.io/path\" == \"kustomization.yaml\")" | \
1165 list2folder_sync_replace \
1166 "${CLUSTER_FOLDER}"
1167}
1168
1169
1170# Low-level function to add a KSU into a profile
1171function create_ksu_into_profile() {
1172 local KSU_NAME="$1"
1173 local TARGET_PROFILE_FOLDER="$2"
1174 local TEMPLATES_PATH="$3"
1175 local SW_CATALOGS_REPO_DIR="$4"
1176 local TRANSFORMER="${5:-noop_transformer}"
1177
1178 # Gathers all optional parameters for transformer funcion (if any) and puts them into an array for further use
1179 local ALL_PARAMS=( "${@}" )
1180 local TRANSFORMER_ARGS=( "${ALL_PARAMS[@]:5}" )
1181
1182 # Composes the route to the local templates folder
1183 local TEMPLATES_FOLDER="${SW_CATALOGS_REPO_DIR}/${TEMPLATES_PATH}"
1184
1185 folder2list \
1186 "${TEMPLATES_FOLDER}" | \
1187 "${TRANSFORMER}" \
1188 "${TRANSFORMER_ARGS[@]}" | \
1189 prepend_folder_path "${KSU_NAME}/" | \
1190 list2folder_cp_over \
1191 "${TARGET_PROFILE_FOLDER}"
1192}
1193
1194
1195# Function to render a KSU from a `ResourceList` into a profile
1196function render_ksu_into_profile() {
1197 local KSU_NAME="$1"
1198 local PROFILE_NAME="$2"
1199 local PROFILE_TYPE="$3"
1200 local PROJECT_NAME="${4:-"${MGMT_PROJECT_NAME}"}"
1201 local FLEET_REPO_DIR="$5"
1202 local SYNC="${6:-"false"}"
1203
1204 local TARGET_PROFILE_PATH=$(
1205 path_to_profile \
1206 "${PROFILE_NAME}" \
1207 "${PROFILE_TYPE}" \
1208 "${PROJECT_NAME}"
1209 )
1210
1211 local TARGET_PROFILE_FOLDER="${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
1212
1213 # Determines the appropriate function depending on rendering strategy
1214 # - Sync (and potentially delete files in target folder)
1215 # - Copy over (only overwrite changed files, keep the rest)
1216 RENDERER=""
1217 if [[ ${SYNC,,} == "true" ]];
1218 then
1219 RENDERER="list2folder_sync_replace"
1220 else
1221 RENDERER="list2folder_cp_over"
1222 fi
1223
1224 # Render with the selected strategy
1225 [[ "${DRY_RUN,,}" != "true" ]] && mkdir -p "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
1226 "${RENDERER}" \
1227 "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
1228 ## This is improves the behaviour of the following code,
1229 ## since avoids unintented deletions in parent folder due to sync
1230 # prepend_folder_path "${KSU_NAME}/" | \
1231 # "${RENDERER}" \
1232 # "${TARGET_PROFILE_FOLDER}"
1233}
1234
1235
1236# High-level function to add a KSU into a profile for the case where
1237# 1. It is originated from an OKA, and
1238# 2. It is based on a HelmRelease.
1239function create_hr_ksu_into_profile() {
1240 # Base KSU generation from template
1241 ## `TEMPLATES_DIR` is the result of:
1242 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}/{{inputs.parameters.templates_path}}"
1243 local TEMPLATES_DIR="$1"
1244 local SUBSTITUTE_ENVIRONMENT="${2:-"false"}"
1245 local SUBSTITUTION_FILTER="${3:-""}"
1246 local CUSTOM_ENV_VARS="${4:-""}"
1247 # Patch HelmRelease in KSU with inline values
1248 local KUSTOMIZATION_NAME="$5"
1249 local HELMRELEASE_NAME="$6"
1250 local INLINE_VALUES="${7:-""}"
1251 # Secret reference and generation (if required)
1252 local IS_PREEXISTING_SECRET="${8:-"false"}"
1253 local TARGET_NS="$9"
1254 local VALUES_SECRET_NAME="${10}"
1255 local SECRET_KEY="${11:-"values.yaml"}"
1256 local AGE_PUBLIC_KEY="${12}"
1257 ## `SECRET_VALUES` will be obtained from the
1258 ## secret named after the input parameter `reference_secret_for_values`,
1259 ## and from the key named after the input parameter `reference_key_for_values`
1260 local LOCAL_SECRET_VALUES="${13:-"${SECRET_VALUES}"}"
1261 # ConfigMap reference and generation (if required)
1262 local IS_PREEXISTING_CM="${14:-"false"}"
1263 local VALUES_CM_NAME="${15:-""}"
1264 local CM_KEY="${16:-""}"
1265 local CM_VALUES="${17:-""}"
1266 # KSU rendering
1267 local KSU_NAME="${18}"
1268 local PROFILE_NAME="${19}"
1269 local PROFILE_TYPE="${20}"
1270 local PROJECT_NAME="${21:-"osm_admin"}"
1271 ## `FLEET_REPO_DIR` is the result of:
1272 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1273 local FLEET_REPO_DIR="${22:-"/fleet/fleet-osm/"}"
1274 local SYNC="${23:-"true"}"
1275
1276 # Decides which steps may be skipped
1277 HAS_INLINE_VALUES=$([[ -n "${INLINE_VALUES}" ]]; echo $?)
1278 HAS_REFERENCES=$([[ ( -n "${VALUES_SECRET_NAME}" ) || ( -n "${VALUES_CM_NAME}" ) ]]; echo $?)
1279 NEEDS_NEW_SECRET=$([[ ( -n "${VALUES_SECRET_NAME}" ) && ( "${IS_PREEXISTING_SECRET,,}" == "false" ) ]]; echo $?)
1280 NEEDS_NEW_CM=$([[ ( -n "${VALUES_CM_NAME}" ) && ( "${IS_PREEXISTING_CM,,}" == "false" ) ]]; echo $?)
1281 ECHO_RESOURCELIST=$([[ "${DEBUG,,}" == "true" ]]; echo $?)
1282
1283 # If applicable, loads additional environment variables
1284 if [[ -n "${CUSTOM_ENV_VARS}" ]];
1285 then
1286 set -a
1287 source <(echo "${CUSTOM_ENV_VARS}")
1288 set +a
1289 fi
1290
1291 # Runs workflow
1292 folder2list_generator \
1293 "${TEMPLATES_DIR}" \
1294 "${SUBSTITUTE_ENVIRONMENT}" \
1295 "${SUBSTITUTION_FILTER}" | \
1296 transform_if \
1297 "${HAS_INLINE_VALUES}" \
1298 add_values_to_helmrelease_via_ks \
1299 "${KUSTOMIZATION_NAME}" \
1300 "${HELMRELEASE_NAME}" \
1301 "${INLINE_VALUES}" | \
1302 transform_if \
1303 "${HAS_REFERENCES}" \
1304 add_ref_values_to_hr_via_ks \
1305 "${KUSTOMIZATION_NAME}" \
1306 "${HELMRELEASE_NAME}" \
1307 "${VALUES_SECRET_NAME}" \
1308 "${VALUES_CM_NAME}" | \
1309 transform_if \
1310 "${NEEDS_NEW_SECRET}" \
1311 make_generator \
1312 "hr-values-secret.yaml" \
1313 kubectl_encrypt \
1314 "${AGE_PUBLIC_KEY}" \
1315 create \
1316 secret \
1317 generic \
1318 "${VALUES_SECRET_NAME}" \
1319 --namespace="${TARGET_NS}" \
1320 --from-file="${SECRET_KEY}"=<(echo "${LOCAL_SECRET_VALUES}") \
1321 -o=yaml \
1322 --dry-run=client | \
1323 transform_if \
1324 "${NEEDS_NEW_CM}" \
1325 make_generator \
1326 "hr-values-configmap.yaml" \
1327 kubectl \
1328 create \
1329 configmap \
1330 "${VALUES_CM_NAME}" \
1331 --namespace="${TARGET_NS}" \
1332 --from-file="${SECRET_KEY}"=<(echo "${CM_VALUES}") \
1333 -o=yaml \
1334 --dry-run=client | \
1335 transform_if \
1336 "${ECHO_RESOURCELIST}" \
1337 tee /dev/stderr | \
1338 render_ksu_into_profile \
1339 "${KSU_NAME}" \
1340 "${PROFILE_NAME}" \
1341 "${PROFILE_TYPE}" \
1342 "${PROJECT_NAME}" \
1343 "${FLEET_REPO_DIR}" \
1344 "${SYNC}"
1345}
1346
1347
1348# High-level function to update a KSU for the case where
1349# 1. It is originated from an OKA, and
1350# 2. It is based on a HelmRelease.
1351# NOTE: It is an alias of `create_hr_ksu_into_profile`, setting `sync` to true
1352function update_hr_ksu_into_profile() {
1353 # Base KSU generation from template
1354 ## `TEMPLATES_DIR` is the result of:
1355 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}/{{inputs.parameters.templates_path}}"
1356 local TEMPLATES_DIR="$1"
1357 local SUBSTITUTE_ENVIRONMENT="${2:-"false"}"
1358 local SUBSTITUTION_FILTER="${3:-""}"
1359 local CUSTOM_ENV_VARS="${4:-""}"
1360 # Patch HelmRelease in KSU with inline values
1361 local KUSTOMIZATION_NAME="$5"
1362 local HELMRELEASE_NAME="$6"
1363 local INLINE_VALUES="${7:-""}"
1364 # Secret reference and generation (if required)
1365 local IS_PREEXISTING_SECRET="${8:-"false"}"
1366 local TARGET_NS="$9"
1367 local VALUES_SECRET_NAME="${10}"
1368 local SECRET_KEY="${11:-"values.yaml"}"
1369 local AGE_PUBLIC_KEY="${12}"
1370 ## `SECRET_VALUES` will be obtained from the
1371 ## secret named after the input parameter `reference_secret_for_values`,
1372 ## and from the key named after the input parameter `reference_key_for_values`
1373 local LOCAL_SECRET_VALUES="${13:-"${SECRET_VALUES}"}"
1374 # ConfigMap reference and generation (if required)
1375 local IS_PREEXISTING_CM="${14:-"false"}"
1376 local VALUES_CM_NAME="${15:-""}"
1377 local CM_KEY="${16:-""}"
1378 local CM_VALUES="${17:-""}"
1379 # KSU rendering
1380 local KSU_NAME="${18}"
1381 local PROFILE_NAME="${19}"
1382 local PROFILE_TYPE="${20}"
1383 local PROJECT_NAME="${21:-"osm_admin"}"
1384 ## `FLEET_REPO_DIR` is the result of:
1385 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1386 local FLEET_REPO_DIR="${22:-"/fleet/fleet-osm/"}"
1387 # local SYNC="${23:-"true"}"
1388
1389
1390 # This function is just an alias of `create_hr_ksu_into_profile`
1391 # forcing synchronization over the KSU folder
1392 create_hr_ksu_into_profile \
1393 "${TEMPLATES_DIR}" \
1394 "${SUBSTITUTE_ENVIRONMENT}" \
1395 "${SUBSTITUTION_FILTER}" \
1396 "${CUSTOM_ENV_VARS}" \
1397 "${KUSTOMIZATION_NAME}" \
1398 "${HELMRELEASE_NAME}" \
1399 "${INLINE_VALUES}" \
1400 "${IS_PREEXISTING_SECRET}" \
1401 "${TARGET_NS}" \
1402 "${VALUES_SECRET_NAME}" \
1403 "${SECRET_KEY}" \
1404 "${AGE_PUBLIC_KEY}" \
1405 "${LOCAL_SECRET_VALUES}" \
1406 "${IS_PREEXISTING_CM}" \
1407 "${VALUES_CM_NAME}" \
1408 "${CM_KEY}" \
1409 "${CM_VALUES}" \
1410 "${KSU_NAME}" \
1411 "${PROFILE_NAME}" \
1412 "${PROFILE_TYPE}" \
1413 "${PROJECT_NAME}" \
1414 "${FLEET_REPO_DIR}" \
1415 "true"
1416}
1417
1418
1419# High-level function to create a "generated" KSU into a profile when:
1420# 1. There is no template (OKA) available.
1421# 2. The SW is based on a Helm Chart that we want to deploy.
1422function create_generated_ksu_from_helm_into_profile() {
1423 # HelmRelease generation
1424 local HELMRELEASE_NAME="$1"
1425 local CHART_NAME="$2"
1426 local CHART_VERSION="$3"
1427 local TARGET_NS="$4"
1428 local CREATE_NS="${5:-"true"}"
1429 # Repo source generation
1430 local IS_PREEXISTING_REPO="${6:-"false"}"
1431 local HELMREPO_NAME="$7"
1432 local HELMREPO_URL="${8:-""}"
1433 local HELMREPO_NS="${9:-"${TARGET_NS}"}"
1434 local HELMREPO_SECRET_REF="${10:-""}"
1435 # HelmRelease inline values (if any)
1436 local INLINE_VALUES="${11:-""}"
1437 # Secret reference and generation (if required)
1438 local IS_PREEXISTING_SECRET="${12:-"false"}"
1439 local VALUES_SECRET_NAME="${13}"
1440 local SECRET_KEY="${14:-"values.yaml"}"
1441 local AGE_PUBLIC_KEY="${15}"
1442 ## `SECRET_VALUES` will be obtained from the
1443 ## secret named after the input parameter `reference_secret_for_values`,
1444 ## and from the key named after the input parameter `reference_key_for_values`
1445 local LOCAL_SECRET_VALUES="${16:-"${SECRET_VALUES}"}"
1446 # ConfigMap reference and generation (if required)
1447 local IS_PREEXISTING_CM="${17:-"false"}"
1448 local VALUES_CM_NAME="${18:-""}"
1449 local CM_KEY="${19:-""}"
1450 local CM_VALUES="${20:-""}"
1451 # KSU rendering
1452 local KSU_NAME="${21}"
1453 local PROFILE_NAME="${22}"
1454 local PROFILE_TYPE="${23}"
1455 local PROJECT_NAME="${24:-"osm_admin"}"
1456 ## `FLEET_REPO_DIR` is the result of:
1457 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1458 local FLEET_REPO_DIR="${25:-"/fleet/fleet-osm/"}"
1459 # By default, it will not syncronize, so that we can easily accumulate more than
1460 # one Helm chart into the same KSU if desired
1461 local SYNC="${26:-"false"}"
1462
1463 # Decides which steps may be skipped
1464 local NEEDS_NEW_NS=$([[ "${CREATE_NS,,}" == "true" ]]; echo $?)
1465 local NEEDS_NEW_REPO_SOURCE=$([[ "${IS_PREEXISTING_REPO,,}" == "false" ]]; echo $?)
1466 local NEEDS_NEW_SECRET=$([[ ( -n "${VALUES_SECRET_NAME}" ) && ( "${IS_PREEXISTING_SECRET,,}" == "false" ) ]]; echo $?)
1467 local NEEDS_NEW_CM=$([[ ( -n "${VALUES_CM_NAME}" ) && ( "${IS_PREEXISTING_CM,,}" == "false" ) ]]; echo $?)
1468 local ECHO_RESOURCELIST=$([[ "${DEBUG,,}" == "true" ]]; echo $?)
1469
1470 # Determine extra options for HelmRelease creation and define full command
1471 OPTION_CHART_VERSION=""
1472 [[ -n "${CHART_VERSION}" ]] && OPTION_CHART_VERSION='--chart-version=${CHART_VERSION}'
1473 OPTION_INLINE_VALUES=""
1474 [[ -n "${INLINE_VALUES}" ]] && OPTION_INLINE_VALUES='--values=<(
1475 echo "${INLINE_VALUES}"
1476 )'
1477 OPTION_REFERENCE_SECRET=""
1478 [[ -n "${VALUES_SECRET_NAME}" ]] && OPTION_REFERENCE_SECRET='--values-from=Secret/${VALUES_SECRET_NAME}'
1479 OPTION_REFERENCE_CM=""
1480 [[ -n "${VALUES_CM_NAME}" ]] && OPTION_REFERENCE_CM='--values-from=ConfigMap/${VALUES_CM_NAME}'
1481
1482 export HR_COMMAND="\
1483 flux \
1484 -n "${TARGET_NS}" \
1485 create hr "${HELMRELEASE_NAME}" \
1486 --chart="${CHART_NAME}" \
1487 --source=HelmRepository/"${HELMREPO_NAME}.${HELMREPO_NS}" \
1488 "${OPTION_CHART_VERSION}" \
1489 "${OPTION_INLINE_VALUES}" \
1490 "${OPTION_REFERENCE_SECRET}" \
1491 "${OPTION_REFERENCE_CM}" \
1492 --export
1493 "
1494
1495 # Determine extra options for Helm source repo creation and define full command
1496 OPTION_REPO_SECRET=""
1497 [[ -n "${HELMREPO_SECRET_REF}" ]] && OPTION_REPO_SECRET='--secret-ref=${HELMREPO_SECRET_REF}'
1498
1499 export REPO_COMMAND="\
1500 flux \
1501 -n "${HELMREPO_NS}" \
1502 create source helm "${HELMREPO_NAME}" \
1503 --url="${HELMREPO_URL}" \
1504 "${OPTION_REPO_SECRET}" \
1505 --export
1506 "
1507
1508 # Runs workflow
1509 echo "" | \
1510 make_generator \
1511 "helm-release.yaml" \
1512 eval "${HR_COMMAND}" | \
1513 transform_if \
1514 "${NEEDS_NEW_NS}" \
1515 make_generator \
1516 "ns-for-hr.yaml" \
1517 kubectl \
1518 create \
1519 namespace \
1520 "${TARGET_NS}" \
1521 -o=yaml \
1522 --dry-run=client | \
1523 transform_if \
1524 "${NEEDS_NEW_REPO_SOURCE}" \
1525 make_generator \
1526 "helm-repo.yaml" \
1527 eval "${REPO_COMMAND}" | \
1528 transform_if \
1529 "${NEEDS_NEW_SECRET}" \
1530 make_generator \
1531 "hr-values-secret.yaml" \
1532 kubectl_encrypt \
1533 "${AGE_PUBLIC_KEY}" \
1534 create \
1535 secret \
1536 generic \
1537 "${VALUES_SECRET_NAME}" \
1538 --namespace="${TARGET_NS}" \
1539 --from-file="${SECRET_KEY}"=<(echo "${LOCAL_SECRET_VALUES}") \
1540 -o=yaml \
1541 --dry-run=client | \
1542 transform_if \
1543 "${NEEDS_NEW_CM}" \
1544 make_generator \
1545 "hr-values-configmap.yaml" \
1546 kubectl \
1547 create \
1548 configmap \
1549 "${VALUES_CM_NAME}" \
1550 --namespace="${TARGET_NS}" \
1551 --from-file="${SECRET_KEY}"=<(echo "${CM_VALUES}") \
1552 -o=yaml \
1553 --dry-run=client | \
1554 transform_if \
1555 "${ECHO_RESOURCELIST}" \
1556 tee /dev/stderr | \
1557 render_ksu_into_profile \
1558 "${KSU_NAME}" \
1559 "${PROFILE_NAME}" \
1560 "${PROFILE_TYPE}" \
1561 "${PROJECT_NAME}" \
1562 "${FLEET_REPO_DIR}" \
1563 "${SYNC}"
1564}
1565
1566
1567# High-level function to update a "generated" KSU:
1568# 1. There is no template (OKA) available.
1569# 2. The SW is based on a Helm Chart that we want to deploy.
1570# NOTE: It is an alias of `create_generated_ksu_from_helm_into_profile`, setting `sync` to true
1571function update_generated_ksu_from_helm_into_profile() {
1572 # HelmRelease generation
1573 local HELMRELEASE_NAME="$1"
1574 local CHART_NAME="$2"
1575 local CHART_VERSION="$3"
1576 local TARGET_NS="$4"
1577 local CREATE_NS="${5:-"true"}"
1578 # Repo source generation
1579 local IS_PREEXISTING_REPO="${6:-"false"}"
1580 local HELMREPO_NAME="$7"
1581 local HELMREPO_URL="${8:-""}"
1582 local HELMREPO_NS="${9:-"${TARGET_NS}"}"
1583 local HELMREPO_SECRET_REF="${10:-""}"
1584 # HelmRelease inline values (if any)
1585 local INLINE_VALUES="${11:-""}"
1586 # Secret reference and generation (if required)
1587 local IS_PREEXISTING_SECRET="${12:-"false"}"
1588 local VALUES_SECRET_NAME="${13}"
1589 local SECRET_KEY="${14:-"values.yaml"}"
1590 local AGE_PUBLIC_KEY="${15}"
1591 ## `SECRET_VALUES` will be obtained from the
1592 ## secret named after the input parameter `reference_secret_for_values`,
1593 ## and from the key named after the input parameter `reference_key_for_values`
1594 local LOCAL_SECRET_VALUES="${16:-"${SECRET_VALUES}"}"
1595 # ConfigMap reference and generation (if required)
1596 local IS_PREEXISTING_CM="${17:-"false"}"
1597 local VALUES_CM_NAME="${18:-""}"
1598 local CM_KEY="${19:-""}"
1599 local CM_VALUES="${20:-""}"
1600 # KSU rendering
1601 local KSU_NAME="${21}"
1602 local PROFILE_NAME="${22}"
1603 local PROFILE_TYPE="${23}"
1604 local PROJECT_NAME="${24:-"osm_admin"}"
1605 ## `FLEET_REPO_DIR` is the result of:
1606 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1607 local FLEET_REPO_DIR="${25:-"/fleet/fleet-osm/"}"
1608 # By default, it will not syncronize, so that we can easily accumulate more than
1609 # one Helm chart into the same KSU if desired
1610 # local SYNC="${26:-"false"}"
1611
1612 # Decides which steps may be skipped
1613 local NEEDS_NEW_NS=$([[ "${CREATE_NS,,}" == "true" ]]; echo $?)
1614 local NEEDS_NEW_REPO_SOURCE=$([[ "${IS_PREEXISTING_REPO,,}" == "false" ]]; echo $?)
1615 local NEEDS_NEW_SECRET=$([[ ( -n "${VALUES_SECRET_NAME}" ) && ( "${IS_PREEXISTING_SECRET,,}" == "false" ) ]]; echo $?)
1616 local NEEDS_NEW_CM=$([[ ( -n "${VALUES_CM_NAME}" ) && ( "${IS_PREEXISTING_CM,,}" == "false" ) ]]; echo $?)
1617 local ECHO_RESOURCELIST=$([[ "${DEBUG,,}" == "true" ]]; echo $?)
1618
1619
1620 # This function is just an alias of `create_generated_ksu_from_helm_into_profile`
1621 # forcing synchronization over the KSU folder
1622 create_generated_ksu_from_helm_into_profile \
1623 "${HELMRELEASE_NAME}" \
1624 "${CHART_NAME}" \
1625 "${CHART_VERSION}" \
1626 "${TARGET_NS}" \
1627 "${CREATE_NS}" \
1628 "${IS_PREEXISTING_REPO}" \
1629 "${HELMREPO_NAME}" \
1630 "${HELMREPO_URL}" \
1631 "${HELMREPO_NS}" \
1632 "${HELMREPO_SECRET_REF}" \
1633 "${INLINE_VALUES}" \
1634 "${IS_PREEXISTING_SECRET}" \
1635 "${VALUES_SECRET_NAME}" \
1636 "${SECRET_KEY}" \
1637 "${AGE_PUBLIC_KEY}" \
1638 "${LOCAL_SECRET_VALUES}" \
1639 "${IS_PREEXISTING_CM}" \
1640 "${VALUES_CM_NAME}" \
1641 "${CM_KEY}" \
1642 "${CM_VALUES}" \
1643 "${KSU_NAME}" \
1644 "${PROFILE_NAME}" \
1645 "${PROFILE_TYPE}" \
1646 "${PROJECT_NAME}" \
1647 "${FLEET_REPO_DIR}" \
1648 "true"
1649}
1650
1651
1652# Low-level function to delete a KSU from a profile
1653function delete_ksu_from_profile_path() {
1654 local KSU_NAME="$1"
1655 local TARGET_PROFILE_PATH="$2"
1656 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
1657
1658 # Calculate profile folder
1659 TARGET_PROFILE_FOLDER="${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
1660
1661 # Delete the KSU folder
1662 rm -rf "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
1663}
1664
1665
1666# High-level function to delete a KSU from a profile
1667function delete_ksu_from_profile() {
1668 local KSU_NAME="$1"
1669 local PROFILE_NAME="$2"
1670 local PROFILE_TYPE="$3"
1671 local PROJECT_NAME="${4:-"osm_admin"}"
1672 ## `FLEET_REPO_DIR` is the result of:
1673 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1674 local FLEET_REPO_DIR="$5"
1675
1676 # Calculate profile folder
1677 local TARGET_PROFILE_PATH=$(
1678 path_to_profile \
1679 "${PROFILE_NAME}" \
1680 "${PROFILE_TYPE}" \
1681 "${PROJECT_NAME}"
1682 )
1683 TARGET_PROFILE_FOLDER="${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
1684
1685 # Delete the KSU folder
1686 rm -rf "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
1687}
1688
1689
1690# High-level function to clone a KSU from a profile to another
1691function clone_ksu() {
1692 local SOURCE_KSU_NAME="$1"
1693 local SOURCE_PROFILE_NAME="$2"
1694 local SOURCE_PROFILE_TYPE="$3"
1695 local SOURCE_PROJECT_NAME="${4:-"osm_admin"}"
1696 local DESTINATION_KSU_NAME="${5:-"${SOURCE_KSU_NAME}"}"
1697 local DESTINATION_PROFILE_NAME="${6:-"${SOURCE_PROFILE_NAME}"}"
1698 local DESTINATION_PROFILE_TYPE="${7:-"${SOURCE_PROFILE_TYPE}"}"
1699 local DESTINATION_PROJECT_NAME="${8:-"${SOURCE_PROJECT_NAME}"}"
1700 ## `FLEET_REPO_DIR` is the result of:
1701 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1702 local FLEET_REPO_DIR="$9"
1703
1704
1705 # If source and destination are identical, aborts
1706 if [[
1707 ("${SOURCE_KSU_NAME}" == "${DESTINATION_KSU_NAME}") && \
1708 ("${SOURCE_PROFILE_NAME}" == "${DESTINATION_PROFILE_NAME}") && \
1709 ("${SOURCE_PROFILE_TYPE}" == "${DESTINATION_PROFILE_TYPE}") && \
1710 ("${SOURCE_PROJECT_NAME}" == "${DESTINATION_PROJECT_NAME}") \
1711 ]];
1712 then
1713 return 1
1714 fi
1715
1716 # Calculate profile folders
1717 local SOURCE_PROFILE_PATH=$(
1718 path_to_profile \
1719 "${SOURCE_PROFILE_NAME}" \
1720 "${SOURCE_PROFILE_TYPE}" \
1721 "${SOURCE_PROJECT_NAME}"
1722 )
1723 local SOURCE_PROFILE_FOLDER="${FLEET_REPO_DIR}/${SOURCE_PROFILE_PATH}"
1724 local DESTINATION_PROFILE_PATH=$(
1725 path_to_profile \
1726 "${DESTINATION_PROFILE_NAME}" \
1727 "${DESTINATION_PROFILE_TYPE}" \
1728 "${DESTINATION_PROJECT_NAME}"
1729 )
1730 local DESTINATION_PROFILE_FOLDER="${FLEET_REPO_DIR}/${DESTINATION_PROFILE_PATH}"
1731
1732 # Clone KSU folder
1733 cp -ar \
1734 "${SOURCE_PROFILE_FOLDER}/${SOURCE_KSU_NAME}" \
1735 "${DESTINATION_PROFILE_FOLDER}/${DESTINATION_KSU_NAME}"
1736}
1737
1738
1739# Create a `ProviderConfig` for a CrossPlane provider
1740function create_crossplane_providerconfig() {
1741 local PROVIDERCONFIG_NAME="$1"
1742 # As of today, one among `azure`, `aws` or `gcp`:
1743 local PROVIDER_TYPE="$2"
1744 local CRED_SECRET_NAME="$3"
1745 local CRED_SECRET_KEY="${4:-"creds"}"
1746 local CRED_SECRET_NS="${5:-"crossplane-system"}"
1747 # If empty, it assumes the secret already exists
1748 local CRED_SECRET_CONTENT="${6:-"${CRED_SECRET_CONTENT:-""}"}"
1749 local AGE_PUBLIC_KEY_MGMT="$7"
1750 ## `FLEET_REPO_DIR` is the result of:
1751 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1752 local FLEET_REPO_DIR="${8:-"${FLEET_REPO_DIR}"}"
1753 ## `SW_CATALOGS_REPO_DIR` is the result of:
1754 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
1755 local SW_CATALOGS_REPO_DIR="${9:-"${SW_CATALOGS_REPO_DIR}"}"
1756 # Only when applicable
1757 local TARGET_GCP_PROJECT="${10:-""}"
1758 # Do not touch unless strictly needed
1759 local BASE_TEMPLATES_PATH="${11:-"infra-configs/crossplane/providers"}"
1760 local OSM_PROJECT_NAME="${12:-"osm_admin"}"
1761 local MGMT_CLUSTER_NAME="${13:-"_management"}"
1762
1763
1764 # Is the provider type supported?
1765 local VALID_PROVIDERS=("aws" "azure" "gcp")
1766 PROVIDER_TYPE="${PROVIDER_TYPE,,}"
1767 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${PROVIDER_TYPE}")) ]] && return 1
1768
1769 # Determines the source dir for the templates and the target folder in Fleet
1770 local TEMPLATES_DIR="${SW_CATALOGS_REPO_DIR}/${BASE_TEMPLATES_PATH}/${PROVIDER_TYPE}/templates"
1771 local TARGET_FOLDER="${FLEET_REPO_DIR}/${OSM_PROJECT_NAME}/infra-config-profiles/${MGMT_CLUSTER_NAME}/crossplane-providerconfigs/${PROVIDER_TYPE}"
1772
1773 # Determine which optional steps may be needed
1774 local NEEDS_NEW_SECRET=$([[ -n "${CRED_SECRET_CONTENT}" ]]; echo $?)
1775 local NEEDS_PROJECT_NAME=$([[ "${PROVIDER_TYPE}" == "gcp" ]]; echo $?)
1776
1777 # Renders the `ProviderConfig` manifest and the encrypted secret (if applicable)
1778 echo "" | \
1779 folder2list_generator \
1780 "${TEMPLATES_DIR}" | \
1781 patch_replace \
1782 ".metadata.name" \
1783 "${PROVIDERCONFIG_NAME}" \
1784 "| select(.kind == \"ProviderConfig\")" | \
1785 patch_replace \
1786 ".spec.credentials.secretRef.name" \
1787 "${CRED_SECRET_NAME}" \
1788 "| select(.kind == \"ProviderConfig\")" | \
1789 patch_replace \
1790 ".spec.credentials.secretRef.key" \
1791 "${CRED_SECRET_KEY}" \
1792 "| select(.kind == \"ProviderConfig\")" | \
1793 patch_replace \
1794 ".spec.credentials.secretRef.namespace" \
1795 "${CRED_SECRET_NS}" \
1796 "| select(.kind == \"ProviderConfig\")" | \
1797 transform_if \
1798 "${NEEDS_PROJECT_NAME}" \
1799 patch_replace \
1800 ".spec.projectID" \
1801 "${TARGET_GCP_PROJECT}" \
1802 "| select(.kind == \"ProviderConfig\")" | \
1803 transform_if \
1804 "${NEEDS_NEW_SECRET}" \
1805 make_generator \
1806 "credentials-secret.yaml" \
1807 kubectl_encrypt \
1808 "${AGE_PUBLIC_KEY_MGMT}" \
1809 create \
1810 secret \
1811 generic \
1812 "${CRED_SECRET_NAME}" \
1813 --namespace="${CRED_SECRET_NS}" \
1814 --from-file="${CRED_SECRET_KEY}"=<(echo "${CRED_SECRET_CONTENT}") \
1815 -o=yaml \
1816 --dry-run=client | \
1817 prepend_folder_path \
1818 "${PROVIDERCONFIG_NAME}/" | \
1819 list2folder_cp_over \
1820 "${TARGET_FOLDER}"
1821}
1822
1823
1824# Delete a `ProviderConfig` for a CrossPlane provider
1825function delete_crossplane_providerconfig() {
1826 local PROVIDERCONFIG_NAME="$1"
1827 # As of today, one among `azure`, `aws` or `gcp`:
1828 local PROVIDER_TYPE="$2"
1829 ## `FLEET_REPO_DIR` is the result of:
1830 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1831 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
1832 # Do not touch unless strictly needed
1833 local OSM_PROJECT_NAME="${4:-"osm_admin"}"
1834 local MGMT_CLUSTER_NAME="${5:-"_management"}"
1835
1836
1837 # Is the provider type supported?
1838 local VALID_PROVIDERS=("aws" "azure" "gcp")
1839 PROVIDER_TYPE="${PROVIDER_TYPE,,}"
1840 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${PROVIDER_TYPE}")) ]] && return 1
1841
1842 # Determines the target folder in Fleet
1843 local PROVIDERCONFIG_FOLDER="${FLEET_REPO_DIR}/${OSM_PROJECT_NAME}/infra-config-profiles/${MGMT_CLUSTER_NAME}/crossplane-providerconfigs/${PROVIDER_TYPE}/${PROVIDERCONFIG_NAME}"
1844
1845 # Delete the folder
1846 rm -rf "${PROVIDERCONFIG_FOLDER}"
1847}
1848
1849
1850# Update a `ProviderConfig` for a CrossPlane provider
1851function update_crossplane_providerconfig() {
1852 local PROVIDERCONFIG_NAME="$1"
1853 # As of today, one among `azure`, `aws` or `gcp`:
1854 local PROVIDER_TYPE="$2"
1855 local CRED_SECRET_NAME="$3"
1856 local CRED_SECRET_KEY="${4:-"creds"}"
1857 local CRED_SECRET_NS="${5:-"crossplane-system"}"
1858 # If empty, it assumes the secret already exists
1859 local CRED_SECRET_CONTENT="${6:-"${CRED_SECRET_CONTENT:-""}"}"
1860 local AGE_PUBLIC_KEY_MGMT="$7"
1861 ## `FLEET_REPO_DIR` is the result of:
1862 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1863 local FLEET_REPO_DIR="${8:-"${FLEET_REPO_DIR}"}"
1864 ## `SW_CATALOGS_REPO_DIR` is the result of:
1865 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
1866 local SW_CATALOGS_REPO_DIR="${9:-"${SW_CATALOGS_REPO_DIR}"}"
1867 # Only when applicable
1868 local TARGET_GCP_PROJECT="${10:-""}"
1869 # Do not touch unless strictly needed
1870 local BASE_TEMPLATES_PATH="${11:-"infra-configs/crossplane/providers"}"
1871 local OSM_PROJECT_NAME="${12:-"osm_admin"}"
1872 local MGMT_CLUSTER_NAME="${13:-"_management"}"
1873
1874
1875 # Is the provider type supported?
1876 local VALID_PROVIDERS=("aws" "azure" "gcp")
1877 PROVIDER_TYPE="${PROVIDER_TYPE,,}"
1878 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${PROVIDER_TYPE}")) ]] && return 1
1879
1880 # First, delete; then, re-create
1881 delete_crossplane_providerconfig \
1882 "${PROVIDERCONFIG_NAME}" \
1883 "${PROVIDER_TYPE}" \
1884 "${FLEET_REPO_DIR}" \
1885 "${OSM_PROJECT_NAME}" \
1886 "${MGMT_CLUSTER_NAME}"
1887
1888 create_crossplane_providerconfig \
1889 "${PROVIDERCONFIG_NAME}" \
1890 "${PROVIDER_TYPE}" \
1891 "${CRED_SECRET_NAME}" \
1892 "${CRED_SECRET_KEY}" \
1893 "${CRED_SECRET_NS}" \
1894 "${CRED_SECRET_CONTENT}" \
1895 "${AGE_PUBLIC_KEY_MGMT}" \
1896 "${FLEET_REPO_DIR}" \
1897 "${SW_CATALOGS_REPO_DIR}" \
1898 "${TARGET_GCP_PROJECT}" \
1899 "${BASE_TEMPLATES_PATH}" \
1900 "${OSM_PROJECT_NAME}" \
1901 "${MGMT_CLUSTER_NAME}"
1902}
1903
1904
1905# Helper function to return the relative path of a location in SW Catalogs for an OKA
1906function path_to_catalog() {
1907 local OKA_TYPE="$1"
1908 local PROJECT_NAME="${2:-"osm_admin"}"
1909
1910 # Corrects `osm_admin` project, since it uses the root folder
1911 PROJECT_NAME="${PROJECT_NAME}"
1912 [[ "${PROJECT_NAME}" == "osm_admin" ]] && PROJECT_NAME="."
1913
1914 # Echoes the relate path from the SW-Catalogs root
1915 case "${OKA_TYPE,,}" in
1916
1917 "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers")
1918 echo -n "${PROJECT_NAME}/infra-controllers"
1919 return 0
1920 ;;
1921
1922 "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs")
1923 echo -n "${PROJECT_NAME}/infra-configs"
1924 return 0
1925 ;;
1926
1927 "managed" | "resources" | "managed-resources" | "managed_resources" | "cloud-resources" | "cloud_resources")
1928 echo -n "${PROJECT_NAME}/cloud-resources"
1929 return 0
1930 ;;
1931
1932 "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs")
1933 echo -n "${PROJECT_NAME}/apps"
1934 return 0
1935 ;;
1936
1937 *)
1938 echo -n "------------ ERROR ------------"
1939 return 1
1940 ;;
1941 esac
1942}
1943
1944
1945# Create OKA of a specific kind
1946function create_oka() {
1947 local OKA_NAME="$1"
1948 local OKA_TYPE="$2"
1949 local PROJECT_NAME="${3:-"."}"
1950 ## `SW_CATALOGS_REPO_DIR` is the result of:
1951 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
1952 local SW_CATALOGS_REPO_DIR="$4"
1953 local OKA_LOCATION="${5:-"."}"
1954 local TARBALL_FILE="${6:-"true"}"
1955
1956
1957 # Finds the corresponding catalog path from the SW-Catalogs root
1958 # and create the destination
1959 local CATALOG_PATH=$(\
1960 path_to_catalog \
1961 "${OKA_TYPE}" \
1962 "${PROJECT_NAME}"
1963 )
1964 local DESTINATION="${SW_CATALOGS_REPO_DIR}/${CATALOG_PATH}/${OKA_NAME}"
1965 mkdir -p "${DESTINATION}"
1966
1967 # When the OKA comes as a `tar.gz`
1968 if [[ "${TARBALL_FILE,,}" == "true" ]];
1969 then
1970 tar xvfz "${OKA_LOCATION}/${OKA_NAME}.tar.gz" -C "${DESTINATION}"
1971 else
1972 # Otherwise it must be a folder structure
1973 cp -var "${OKA_LOCATION}/${OKA_NAME}/*" "${DESTINATION}/"
1974 fi
1975}
1976
1977
1978# Delete OKA of a specific kind
1979function delete_oka() {
1980 local OKA_NAME="$1"
1981 local OKA_TYPE="$2"
1982 local PROJECT_NAME="${3:-"."}"
1983 ## `SW_CATALOGS_REPO_DIR` is the result of:
1984 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
1985 local SW_CATALOGS_REPO_DIR="$4"
1986
1987
1988 # Finds the corresponding catalog path from the SW-Catalogs root
1989 # and determine the destination
1990 local CATALOG_PATH=$(\
1991 path_to_catalog \
1992 "${OKA_TYPE}" \
1993 "${PROJECT_NAME}"
1994 )
1995 local DESTINATION="${SW_CATALOGS_REPO_DIR}/${CATALOG_PATH}/${OKA_NAME}"
1996
1997 # Remove the folder
1998 rm -rf "${DESTINATION}"
1999}
2000
2001
2002# Update OKA of a specific kind
2003function update_oka() {
2004 local OKA_NAME="$1"
2005 local OKA_TYPE="$2"
2006 local PROJECT_NAME="${3:-"."}"
2007 ## `SW_CATALOGS_REPO_DIR` is the result of:
2008 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
2009 local SW_CATALOGS_REPO_DIR="$4"
2010 local OKA_LOCATION="${5:-"."}"
2011 local TARBALL_FILE="${6:-"true"}"
2012
2013
2014 # Finds the corresponding catalog path from the SW-Catalogs root
2015 # and determine the destination
2016 local CATALOG_PATH=$(\
2017 path_to_catalog \
2018 "${OKA_TYPE}" \
2019 "${PROJECT_NAME}"
2020 )
2021 local DESTINATION="${SW_CATALOGS_REPO_DIR}/${CATALOG_PATH}/${OKA_NAME}"
2022
2023 # Remove and re-create
2024 rm -rf "${DESTINATION}"
2025 create_oka \
2026 "${OKA_NAME}" \
2027 "${OKA_TYPE}" \
2028 "${PROJECT_NAME}" \
2029 "${SW_CATALOGS_REPO_DIR}" \
2030 "${OKA_LOCATION}" \
2031 "${TARBALL_FILE}"
2032}