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