3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
16 function generator_encrypted_secret_cloud_credentials() {
17 local CLOUD_CREDENTIALS_FILENAME="$1"
18 local SECRET_NAME="$2"
20 local SECRET_MANIFEST_FILENAME="${4:-secret-${SECRET_NAME}.yaml}"
24 <(cat "${CREDENTIALS_DIR}/${CLOUD_CREDENTIALS_FILENAME}" | \
25 kubectl create secret generic ${SECRET_NAME} \
26 --namespace crossplane-system \
27 --from-file creds=/dev/stdin \
28 -o yaml --dry-run=client | \
29 encrypt_secret_from_stdin "${PUBLIC_KEY_MGMT}" | \
31 set_filename_to_items "${SECRET_MANIFEST_FILENAME}")
35 # Create ProviderConfig for Azure
36 function add_providerconfig_for_azure() {
38 local CLOUD_CREDENTIALS="$1"
39 local NEW_SECRET_NAME="$2"
40 local PROVIDERCONFIG_NAME="${3:-default}"
41 local PUBLIC_KEY="${4:-${PUBLIC_KEY_MGMT}}"
42 local TARGET_FOLDER="${5:-${MGMT_ADDON_CONFIG_DIR}}"
44 # Path to folder with base templates
45 local TEMPLATES="${SW_CATALOGS_REPO_DIR}/infra-configs/crossplane/providers/azure/templates/"
52 "${PROVIDERCONFIG_NAME}" \
53 "| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")" | \
55 ".spec.credentials.secretRef.name" \
56 "${NEW_SECRET_NAME}" \
57 "| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")" | \
58 rename_file_in_items \
59 "crossplane-providerconfig-azure.yaml" \
60 "crossplane-providerconfig-azure-${PROVIDERCONFIG_NAME}.yaml" | \
61 generator_encrypted_secret_cloud_credentials \
62 "${CLOUD_CREDENTIALS}" \
63 "${NEW_SECRET_NAME}" \
70 # Create ProviderConfig for GCP
71 function add_providerconfig_for_gcp() {
73 local CLOUD_CREDENTIALS="$1"
74 local NEW_SECRET_NAME="$2"
75 local GCP_PROJECT="$3"
76 local PROVIDERCONFIG_NAME="${4:-default}"
77 local PUBLIC_KEY="${5:-${PUBLIC_KEY_MGMT}}"
78 local TARGET_FOLDER="${6:-${MGMT_ADDON_CONFIG_DIR}}"
80 # Path to folder with base templates
81 local TEMPLATES="${SW_CATALOGS_REPO_DIR}/infra-configs/crossplane/providers/gcp/templates/"
88 "${PROVIDERCONFIG_NAME}" \
89 "| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")" | \
91 ".spec.credentials.secretRef.name" \
92 "${NEW_SECRET_NAME}" \
93 "| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")" | \
97 "| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")" | \
98 rename_file_in_items \
99 "crossplane-providerconfig-gcp.yaml" \
100 "crossplane-providerconfig-gcp-${PROVIDERCONFIG_NAME}.yaml" | \
101 generator_encrypted_secret_cloud_credentials \
102 "${CLOUD_CREDENTIALS}" \
103 "${NEW_SECRET_NAME}" \
105 list2folder_cp_over \
111 # Create AKS cluster (without bootstrap)
112 function create_cluster_aks() {
113 local CLUSTER_NAME="$1"
115 local NODE_COUNT="$3"
116 local CLUSTER_LOCATION="$4"
118 local K8S_VERSION="${6:-"'1.28'"}"
119 local PROVIDERCONFIG_NAME="${7:-default}"
120 local CLUSTER_KUSTOMIZATION_NAME="${8:$(safe_name ${CLUSTER_NAME})}"
121 local TARGET_FOLDER="${9:-${MGMT_RESOURCES_DIR}}"
122 local MANIFEST_FILENAME="${10:-"${CLUSTER_NAME}.yaml"}"
123 local TEMPLATES="${11:-"${SW_CATALOGS_REPO_DIR}/cloud-resources/aks/templates/"}"
124 local TEMPLATE_MANIFEST_FILENAME="${12:-"aks01.yaml"}"
126 export CLUSTER_KUSTOMIZATION_NAME
130 '${CLUSTER_KUSTOMIZATION_NAME}' | \
132 ".spec.postBuild.substitute.cluster_name" \
134 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
136 ".spec.postBuild.substitute.cluster_name" \
138 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
140 ".spec.postBuild.substitute.vm_size" \
142 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
144 ".spec.postBuild.substitute.node_count" \
146 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
148 ".spec.postBuild.substitute.cluster_location" \
149 "${CLUSTER_LOCATION}" \
150 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
152 ".spec.postBuild.substitute.rg_name" \
154 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
156 ".spec.postBuild.substitute.k8s_version" \
158 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
160 ".spec.postBuild.substitute.providerconfig_name" \
161 "${PROVIDERCONFIG_NAME}" \
162 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
163 rename_file_in_items \
164 "${TEMPLATE_MANIFEST_FILENAME}" \
165 "${MANIFEST_FILENAME}" | \
166 prepend_folder_path "${CLUSTER_KUSTOMIZATION_NAME}/" | \
167 list2folder_cp_over \
172 # Generator to create a profile folder
173 function generator_profile_folder() {
174 local CONFIGMAP_NAME="$1"
175 local PROFILE_PATH="$2"
176 local PROFILE_TYPE="$3"
177 local REPO_URL="${4:-${FLEET_REPO_URL}}"
178 local PROFILE_LOCAL_DIR="${5:-"${PROFILE_PATH}"}"
182 <(kubectl create configmap $(safe_name "${CONFIGMAP_NAME}") \
183 --namespace flux-system \
184 --from-literal=repo="${REPO_URL}" \
185 --from-literal=path="${PROFILE_PATH}" \
191 "${PROFILE_TYPE}" | \
192 set_filename_to_items "profile-configmap.yaml" | \
193 prepend_folder_path "${PROFILE_LOCAL_DIR}/")
197 # Helper function to return the relative path of a profile
198 function path_to_profile() {
199 local PROFILE_NAME="$1"
200 local PROFILE_TYPE="$2"
201 local PROJECT_NAME="${3:-"${MGMT_PROJECT_NAME}"}"
203 case "${PROFILE_TYPE,,}" in
205 "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers")
206 echo -n "${PROJECT_NAME}/infra-controller-profiles/${PROFILE_NAME}"
210 "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs")
211 echo -n "${PROJECT_NAME}/infra-config-profiles/${PROFILE_NAME}"
215 "managed" | "resources" | "managed-resources" | "managed_resources")
216 echo -n "${PROJECT_NAME}/managed-resources/${PROFILE_NAME}"
220 "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs")
221 echo -n "${PROJECT_NAME}/app-profiles/${PROFILE_NAME}"
226 echo -n "------------ ERROR ------------"
233 # Function to create a new profile
234 function create_profile() {
235 local PROFILE_NAME="$1"
236 local PROFILE_TYPE="$2"
237 local PROJECT_NAME="${3:-"${MGMT_PROJECT_NAME}"}"
238 local FLEET_REPO_URL="${4:-"${FLEET_REPO_URL}"}"
239 local FLEET_REPO_DIR="${5:-"${FLEET_REPO_DIR}"}"
241 local TARGET_PROFILE_PATH="$(
248 # Generate profile as `ResourceList` and render to target folder.
250 generator_profile_folder \
251 "${PROFILE_NAME}-${PROFILE_TYPE}" \
252 "${TARGET_PROFILE_PATH}" \
254 "${FLEET_REPO_URL}" \
256 list2folder_cp_over \
257 "${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
261 # Function to delete a profile
262 function delete_profile() {
263 local PROFILE_NAME="$1"
264 local PROFILE_TYPE="$2"
265 local PROJECT_NAME="${3:-"${MGMT_PROJECT_NAME}"}"
266 local FLEET_REPO_DIR="${4:-"${FLEET_REPO_DIR}"}"
268 local TARGET_PROFILE_PATH="$(
275 # Delete the profile folder
276 rm -rf "${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
280 # ----- BEGIN of Helper functions for remote cluster bootstrap -----
282 # Generate structure of profile folders prior to bootstrap
283 function generator_profile_folders_new_cluster() {
285 local PROFILE_NAME="$1"
286 local FLEET_REPO_URL="$2"
287 local PROJECT_NAME="${3:-"${MGMT_PROJECT_NAME}"}"
288 # Optional inputs: Paths for each profile in the Git repo
289 local INFRA_CONTROLLERS_PATH="${4:-"${PROJECT_NAME}/infra-controller-profiles/${PROFILE_NAME}"}"
290 local INFRA_CONFIGS_PATH="${5:-"${PROJECT_NAME}/infra-config-profiles/${PROFILE_NAME}"}"
291 local MANAGED_RESOURCES_PATH="${6:-"${PROJECT_NAME}/managed-resources/${PROFILE_NAME}"}"
292 local APPS_PATH="${7:-"${PROJECT_NAME}/app-profiles/${PROFILE_NAME}"}"
294 # Generate profiles as `ResourceList`. merging with inputs
299 generator_profile_folder \
300 "${PROFILE_NAME}-profile-infra-controllers" \
301 "${INFRA_CONTROLLERS_PATH}" \
302 "infra-controllers" \
303 "${FLEET_REPO_URL}" | \
304 generator_profile_folder \
305 "${PROFILE_NAME}-profile-infra-configs" \
306 "${INFRA_CONFIGS_PATH}" \
308 "${FLEET_REPO_URL}" | \
309 generator_profile_folder \
310 "${PROFILE_NAME}-profile-managed-resources" \
311 "${MANAGED_RESOURCES_PATH}" \
312 "managed-resources" \
313 "${FLEET_REPO_URL}" | \
314 generator_profile_folder \
315 "${PROFILE_NAME}-profile-apps" \
323 # Generate base Flux Kustomizations for the new cluster prior to bootstrap
324 function generator_base_kustomizations_new_cluster() {
325 local CLUSTER_KUSTOMIZATION_NAME="$1"
326 local FLEET_REPO_URL="$2"
327 local SW_CATALOGS_REPO_URL="$3"
328 local PROJECT_NAME="${4:-"${MGMT_PROJECT_NAME}"}"
329 local SW_CATALOGS_REPO_DIR="${5:-"${SW_CATALOGS_REPO_DIR}"}"
332 # Paths for each profile in the Git repo
333 local INFRA_CONTROLLERS_PATH="${6:-"${PROJECT_NAME}/infra-controller-profiles/${CLUSTER_KUSTOMIZATION_NAME}"}"
334 local INFRA_CONFIGS_PATH="${7:-"${PROJECT_NAME}/infra-config-profiles/${CLUSTER_KUSTOMIZATION_NAME}"}"
335 local MANAGED_RESOURCES_PATH="${8:-"${PROJECT_NAME}/managed-resources/${CLUSTER_KUSTOMIZATION_NAME}"}"
336 local APPS_PATH="${9:-"${PROJECT_NAME}/app-profiles/${CLUSTER_KUSTOMIZATION_NAME}"}"
338 # Path for the source templates
339 local TEMPLATES="${SW_CATALOGS_REPO_DIR}/cloud-resources/flux-remote-bootstrap/cluster-base/templates"
342 export CLUSTER_KUSTOMIZATION_NAME
343 export FLEET_REPO_URL
344 export SW_CATALOGS_REPO_URL
345 export INFRA_CONTROLLERS_PATH
346 export INFRA_CONFIGS_PATH
347 export MANAGED_RESOURCES_PATH
355 '${CLUSTER_KUSTOMIZATION_NAME},${FLEET_REPO_URL},${SW_CATALOGS_REPO_URL},${INFRA_CONTROLLERS_PATH},${INFRA_CONFIGS_PATH},${MANAGED_RESOURCES_PATH},${APPS_PATH}'
360 # Create SOPS configuration file for the root folder of the cluster
361 function create_sops_configuration_file_new_cluster() {
362 local PUBLIC_KEY="$1"
364 MANIFEST="creation_rules:
365 - encrypted_regex: ^(data|stringData)$
367 # - path_regex: .*.yaml
368 # encrypted_regex: ^(data|stringData)$
369 # age: ${PUBLIC_KEY}"
371 # Generate SOPS configuration file for the root folder
376 # Generate K8s secret for management cluster storing secret age key for the new cluster
377 function generator_k8s_age_secret_new_cluster() {
378 local PRIVATE_KEY_NEW_CLUSTER="$1"
379 local PUBLIC_KEY_MGMT="$2"
380 local CLUSTER_AGE_SECRET_NAME="${3:-$(safe_name "sops-age-${CLUSTER_KUSTOMIZATION_NAME}")}"
385 echo "${PRIVATE_KEY_NEW_CLUSTER}" | \
387 kubectl create secret generic "${CLUSTER_AGE_SECRET_NAME}" \
388 --namespace=managed-resources \
389 --from-file=agekey=/dev/stdin \
390 -o yaml --dry-run=client | \
391 encrypt_secret_from_stdin \
392 "${PUBLIC_KEY_MGMT}" |
394 set_filename_to_items "${CLUSTER_AGE_SECRET_NAME}.yaml"
399 # Generate bootstrap manifests for new cluster from the management cluster
400 function generator_bootstrap_new_cluster() {
401 local CLUSTER_NAME="$1"
402 local CLUSTER_KUSTOMIZATION_NAME="${2:$(safe_name ${CLUSTER_NAME})}"
403 local CLUSTER_AGE_SECRET_NAME="${3:-$(safe_name "sops-age-${CLUSTER_KUSTOMIZATION_NAME}")}"
404 local SW_CATALOGS_REPO_DIR="${4:-"${SW_CATALOGS_REPO_DIR}"}"
406 # Paths and names for the templates
407 local MANIFEST_FILENAME="${5:-"cluster-bootstrap-${CLUSTER_KUSTOMIZATION_NAME}.yaml"}"
408 local TEMPLATES="${6:-"${SW_CATALOGS_REPO_DIR}/cloud-resources/flux-remote-bootstrap/bootstrap/templates"}"
409 local TEMPLATE_MANIFEST_FILENAME="${7:-"remote-cluster-bootstrap.yaml"}"
412 export CLUSTER_KUSTOMIZATION_NAME
414 export CLUSTER_AGE_SECRET_NAME
421 rename_file_in_items \
422 "${TEMPLATE_MANIFEST_FILENAME}" \
423 "${MANIFEST_FILENAME}" | \
425 '${CLUSTER_KUSTOMIZATION_NAME},${CLUSTER_NAME},${CLUSTER_AGE_SECRET_NAME}'
430 # Auxiliary function to create kustomization manifests
431 function manifest_kustomization() {
434 local SOURCE_REPO="$3"
435 local MANIFESTS_PATH="$4"
436 local SOURCE_SYNC_INTERVAL="$5"
437 local HEALTH_CHECK_TO="$6"
438 local DEPENDS_ON="${7:-""}"
439 local OPTIONS="${8:-""}"
442 local OPTION_FOR_DEPENDS_ON="$(
443 if [[ -z "${DEPENDS_ON}" ]];
447 echo "--depends-on=${DEPENDS_ON}"
452 "${OPTION_FOR_DEPENDS_ON}" \
455 # Create Kustomization manifest
456 flux create kustomization "${KS_NAME}" \
457 --namespace="${KS_NS}" \
458 --source="${SOURCE_REPO}" \
459 --path="${MANIFESTS_PATH}" \
460 --interval="${SOURCE_SYNC_INTERVAL}" \
461 --health-check-timeout="${HEALTH_CHECK_TO}" \
466 # Helper function to generate a Kustomization
467 function generator_kustomization() {
468 local MANIFEST_FILENAME="$1"
469 local ALL_PARAMS=( "${@}" )
470 local PARAMS=( "${ALL_PARAMS[@]:1}" )
472 # Use manifest creator to become a generator
474 "${MANIFEST_FILENAME}" \
475 manifest_kustomization \
479 # ----- END of Helper functions for remote cluster bootstrap -----
482 # Create bootstrap for remote cluster
483 function create_bootstrap_for_remote_cluster() {
484 local CLUSTER_NAME="$1"
485 local CLUSTER_KUSTOMIZATION_NAME="$2"
486 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
487 local SW_CATALOGS_REPO_DIR="${4:-"${SW_CATALOGS_REPO_DIR}"}"
488 local FLEET_REPO_URL="${5:-""}"
489 local SW_CATALOGS_REPO_URL="${6:-""}"
490 local MGMT_PROJECT_NAME="${7:-${MGMT_PROJECT_NAME}}"
491 local PUBLIC_KEY_MGMT="${8:-"${PUBLIC_KEY_MGMT}"}"
492 local PUBLIC_KEY_NEW_CLUSTER="$9"
493 local PRIVATE_KEY_NEW_CLUSTER="${10:-${PRIVATE_KEY_NEW_CLUSTER}}"
495 # Calculates the folder where managed resources area defined
496 local MGMT_RESOURCES_DIR="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/_management"
499 # Create profile folders
501 generator_profile_folders_new_cluster \
502 "${CLUSTER_KUSTOMIZATION_NAME}" \
503 "${FLEET_REPO_URL}" \
504 "${MGMT_PROJECT_NAME}" | \
505 list2folder_cp_over \
508 # Create base Kustomizations for the new cluster
509 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
511 generator_base_kustomizations_new_cluster \
512 "${CLUSTER_KUSTOMIZATION_NAME}" \
513 "${FLEET_REPO_URL}" \
514 "${SW_CATALOGS_REPO_URL}" \
515 "${MGMT_PROJECT_NAME}" \
516 "${SW_CATALOGS_REPO_DIR}" | \
517 list2folder_cp_over \
520 # Add SOPS configuration at the root folder of the cluster
521 # NOTE: This file cannot be generated by pure KRM functions since it begins by a dot ('.')
522 create_sops_configuration_file_new_cluster \
523 "${PUBLIC_KEY_NEW_CLUSTER}" \
524 > "${CLUSTER_FOLDER}/.sops.yaml"
526 # Add also the public SOPS key to the repository so that others who clone the repo can encrypt new files
527 # NOTE: This file cannot be generated by pure KRM functions since it begins by a dot ('.')
528 echo "${PUBLIC_KEY_NEW_CLUSTER}" \
529 > "${CLUSTER_FOLDER}/.sops.pub.asc"
531 # Prepare everything to perform a Flux bootstrap of the new remote cluster from the management cluster.
532 # 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
533 local CLUSTER_AGE_SECRET_NAME=$(safe_name "sops-age-${CLUSTER_KUSTOMIZATION_NAME}")
535 generator_bootstrap_new_cluster \
537 "${CLUSTER_KUSTOMIZATION_NAME}" \
538 "${CLUSTER_AGE_SECRET_NAME}" \
539 "${SW_CATALOGS_REPO_DIR}" | \
540 generator_k8s_age_secret_new_cluster \
541 "${PRIVATE_KEY_NEW_CLUSTER}" \
542 "${PUBLIC_KEY_MGMT}" \
543 "${CLUSTER_AGE_SECRET_NAME}" | \
544 prepend_folder_path "${CLUSTER_KUSTOMIZATION_NAME}/" | \
545 list2folder_cp_over \
546 "${MGMT_RESOURCES_DIR}"
550 # Create remote CrossPlane cluster (generic for any cloud)
551 function create_crossplane_cluster() {
552 local CLUSTER_KUSTOMIZATION_NAME="$1"
553 local CLUSTER_NAME="$2"
554 # As of today, one among `aks`, `eks` or `gke`:
555 local CLUSTER_TYPE="$3"
556 local PROVIDERCONFIG_NAME="${4:-default}"
558 local NODE_COUNT="$6"
559 local CLUSTER_LOCATION="$7"
560 local K8S_VERSION="${8:-"'1.28'"}"
561 local PUBLIC_KEY_MGMT="${9:-"${PUBLIC_KEY_MGMT}"}"
562 local PUBLIC_KEY_NEW_CLUSTER="${10}"
563 local PRIVATE_KEY_NEW_CLUSTER="${11:-"${PRIVATE_KEY_NEW_CLUSTER}"}"
565 local AKS_RG_NAME="${12:-""}"
567 local GKE_PREEMPTIBLE_NODES="${13:-""}"
568 ## `FLEET_REPO_DIR` is the result of:
569 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
570 local FLEET_REPO_DIR="${14:-"${FLEET_REPO_DIR}"}"
571 local FLEET_REPO_URL="${15:-""}"
572 ## `SW_CATALOGS_REPO_DIR` is the result of:
573 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
574 local SW_CATALOGS_REPO_DIR="${16:-"${SW_CATALOGS_REPO_DIR}"}"
575 local SW_CATALOGS_REPO_URL="${17:-""}"
576 # Perform bootstrap unless asked otherwise
577 local SKIP_BOOTSTRAP="${18:"false"}"
578 # Only change if absolutely needeed
579 local MGMT_PROJECT_NAME="${19:-"osm_admin"}"
580 local MGMT_CLUSTER_NAME="${20:-"_management"}"
581 local BASE_TEMPLATES_PATH="${21:-"cloud-resources"}"
582 local TEMPLATE_MANIFEST_FILENAME="${22:-"${CLUSTER_TYPE,,}01.yaml"}"
583 local MANIFEST_FILENAME="${23:-"${CLUSTER_TYPE,,}-${CLUSTER_NAME}.yaml"}"
586 # Is the provider type supported?
587 local VALID_PROVIDERS=("eks" "aks" "gke")
588 CLUSTER_TYPE="${CLUSTER_TYPE,,}"
589 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${CLUSTER_TYPE}")) ]] && return 1
591 # Determines the source dir for the templates and the target folder in Fleet
592 local TEMPLATES_DIR="${SW_CATALOGS_REPO_DIR}/${BASE_TEMPLATES_PATH}/${CLUSTER_TYPE}/templates"
593 local TARGET_FOLDER="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}"
595 # Determine which optional steps may be needed
596 local IS_AKS=$([[ "${CLUSTER_TYPE}" == "aks" ]]; echo $?)
597 local IS_GCP=$([[ "${CLUSTER_TYPE}" == "gcp" ]]; echo $?)
599 # Pipeline of transformations to create the cluster resource
600 export CLUSTER_KUSTOMIZATION_NAME
602 "${TEMPLATES_DIR}" | \
604 '${CLUSTER_KUSTOMIZATION_NAME}' | \
606 ".spec.postBuild.substitute.cluster_name" \
608 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
610 ".spec.postBuild.substitute.vm_size" \
612 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
614 ".spec.postBuild.substitute.node_count" \
616 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
618 ".spec.postBuild.substitute.cluster_location" \
619 "${CLUSTER_LOCATION}" \
620 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
622 ".spec.postBuild.substitute.k8s_version" \
624 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
626 ".spec.postBuild.substitute.providerconfig_name" \
627 "${PROVIDERCONFIG_NAME}" \
628 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
632 ".spec.postBuild.substitute.rg_name" \
634 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
638 ".spec.postBuild.substitute.preemptible_nodes" \
639 "${GKE_PREEMPTIBLE_NODES}" \
640 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
641 rename_file_in_items \
642 "${TEMPLATE_MANIFEST_FILENAME}" \
643 "${MANIFEST_FILENAME}" | \
644 prepend_folder_path "${CLUSTER_KUSTOMIZATION_NAME}/" | \
645 list2folder_cp_over \
648 # Bootstrap (unless asked to skip)
649 if [[ "${SKIP_BOOTSTRAP,,}" == "true" ]]; then
652 create_bootstrap_for_remote_cluster \
654 "${CLUSTER_KUSTOMIZATION_NAME}" \
655 "${FLEET_REPO_DIR}" \
656 "${SW_CATALOGS_REPO_DIR}" \
657 "${FLEET_REPO_URL}" \
658 "${SW_CATALOGS_REPO_URL}" \
659 "${MGMT_PROJECT_NAME}" \
660 "${PUBLIC_KEY_MGMT}" \
661 "${PUBLIC_KEY_NEW_CLUSTER}" \
662 "${PRIVATE_KEY_NEW_CLUSTER}"
666 # Delete remote cluster (generic for any cloud)
667 function delete_remote_cluster() {
668 local CLUSTER_KUSTOMIZATION_NAME="$1"
669 local PROJECT_NAME="${2:-"${MGMT_PROJECT_NAME}"}"
670 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
671 local MGMT_RESOURCES_DIR="${4:-"${MGMT_RESOURCES_DIR}"}"
673 # Optional inputs: Paths for each profile in the Git repo
674 local INFRA_CONTROLLERS_DIR="${5:-"${FLEET_REPO_DIR}/${PROJECT_NAME}/infra-controller-profiles/${CLUSTER_KUSTOMIZATION_NAME}"}"
675 local INFRA_CONFIGS_DIR="${6:-"${FLEET_REPO_DIR}/${PROJECT_NAME}/infra-config-profiles/${CLUSTER_KUSTOMIZATION_NAME}"}"
676 local MANAGED_RESOURCES_DIR="${7:-"${FLEET_REPO_DIR}/${PROJECT_NAME}/managed-resources/${CLUSTER_KUSTOMIZATION_NAME}"}"
677 local APPS_DIR="${8:-"${FLEET_REPO_DIR}/${PROJECT_NAME}/app-profiles/${CLUSTER_KUSTOMIZATION_NAME}"}"
678 local CLUSTER_DIR="${9:-"${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"}"
680 # Delete profile folders
681 rm -rf "${INFRA_CONTROLLERS_DIR}"
682 rm -rf "${INFRA_CONFIGS_DIR}"
683 rm -rf "${MANAGED_RESOURCES_DIR}"
686 # Delete base cluster Kustomizations
687 rm -rf "${CLUSTER_DIR}"
689 # Delete cluster resources
690 rm -rf "${MGMT_RESOURCES_DIR}/${CLUSTER_KUSTOMIZATION_NAME}"
694 # Update remote CrossPlane cluster (generic for any cloud)
695 function update_crossplane_cluster() {
696 local CLUSTER_KUSTOMIZATION_NAME="$1"
697 local CLUSTER_NAME="$2"
698 # As of today, one among `aks`, `eks` or `gke`:
699 local CLUSTER_TYPE="$3"
700 local PROVIDERCONFIG_NAME="${4:-default}"
702 local NODE_COUNT="$6"
703 local CLUSTER_LOCATION="$7"
704 local K8S_VERSION="${8:-"'1.28'"}"
705 local PUBLIC_KEY_MGMT="${9:-"${PUBLIC_KEY_MGMT}"}"
706 local PUBLIC_KEY_NEW_CLUSTER="${10}"
707 local PRIVATE_KEY_NEW_CLUSTER="${11:-"${PRIVATE_KEY_NEW_CLUSTER}"}"
709 local AKS_RG_NAME="${12:-""}"
711 local GKE_PREEMPTIBLE_NODES="${13:-""}"
712 ## `FLEET_REPO_DIR` is the result of:
713 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
714 local FLEET_REPO_DIR="${14:-"${FLEET_REPO_DIR}"}"
715 local FLEET_REPO_URL="${15:-""}"
716 ## `SW_CATALOGS_REPO_DIR` is the result of:
717 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
718 local SW_CATALOGS_REPO_DIR="${16:-"${SW_CATALOGS_REPO_DIR}"}"
719 local SW_CATALOGS_REPO_URL="${17:-""}"
720 # Prevent a new bootstrap by default
721 local SKIP_BOOTSTRAP="${18:"true"}"
722 # Only change if absolutely needeed
723 local MGMT_PROJECT_NAME="${19:-"osm_admin"}"
724 local MGMT_CLUSTER_NAME="${20:-"_management"}"
725 local BASE_TEMPLATES_PATH="${21:-"cloud-resources"}"
726 local TEMPLATE_MANIFEST_FILENAME="${22:-"${CLUSTER_TYPE,,}01.yaml"}"
727 local MANIFEST_FILENAME="${23:-"${CLUSTER_TYPE,,}-${CLUSTER_NAME}.yaml"}"
730 # Is the provider type supported?
731 local VALID_PROVIDERS=("eks" "aks" "gke")
732 CLUSTER_TYPE="${CLUSTER_TYPE,,}"
733 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${CLUSTER_TYPE}")) ]] && return 1
735 # Determine key folders in Fleet
736 local MGMT_RESOURCES_DIR="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}"
738 # First, delete cluster's CrossPlane resources
739 # NOTE: We only delete de Kustomization referring to CrossPlane resources,
740 # not the bootstrap resources or the profiles. Thus we avoid that KSUs are
741 # affected or a potential second unnecesary bootstrap.
742 rm -rf "${MGMT_RESOURCES_DIR}/${CLUSTER_KUSTOMIZATION_NAME}/${MANIFEST_FILENAME}"
744 # Then, recreate the manifests with updated values
745 create_crossplane_cluster \
746 "${CLUSTER_KUSTOMIZATION_NAME}" \
749 "${PROVIDERCONFIG_NAME}" \
752 "${CLUSTER_LOCATION}" \
754 "${PUBLIC_KEY_MGMT}" \
755 "${PUBLIC_KEY_NEW_CLUSTER}" \
756 "${PRIVATE_KEY_NEW_CLUSTER}" \
758 "${GKE_PREEMPTIBLE_NODES}" \
759 "${FLEET_REPO_DIR}" \
760 "${FLEET_REPO_URL}" \
761 "${SW_CATALOGS_REPO_DIR}" \
762 "${SW_CATALOGS_REPO_URL}" \
763 "${SKIP_BOOTSTRAP}" \
764 "${MGMT_PROJECT_NAME}" \
765 "${MGMT_CLUSTER_NAME}" \
766 "${BASE_TEMPLATES_PATH}" \
767 "${TEMPLATE_MANIFEST_FILENAME}" \
768 "${MANIFEST_FILENAME}"
772 # ----- Helper functions for adding/removing a profile from a cluster -----
774 # Helper function to find profiles of a given type already used in the cluster
775 function profiles_of_type_in_cluster() {
776 local CLUSTER_KUSTOMIZATION_NAME="$1"
777 local RELEVANT_PROFILE_TYPE="$2"
778 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
781 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
783 # Processing (echoes the list)
785 "${CLUSTER_FOLDER}" | \
786 get_value_from_resourcelist \
788 "| select(.kind == \"Kustomization\")
789 | select(.metadata.labels.osm_profile_type == \"${RELEVANT_PROFILE_TYPE}\")" | \
794 # Function to list the profiles **this profile depends on**
795 function profiles_this_one_depends_on() {
796 local CLUSTER_KUSTOMIZATION_NAME="$1"
797 local PROFILE_TYPE="$2"
798 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
800 case "${PROFILE_TYPE,,}" in
802 "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers")
803 # Controllers do not depend on any other type of profiles
808 "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs")
809 # Infra configs depend on controllers
810 profiles_of_type_in_cluster \
811 "${CLUSTER_KUSTOMIZATION_NAME}" \
812 "infra-controllers" \
817 "managed" | "resources" | "managed-resources" | "managed_resources")
818 # Managed resources depend on infra configs
819 profiles_of_type_in_cluster \
820 "${CLUSTER_KUSTOMIZATION_NAME}" \
826 "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs")
827 # Apps (also) depend on infra configs
828 profiles_of_type_in_cluster \
829 "${CLUSTER_KUSTOMIZATION_NAME}" \
836 echo -n "------------ ERROR ------------"
843 # Function to list the profiles that **depend on this profile**
844 function profiles_depend_on_this_one() {
845 local CLUSTER_KUSTOMIZATION_NAME="$1"
846 local PROFILE_TYPE="$2"
847 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
849 case "${PROFILE_TYPE,,}" in
851 "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers")
852 # Infra configs depend on infra controllers
853 profiles_of_type_in_cluster \
854 "${CLUSTER_KUSTOMIZATION_NAME}" \
860 "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs")
861 # Both managed resources and apps depend on configs
864 profiles_of_type_in_cluster \
865 "${CLUSTER_KUSTOMIZATION_NAME}" \
866 "managed-resources" \
870 profiles_of_type_in_cluster \
871 "${CLUSTER_KUSTOMIZATION_NAME}" \
876 printf '%s,' "${PROFILES[@]}" | sed 's/,$//g'
880 "managed" | "resources" | "managed-resources" | "managed_resources")
881 # No other profiles depend on managed resources
886 "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs")
887 # No other profiles depend on apps
893 echo -n "------------ ERROR ------------"
900 # Helper function to add a dependency to a Kustomization only if it does not exist already
901 function add_dependency_to_kustomization_safely() {
902 local KUSTOMIZATION_NAME="$1"
903 local KUSTOMIZATION_TO_ADD_AS_DEP="$2"
906 local FILTER="| select(.kind == \"Kustomization\") | select(.metadata.name == \"${KUSTOMIZATION_NAME}\")"
908 # Check if the dependency was added already
912 ".spec.dependsOn[].name" \
913 "${KUSTOMIZATION_TO_ADD_AS_DEP}" \
917 # If it existed already, returns the stream as is
918 if [[ "${TEST_RESULT}" == "true" ]]
921 # Otherwise, processes the stream to add it
926 "{name: ${KUSTOMIZATION_TO_ADD_AS_DEP}}" \
932 # Helper function to remove a dependency from a Kustomization
933 function remove_dependency_from_kustomization_safely() {
934 local KUSTOMIZATION_NAME="$1"
935 local KUSTOMIZATION_TO_REMOVE_AS_DEP="$2"
938 local KEY_PATH=".spec.dependsOn[] | select(.name == \"${KUSTOMIZATION_TO_REMOVE_AS_DEP}\")"
939 local FILTER="| select(.kind == \"Kustomization\") | select(.metadata.name == \"${KUSTOMIZATION_NAME}\")"
941 # Remove the entry from the dependency list (if it exists)
942 yq "del((.items[]${FILTER})${KEY_PATH})"
946 # Ensure list of Kustomizations depend on a given Kustomization
947 function add_dependency_to_set_of_kustomizations_safely() {
949 local THEY_DEPEND_ON_THIS="$2"
954 # For each of the Kustomizations on the comma-separated list, adds `KS_NAME` as one of their dependencies
955 for KUST in ${THEY_DEPEND_ON_THIS//,/ }
959 add_dependency_to_kustomization_safely \
963 local INPUT="${OUTPUT}"
966 # Return the final `ResultList`, after all iterations
971 # Ensure list of Kustomizations no longer depend on a given Kustomization
972 function remove_dependency_from_set_of_kustomizations_safely() {
974 local THEY_NO_LONGER_DEPEND_ON_THIS="$2"
979 # For each of the Kustomizations on the comma-separated list, removes `KS_NAME` from their dependencies
980 for KUST in ${THEY_NO_LONGER_DEPEND_ON_THIS//,/ }
984 remove_dependency_from_kustomization_safely \
988 local INPUT="${OUTPUT}"
991 # Return the final `ResultList`, after all iterations
995 # ----- END of Helper functions for adding/removing a profile from a cluster -----
998 # Add an existing profile to a cluster
999 function attach_profile_to_cluster() {
1000 local PROFILE_NAME="$1"
1001 local PROFILE_TYPE="$2"
1002 local PROJECT_NAME="$3"
1003 local CLUSTER_KUSTOMIZATION_NAME="$4"
1004 local FLEET_REPO_DIR="${5:-"${FLEET_REPO_DIR}"}"
1007 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
1008 local TARGET_PROFILE_PATH="$(
1015 # Finds out which profiles it should depend on... and which profiles should depend on it
1017 profiles_this_one_depends_on \
1018 "${CLUSTER_KUSTOMIZATION_NAME}" \
1023 local THEY_DEPEND_ON_THIS=$(
1024 profiles_depend_on_this_one \
1025 "${CLUSTER_KUSTOMIZATION_NAME}" \
1030 # Parameters for the new Kustomization object to point to the profile
1031 local KS_NAME="$(safe_name "${PROFILE_TYPE}-${PROFILE_NAME}")"
1032 local MANIFEST_FILENAME="${KS_NAME}.yaml"
1033 local KS_NS=flux-system
1034 local MANIFESTS_PATH="${TARGET_PROFILE_PATH}"
1035 local SOURCE_REPO=GitRepository/fleet-repo.flux-system
1036 local SOURCE_SYNC_INTERVAL="60m"
1037 local HEALTH_CHECK_TO="3m"
1038 local RETRY_INTERVAL="1m"
1041 --decryption-provider=sops \
1042 --decryption-secret=sops-age \
1044 --timeout="${TIMEOUT}" \
1045 --retry-interval="${RETRY_INTERVAL}" \
1046 --label osm_profile_type="${PROFILE_TYPE}"
1049 # Finally, we update the folder with all the required changes:
1050 # - Update pre-existing Kustomizations that should depend on the new profile (besides others).
1051 # - Create a new Kustomization pointing to the profile.
1052 # - Update Kustomize's `kustomization.yaml` at the root of the cluster folder to take into account the new Kustomization pointing to the profile.
1053 # - Update the cluster folder accordingly.
1055 "${CLUSTER_FOLDER}" |
1056 add_dependency_to_set_of_kustomizations_safely \
1058 "${THEY_DEPEND_ON_THIS}" | \
1059 generator_kustomization \
1060 "${MANIFEST_FILENAME}" \
1064 "${MANIFESTS_PATH}" \
1065 "${SOURCE_SYNC_INTERVAL}" \
1066 "${HEALTH_CHECK_TO}" \
1071 "${MANIFEST_FILENAME}" \
1072 "| select(.kind == \"Kustomization\") | select(.apiVersion == \"kustomize.config.k8s.io/v1beta1\") | select(.metadata.annotations.\"config.kubernetes.io/path\" == \"kustomization.yaml\")" | \
1073 list2folder_sync_replace \
1078 # Remove an existing profile from a cluster
1079 function detach_profile_from_cluster() {
1080 local PROFILE_NAME="$1"
1081 local PROFILE_TYPE="$2"
1082 local PROJECT_NAME="$3"
1083 local CLUSTER_KUSTOMIZATION_NAME="$4"
1084 local FLEET_REPO_DIR="${5:-"${FLEET_REPO_DIR}"}"
1087 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
1088 local TARGET_PROFILE_PATH="$(
1095 # Finds out which profiles still depend on it
1096 local THEY_DEPEND_ON_THIS=$(
1097 profiles_depend_on_this_one \
1098 "${CLUSTER_KUSTOMIZATION_NAME}" \
1103 # Parameters for the new Kustomization object to point to the profile
1104 local KS_NAME="$(safe_name "${PROFILE_TYPE}-${PROFILE_NAME}")"
1106 # Finally, we update the folder with all the required changes:
1107 # - Update pre-existing Kustomizations that should depend on the new profile (besides others).
1108 # - Create a new Kustomization pointing to the profile.
1109 # - 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.
1110 # - Update the cluster folder accordingly.
1112 "${CLUSTER_FOLDER}" |
1113 remove_dependency_from_set_of_kustomizations_safely \
1115 "${THEY_DEPEND_ON_THIS}" | \
1119 "kustomize.toolkit.fluxcd.io/v1" | \
1120 patch_delete_from_list \
1121 ".resources[] | select(. == \"${MANIFEST_FILENAME}\") " \
1122 "| select(.kind == \"Kustomization\") | select(.apiVersion == \"kustomize.config.k8s.io/v1beta1\") | select(.metadata.annotations.\"config.kubernetes.io/path\" == \"kustomization.yaml\")" | \
1123 list2folder_sync_replace \
1128 # Low-level function to add a KSU into a profile
1129 function create_ksu_into_profile() {
1131 local TARGET_PROFILE_FOLDER="$2"
1132 local TEMPLATES_PATH="$3"
1133 local SW_CATALOGS_REPO_DIR="$4"
1134 local TRANSFORMER="${5:-noop_transformer}"
1136 # Gathers all optional parameters for transformer funcion (if any) and puts them into an array for further use
1137 local ALL_PARAMS=( "${@}" )
1138 local TRANSFORMER_ARGS=( "${ALL_PARAMS[@]:5}" )
1140 # Composes the route to the local templates folder
1141 local TEMPLATES_FOLDER="${SW_CATALOGS_REPO_DIR}/${TEMPLATES_PATH}"
1144 "${TEMPLATES_FOLDER}" | \
1146 "${TRANSFORMER_ARGS[@]}" | \
1147 prepend_folder_path "${KSU_NAME}/" | \
1148 list2folder_cp_over \
1149 "${TARGET_PROFILE_FOLDER}"
1153 # Function to render a KSU from a `ResourceList` into a profile
1154 function render_ksu_into_profile() {
1156 local PROFILE_NAME="$2"
1157 local PROFILE_TYPE="$3"
1158 local PROJECT_NAME="${4:-"${MGMT_PROJECT_NAME}"}"
1159 local FLEET_REPO_DIR="$5"
1160 local SYNC="${6:-"false"}"
1162 local TARGET_PROFILE_PATH=$(
1169 local TARGET_PROFILE_FOLDER="${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
1171 # Determines the appropriate function depending on rendering strategy
1172 # - Sync (and potentially delete files in target folder)
1173 # - Copy over (only overwrite changed files, keep the rest)
1175 if [[ ${SYNC,,} == "true" ]];
1177 RENDERER="list2folder_sync_replace"
1179 RENDERER="list2folder_cp_over"
1182 # Render with the selected strategy
1183 [[ "${DRY_RUN,,}" != "true" ]] && mkdir -p "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
1185 "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
1186 ## This is improves the behaviour of the following code,
1187 ## since avoids unintented deletions in parent folder due to sync
1188 # prepend_folder_path "${KSU_NAME}/" | \
1190 # "${TARGET_PROFILE_FOLDER}"
1194 # High-level function to add a KSU into a profile for the case where
1195 # 1. It is originated from an OKA, and
1196 # 2. It is based on a HelmRelease.
1197 function create_hr_ksu_into_profile() {
1198 # Base KSU generation from template
1199 ## `TEMPLATES_DIR` is the result of:
1200 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}/{{inputs.parameters.templates_path}}"
1201 local TEMPLATES_DIR="$1"
1202 local SUBSTITUTE_ENVIRONMENT="${2:-"false"}"
1203 local SUBSTITUTION_FILTER="${3:-""}"
1204 local CUSTOM_ENV_VARS="${4:-""}"
1205 # Patch HelmRelease in KSU with inline values
1206 local KUSTOMIZATION_NAME="$5"
1207 local HELMRELEASE_NAME="$6"
1208 local INLINE_VALUES="${7:-""}"
1209 # Secret reference and generation (if required)
1210 local IS_PREEXISTING_SECRET="${8:-"false"}"
1211 local TARGET_NS="$9"
1212 local VALUES_SECRET_NAME="${10}"
1213 local SECRET_KEY="${11:-"values.yaml"}"
1214 local AGE_PUBLIC_KEY="${12}"
1215 ## `SECRET_VALUES` will be obtained from the
1216 ## secret named after the input parameter `reference_secret_for_values`,
1217 ## and from the key named after the input parameter `reference_key_for_values`
1218 local LOCAL_SECRET_VALUES="${13:-"${SECRET_VALUES}"}"
1219 # ConfigMap reference and generation (if required)
1220 local IS_PREEXISTING_CM="${14:-"false"}"
1221 local VALUES_CM_NAME="${15:-""}"
1222 local CM_KEY="${16:-""}"
1223 local CM_VALUES="${17:-""}"
1225 local KSU_NAME="${18}"
1226 local PROFILE_NAME="${19}"
1227 local PROFILE_TYPE="${20}"
1228 local PROJECT_NAME="${21:-"osm_admin"}"
1229 ## `FLEET_REPO_DIR` is the result of:
1230 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1231 local FLEET_REPO_DIR="${22:-"/fleet/fleet-osm/"}"
1232 local SYNC="${23:-"true"}"
1234 # Decides which steps may be skipped
1235 HAS_INLINE_VALUES=$([[ -n "${INLINE_VALUES}" ]]; echo $?)
1236 HAS_REFERENCES=$([[ ( -n "${VALUES_SECRET_NAME}" ) || ( -n "${VALUES_CM_NAME}" ) ]]; echo $?)
1237 NEEDS_NEW_SECRET=$([[ ( -n "${VALUES_SECRET_NAME}" ) && ( "${IS_PREEXISTING_SECRET,,}" == "false" ) ]]; echo $?)
1238 NEEDS_NEW_CM=$([[ ( -n "${VALUES_CM_NAME}" ) && ( "${IS_PREEXISTING_CM,,}" == "false" ) ]]; echo $?)
1239 ECHO_RESOURCELIST=$([[ "${DEBUG,,}" == "true" ]]; echo $?)
1241 # If applicable, loads additional environment variables
1242 if [[ -n "${CUSTOM_ENV_VARS}" ]];
1245 source <(echo "${CUSTOM_ENV_VARS}")
1250 folder2list_generator \
1251 "${TEMPLATES_DIR}" \
1252 "${SUBSTITUTE_ENVIRONMENT}" \
1253 "${SUBSTITUTION_FILTER}" | \
1255 "${HAS_INLINE_VALUES}" \
1256 add_values_to_helmrelease_via_ks \
1257 "${KUSTOMIZATION_NAME}" \
1258 "${HELMRELEASE_NAME}" \
1259 "${INLINE_VALUES}" | \
1261 "${HAS_REFERENCES}" \
1262 add_ref_values_to_hr_via_ks \
1263 "${KUSTOMIZATION_NAME}" \
1264 "${HELMRELEASE_NAME}" \
1265 "${VALUES_SECRET_NAME}" \
1266 "${VALUES_CM_NAME}" | \
1268 "${NEEDS_NEW_SECRET}" \
1270 "hr-values-secret.yaml" \
1272 "${AGE_PUBLIC_KEY}" \
1276 "${VALUES_SECRET_NAME}" \
1277 --namespace="${TARGET_NS}" \
1278 --from-file="${SECRET_KEY}"=<(echo "${LOCAL_SECRET_VALUES}") \
1280 --dry-run=client | \
1284 "hr-values-configmap.yaml" \
1288 "${VALUES_CM_NAME}" \
1289 --namespace="${TARGET_NS}" \
1290 --from-file="${SECRET_KEY}"=<(echo "${CM_VALUES}") \
1292 --dry-run=client | \
1294 "${ECHO_RESOURCELIST}" \
1296 render_ksu_into_profile \
1301 "${FLEET_REPO_DIR}" \
1306 # High-level function to update a KSU for the case where
1307 # 1. It is originated from an OKA, and
1308 # 2. It is based on a HelmRelease.
1309 # NOTE: It is an alias of `create_hr_ksu_into_profile`, setting `sync` to true
1310 function update_hr_ksu_into_profile() {
1311 # Base KSU generation from template
1312 ## `TEMPLATES_DIR` is the result of:
1313 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}/{{inputs.parameters.templates_path}}"
1314 local TEMPLATES_DIR="$1"
1315 local SUBSTITUTE_ENVIRONMENT="${2:-"false"}"
1316 local SUBSTITUTION_FILTER="${3:-""}"
1317 local CUSTOM_ENV_VARS="${4:-""}"
1318 # Patch HelmRelease in KSU with inline values
1319 local KUSTOMIZATION_NAME="$5"
1320 local HELMRELEASE_NAME="$6"
1321 local INLINE_VALUES="${7:-""}"
1322 # Secret reference and generation (if required)
1323 local IS_PREEXISTING_SECRET="${8:-"false"}"
1324 local TARGET_NS="$9"
1325 local VALUES_SECRET_NAME="${10}"
1326 local SECRET_KEY="${11:-"values.yaml"}"
1327 local AGE_PUBLIC_KEY="${12}"
1328 ## `SECRET_VALUES` will be obtained from the
1329 ## secret named after the input parameter `reference_secret_for_values`,
1330 ## and from the key named after the input parameter `reference_key_for_values`
1331 local LOCAL_SECRET_VALUES="${13:-"${SECRET_VALUES}"}"
1332 # ConfigMap reference and generation (if required)
1333 local IS_PREEXISTING_CM="${14:-"false"}"
1334 local VALUES_CM_NAME="${15:-""}"
1335 local CM_KEY="${16:-""}"
1336 local CM_VALUES="${17:-""}"
1338 local KSU_NAME="${18}"
1339 local PROFILE_NAME="${19}"
1340 local PROFILE_TYPE="${20}"
1341 local PROJECT_NAME="${21:-"osm_admin"}"
1342 ## `FLEET_REPO_DIR` is the result of:
1343 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1344 local FLEET_REPO_DIR="${22:-"/fleet/fleet-osm/"}"
1345 # local SYNC="${23:-"true"}"
1348 # This function is just an alias of `create_hr_ksu_into_profile`
1349 # forcing synchronization over the KSU folder
1350 create_hr_ksu_into_profile \
1351 "${TEMPLATES_DIR}" \
1352 "${SUBSTITUTE_ENVIRONMENT}" \
1353 "${SUBSTITUTION_FILTER}" \
1354 "${CUSTOM_ENV_VARS}" \
1355 "${KUSTOMIZATION_NAME}" \
1356 "${HELMRELEASE_NAME}" \
1357 "${INLINE_VALUES}" \
1358 "${IS_PREEXISTING_SECRET}" \
1360 "${VALUES_SECRET_NAME}" \
1362 "${AGE_PUBLIC_KEY}" \
1363 "${LOCAL_SECRET_VALUES}" \
1364 "${IS_PREEXISTING_CM}" \
1365 "${VALUES_CM_NAME}" \
1372 "${FLEET_REPO_DIR}" \
1377 # High-level function to create a "generated" KSU into a profile when:
1378 # 1. There is no template (OKA) available.
1379 # 2. The SW is based on a Helm Chart that we want to deploy.
1380 function create_generated_ksu_from_helm_into_profile() {
1381 # HelmRelease generation
1382 local HELMRELEASE_NAME="$1"
1383 local CHART_NAME="$2"
1384 local CHART_VERSION="$3"
1385 local TARGET_NS="$4"
1386 local CREATE_NS="${5:-"true"}"
1387 # Repo source generation
1388 local IS_PREEXISTING_REPO="${6:-"false"}"
1389 local HELMREPO_NAME="$7"
1390 local HELMREPO_URL="${8:-""}"
1391 local HELMREPO_NS="${9:-"${TARGET_NS}"}"
1392 local HELMREPO_SECRET_REF="${10:-""}"
1393 # HelmRelease inline values (if any)
1394 local INLINE_VALUES="${11:-""}"
1395 # Secret reference and generation (if required)
1396 local IS_PREEXISTING_SECRET="${12:-"false"}"
1397 local VALUES_SECRET_NAME="${13}"
1398 local SECRET_KEY="${14:-"values.yaml"}"
1399 local AGE_PUBLIC_KEY="${15}"
1400 ## `SECRET_VALUES` will be obtained from the
1401 ## secret named after the input parameter `reference_secret_for_values`,
1402 ## and from the key named after the input parameter `reference_key_for_values`
1403 local LOCAL_SECRET_VALUES="${16:-"${SECRET_VALUES}"}"
1404 # ConfigMap reference and generation (if required)
1405 local IS_PREEXISTING_CM="${17:-"false"}"
1406 local VALUES_CM_NAME="${18:-""}"
1407 local CM_KEY="${19:-""}"
1408 local CM_VALUES="${20:-""}"
1410 local KSU_NAME="${21}"
1411 local PROFILE_NAME="${22}"
1412 local PROFILE_TYPE="${23}"
1413 local PROJECT_NAME="${24:-"osm_admin"}"
1414 ## `FLEET_REPO_DIR` is the result of:
1415 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1416 local FLEET_REPO_DIR="${25:-"/fleet/fleet-osm/"}"
1417 # By default, it will not syncronize, so that we can easily accumulate more than
1418 # one Helm chart into the same KSU if desired
1419 local SYNC="${26:-"false"}"
1421 # Decides which steps may be skipped
1422 local NEEDS_NEW_NS=$([[ "${CREATE_NS,,}" == "true" ]]; echo $?)
1423 local NEEDS_NEW_REPO_SOURCE=$([[ "${IS_PREEXISTING_REPO,,}" == "false" ]]; echo $?)
1424 local NEEDS_NEW_SECRET=$([[ ( -n "${VALUES_SECRET_NAME}" ) && ( "${IS_PREEXISTING_SECRET,,}" == "false" ) ]]; echo $?)
1425 local NEEDS_NEW_CM=$([[ ( -n "${VALUES_CM_NAME}" ) && ( "${IS_PREEXISTING_CM,,}" == "false" ) ]]; echo $?)
1426 local ECHO_RESOURCELIST=$([[ "${DEBUG,,}" == "true" ]]; echo $?)
1428 # Determine extra options for HelmRelease creation and define full command
1429 OPTION_CHART_VERSION=""
1430 [[ -n "${CHART_VERSION}" ]] && OPTION_CHART_VERSION='--chart-version=${CHART_VERSION}'
1431 OPTION_INLINE_VALUES=""
1432 [[ -n "${INLINE_VALUES}" ]] && OPTION_INLINE_VALUES='--values=<(
1433 echo "${INLINE_VALUES}"
1435 OPTION_REFERENCE_SECRET=""
1436 [[ -n "${VALUES_SECRET_NAME}" ]] && OPTION_REFERENCE_SECRET='--values-from=Secret/${VALUES_SECRET_NAME}'
1437 OPTION_REFERENCE_CM=""
1438 [[ -n "${VALUES_CM_NAME}" ]] && OPTION_REFERENCE_CM='--values-from=ConfigMap/${VALUES_CM_NAME}'
1440 export HR_COMMAND="\
1443 create hr "${HELMRELEASE_NAME}" \
1444 --chart="${CHART_NAME}" \
1445 --source=HelmRepository/"${HELMREPO_NAME}.${HELMREPO_NS}" \
1446 "${OPTION_CHART_VERSION}" \
1447 "${OPTION_INLINE_VALUES}" \
1448 "${OPTION_REFERENCE_SECRET}" \
1449 "${OPTION_REFERENCE_CM}" \
1453 # Determine extra options for Helm source repo creation and define full command
1454 OPTION_REPO_SECRET=""
1455 [[ -n "${HELMREPO_SECRET_REF}" ]] && OPTION_REPO_SECRET='--secret-ref=${HELMREPO_SECRET_REF}'
1457 export REPO_COMMAND="\
1459 -n "${HELMREPO_NS}" \
1460 create source helm "${HELMREPO_NAME}" \
1461 --url="${HELMREPO_URL}" \
1462 "${OPTION_REPO_SECRET}" \
1469 "helm-release.yaml" \
1470 eval "${HR_COMMAND}" | \
1480 --dry-run=client | \
1482 "${NEEDS_NEW_REPO_SOURCE}" \
1485 eval "${REPO_COMMAND}" | \
1487 "${NEEDS_NEW_SECRET}" \
1489 "hr-values-secret.yaml" \
1491 "${AGE_PUBLIC_KEY}" \
1495 "${VALUES_SECRET_NAME}" \
1496 --namespace="${TARGET_NS}" \
1497 --from-file="${SECRET_KEY}"=<(echo "${LOCAL_SECRET_VALUES}") \
1499 --dry-run=client | \
1503 "hr-values-configmap.yaml" \
1507 "${VALUES_CM_NAME}" \
1508 --namespace="${TARGET_NS}" \
1509 --from-file="${SECRET_KEY}"=<(echo "${CM_VALUES}") \
1511 --dry-run=client | \
1513 "${ECHO_RESOURCELIST}" \
1515 render_ksu_into_profile \
1520 "${FLEET_REPO_DIR}" \
1525 # High-level function to update a "generated" KSU:
1526 # 1. There is no template (OKA) available.
1527 # 2. The SW is based on a Helm Chart that we want to deploy.
1528 # NOTE: It is an alias of `create_generated_ksu_from_helm_into_profile`, setting `sync` to true
1529 function update_generated_ksu_from_helm_into_profile() {
1530 # HelmRelease generation
1531 local HELMRELEASE_NAME="$1"
1532 local CHART_NAME="$2"
1533 local CHART_VERSION="$3"
1534 local TARGET_NS="$4"
1535 local CREATE_NS="${5:-"true"}"
1536 # Repo source generation
1537 local IS_PREEXISTING_REPO="${6:-"false"}"
1538 local HELMREPO_NAME="$7"
1539 local HELMREPO_URL="${8:-""}"
1540 local HELMREPO_NS="${9:-"${TARGET_NS}"}"
1541 local HELMREPO_SECRET_REF="${10:-""}"
1542 # HelmRelease inline values (if any)
1543 local INLINE_VALUES="${11:-""}"
1544 # Secret reference and generation (if required)
1545 local IS_PREEXISTING_SECRET="${12:-"false"}"
1546 local VALUES_SECRET_NAME="${13}"
1547 local SECRET_KEY="${14:-"values.yaml"}"
1548 local AGE_PUBLIC_KEY="${15}"
1549 ## `SECRET_VALUES` will be obtained from the
1550 ## secret named after the input parameter `reference_secret_for_values`,
1551 ## and from the key named after the input parameter `reference_key_for_values`
1552 local LOCAL_SECRET_VALUES="${16:-"${SECRET_VALUES}"}"
1553 # ConfigMap reference and generation (if required)
1554 local IS_PREEXISTING_CM="${17:-"false"}"
1555 local VALUES_CM_NAME="${18:-""}"
1556 local CM_KEY="${19:-""}"
1557 local CM_VALUES="${20:-""}"
1559 local KSU_NAME="${21}"
1560 local PROFILE_NAME="${22}"
1561 local PROFILE_TYPE="${23}"
1562 local PROJECT_NAME="${24:-"osm_admin"}"
1563 ## `FLEET_REPO_DIR` is the result of:
1564 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1565 local FLEET_REPO_DIR="${25:-"/fleet/fleet-osm/"}"
1566 # By default, it will not syncronize, so that we can easily accumulate more than
1567 # one Helm chart into the same KSU if desired
1568 # local SYNC="${26:-"false"}"
1570 # Decides which steps may be skipped
1571 local NEEDS_NEW_NS=$([[ "${CREATE_NS,,}" == "true" ]]; echo $?)
1572 local NEEDS_NEW_REPO_SOURCE=$([[ "${IS_PREEXISTING_REPO,,}" == "false" ]]; echo $?)
1573 local NEEDS_NEW_SECRET=$([[ ( -n "${VALUES_SECRET_NAME}" ) && ( "${IS_PREEXISTING_SECRET,,}" == "false" ) ]]; echo $?)
1574 local NEEDS_NEW_CM=$([[ ( -n "${VALUES_CM_NAME}" ) && ( "${IS_PREEXISTING_CM,,}" == "false" ) ]]; echo $?)
1575 local ECHO_RESOURCELIST=$([[ "${DEBUG,,}" == "true" ]]; echo $?)
1578 # This function is just an alias of `create_generated_ksu_from_helm_into_profile`
1579 # forcing synchronization over the KSU folder
1580 create_generated_ksu_from_helm_into_profile \
1581 "${HELMRELEASE_NAME}" \
1583 "${CHART_VERSION}" \
1586 "${IS_PREEXISTING_REPO}" \
1587 "${HELMREPO_NAME}" \
1590 "${HELMREPO_SECRET_REF}" \
1591 "${INLINE_VALUES}" \
1592 "${IS_PREEXISTING_SECRET}" \
1593 "${VALUES_SECRET_NAME}" \
1595 "${AGE_PUBLIC_KEY}" \
1596 "${LOCAL_SECRET_VALUES}" \
1597 "${IS_PREEXISTING_CM}" \
1598 "${VALUES_CM_NAME}" \
1605 "${FLEET_REPO_DIR}" \
1610 # Low-level function to delete a KSU from a profile
1611 function delete_ksu_from_profile_path() {
1613 local TARGET_PROFILE_PATH="$2"
1614 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
1616 # Calculate profile folder
1617 TARGET_PROFILE_FOLDER="${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
1619 # Delete the KSU folder
1620 rm -rf "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
1624 # High-level function to delete a KSU from a profile
1625 function delete_ksu_from_profile() {
1627 local PROFILE_NAME="$2"
1628 local PROFILE_TYPE="$3"
1629 local PROJECT_NAME="${4:-"osm_admin"}"
1630 ## `FLEET_REPO_DIR` is the result of:
1631 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1632 local FLEET_REPO_DIR="$5"
1634 # Calculate profile folder
1635 local TARGET_PROFILE_PATH=$(
1641 TARGET_PROFILE_FOLDER="${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
1643 # Delete the KSU folder
1644 rm -rf "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
1648 # High-level function to clone a KSU from a profile to another
1649 function clone_ksu() {
1650 local SOURCE_KSU_NAME="$1"
1651 local SOURCE_PROFILE_NAME="$2"
1652 local SOURCE_PROFILE_TYPE="$3"
1653 local SOURCE_PROJECT_NAME="${4:-"osm_admin"}"
1654 local DESTINATION_KSU_NAME="${5:-"${SOURCE_KSU_NAME}"}"
1655 local DESTINATION_PROFILE_NAME="${6:-"${SOURCE_PROFILE_NAME}"}"
1656 local DESTINATION_PROFILE_TYPE="${7:-"${SOURCE_PROFILE_TYPE}"}"
1657 local DESTINATION_PROJECT_NAME="${8:-"${SOURCE_PROJECT_NAME}"}"
1658 ## `FLEET_REPO_DIR` is the result of:
1659 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1660 local FLEET_REPO_DIR="$9"
1663 # If source and destination are identical, aborts
1665 ("${SOURCE_KSU_NAME}" == "${DESTINATION_KSU_NAME}") && \
1666 ("${SOURCE_PROFILE_NAME}" == "${DESTINATION_PROFILE_NAME}") && \
1667 ("${SOURCE_PROFILE_TYPE}" == "${DESTINATION_PROFILE_TYPE}") && \
1668 ("${SOURCE_PROJECT_NAME}" == "${DESTINATION_PROJECT_NAME}") \
1674 # Calculate profile folders
1675 local SOURCE_PROFILE_PATH=$(
1677 "${SOURCE_PROFILE_NAME}" \
1678 "${SOURCE_PROFILE_TYPE}" \
1679 "${SOURCE_PROJECT_NAME}"
1681 local SOURCE_PROFILE_FOLDER="${FLEET_REPO_DIR}/${SOURCE_PROFILE_PATH}"
1682 local DESTINATION_PROFILE_PATH=$(
1684 "${DESTINATION_PROFILE_NAME}" \
1685 "${DESTINATION_PROFILE_TYPE}" \
1686 "${DESTINATION_PROJECT_NAME}"
1688 local DESTINATION_PROFILE_FOLDER="${FLEET_REPO_DIR}/${DESTINATION_PROFILE_PATH}"
1692 "${SOURCE_PROFILE_FOLDER}/${SOURCE_KSU_NAME}" \
1693 "${DESTINATION_PROFILE_FOLDER}/${DESTINATION_KSU_NAME}"
1697 # Create a `ProviderConfig` for a CrossPlane provider
1698 function create_crossplane_providerconfig() {
1699 local PROVIDERCONFIG_NAME="$1"
1700 # As of today, one among `azure`, `aws` or `gcp`:
1701 local PROVIDER_TYPE="$2"
1702 local CRED_SECRET_NAME="$3"
1703 local CRED_SECRET_KEY="${4:-"creds"}"
1704 local CRED_SECRET_NS="${5:-"crossplane-system"}"
1705 # If empty, it assumes the secret already exists
1706 local CRED_SECRET_CONTENT="${6:-"${CRED_SECRET_CONTENT:-""}"}"
1707 local AGE_PUBLIC_KEY_MGMT="$7"
1708 ## `FLEET_REPO_DIR` is the result of:
1709 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1710 local FLEET_REPO_DIR="${8:-"${FLEET_REPO_DIR}"}"
1711 ## `SW_CATALOGS_REPO_DIR` is the result of:
1712 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
1713 local SW_CATALOGS_REPO_DIR="${9:-"${SW_CATALOGS_REPO_DIR}"}"
1714 # Only when applicable
1715 local TARGET_GCP_PROJECT="${10:-""}"
1716 # Do not touch unless strictly needed
1717 local BASE_TEMPLATES_PATH="${11:-"infra-configs/crossplane/providers"}"
1718 local OSM_PROJECT_NAME="${12:-"osm_admin"}"
1719 local MGMT_CLUSTER_NAME="${13:-"_management"}"
1722 # Is the provider type supported?
1723 local VALID_PROVIDERS=("aws" "azure" "gcp")
1724 PROVIDER_TYPE="${PROVIDER_TYPE,,}"
1725 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${PROVIDER_TYPE}")) ]] && return 1
1727 # Determines the source dir for the templates and the target folder in Fleet
1728 local TEMPLATES_DIR="${SW_CATALOGS_REPO_DIR}/${BASE_TEMPLATES_PATH}/${PROVIDER_TYPE}/templates"
1729 local TARGET_FOLDER="${FLEET_REPO_DIR}/${OSM_PROJECT_NAME}/infra-config-profiles/${MGMT_CLUSTER_NAME}/crossplane-providerconfigs/${PROVIDER_TYPE}"
1731 # Determine which optional steps may be needed
1732 local NEEDS_NEW_SECRET=$([[ -n "${CRED_SECRET_CONTENT}" ]]; echo $?)
1733 local NEEDS_PROJECT_NAME=$([[ "${PROVIDER_TYPE}" == "gcp" ]]; echo $?)
1735 # Renders the `ProviderConfig` manifest and the encrypted secret (if applicable)
1737 folder2list_generator \
1738 "${TEMPLATES_DIR}" | \
1741 "${PROVIDERCONFIG_NAME}" \
1742 "| select(.kind == \"ProviderConfig\")" | \
1744 ".spec.credentials.secretRef.name" \
1745 "${CRED_SECRET_NAME}" \
1746 "| select(.kind == \"ProviderConfig\")" | \
1748 ".spec.credentials.secretRef.key" \
1749 "${CRED_SECRET_KEY}" \
1750 "| select(.kind == \"ProviderConfig\")" | \
1752 ".spec.credentials.secretRef.namespace" \
1753 "${CRED_SECRET_NS}" \
1754 "| select(.kind == \"ProviderConfig\")" | \
1756 "${NEEDS_PROJECT_NAME}" \
1759 "${TARGET_GCP_PROJECT}" \
1760 "| select(.kind == \"ProviderConfig\")" | \
1762 "${NEEDS_NEW_SECRET}" \
1764 "credentials-secret.yaml" \
1766 "${AGE_PUBLIC_KEY_MGMT}" \
1770 "${CRED_SECRET_NAME}" \
1771 --namespace="${CRED_SECRET_NS}" \
1772 --from-file="${CRED_SECRET_KEY}"=<(echo "${CRED_SECRET_CONTENT}") \
1774 --dry-run=client | \
1775 prepend_folder_path \
1776 "${PROVIDERCONFIG_NAME}/" | \
1777 list2folder_cp_over \
1782 # Delete a `ProviderConfig` for a CrossPlane provider
1783 function delete_crossplane_providerconfig() {
1784 local PROVIDERCONFIG_NAME="$1"
1785 # As of today, one among `azure`, `aws` or `gcp`:
1786 local PROVIDER_TYPE="$2"
1787 ## `FLEET_REPO_DIR` is the result of:
1788 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1789 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
1790 # Do not touch unless strictly needed
1791 local OSM_PROJECT_NAME="${4:-"osm_admin"}"
1792 local MGMT_CLUSTER_NAME="${5:-"_management"}"
1795 # Is the provider type supported?
1796 local VALID_PROVIDERS=("aws" "azure" "gcp")
1797 PROVIDER_TYPE="${PROVIDER_TYPE,,}"
1798 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${PROVIDER_TYPE}")) ]] && return 1
1800 # Determines the target folder in Fleet
1801 local PROVIDERCONFIG_FOLDER="${FLEET_REPO_DIR}/${OSM_PROJECT_NAME}/infra-config-profiles/${MGMT_CLUSTER_NAME}/crossplane-providerconfigs/${PROVIDER_TYPE}/${PROVIDERCONFIG_NAME}"
1804 rm -rf "${PROVIDERCONFIG_FOLDER}"
1808 # Update a `ProviderConfig` for a CrossPlane provider
1809 function update_crossplane_providerconfig() {
1810 local PROVIDERCONFIG_NAME="$1"
1811 # As of today, one among `azure`, `aws` or `gcp`:
1812 local PROVIDER_TYPE="$2"
1813 local CRED_SECRET_NAME="$3"
1814 local CRED_SECRET_KEY="${4:-"creds"}"
1815 local CRED_SECRET_NS="${5:-"crossplane-system"}"
1816 # If empty, it assumes the secret already exists
1817 local CRED_SECRET_CONTENT="${6:-"${CRED_SECRET_CONTENT:-""}"}"
1818 local AGE_PUBLIC_KEY_MGMT="$7"
1819 ## `FLEET_REPO_DIR` is the result of:
1820 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1821 local FLEET_REPO_DIR="${8:-"${FLEET_REPO_DIR}"}"
1822 ## `SW_CATALOGS_REPO_DIR` is the result of:
1823 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
1824 local SW_CATALOGS_REPO_DIR="${9:-"${SW_CATALOGS_REPO_DIR}"}"
1825 # Only when applicable
1826 local TARGET_GCP_PROJECT="${10:-""}"
1827 # Do not touch unless strictly needed
1828 local BASE_TEMPLATES_PATH="${11:-"infra-configs/crossplane/providers"}"
1829 local OSM_PROJECT_NAME="${12:-"osm_admin"}"
1830 local MGMT_CLUSTER_NAME="${13:-"_management"}"
1833 # Is the provider type supported?
1834 local VALID_PROVIDERS=("aws" "azure" "gcp")
1835 PROVIDER_TYPE="${PROVIDER_TYPE,,}"
1836 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${PROVIDER_TYPE}")) ]] && return 1
1838 # First, delete; then, re-create
1839 delete_crossplane_providerconfig \
1840 "${PROVIDERCONFIG_NAME}" \
1841 "${PROVIDER_TYPE}" \
1842 "${FLEET_REPO_DIR}" \
1843 "${OSM_PROJECT_NAME}" \
1844 "${MGMT_CLUSTER_NAME}"
1846 create_crossplane_providerconfig \
1847 "${PROVIDERCONFIG_NAME}" \
1848 "${PROVIDER_TYPE}" \
1849 "${CRED_SECRET_NAME}" \
1850 "${CRED_SECRET_KEY}" \
1851 "${CRED_SECRET_NS}" \
1852 "${CRED_SECRET_CONTENT}" \
1853 "${AGE_PUBLIC_KEY_MGMT}" \
1854 "${FLEET_REPO_DIR}" \
1855 "${SW_CATALOGS_REPO_DIR}" \
1856 "${TARGET_GCP_PROJECT}" \
1857 "${BASE_TEMPLATES_PATH}" \
1858 "${OSM_PROJECT_NAME}" \
1859 "${MGMT_CLUSTER_NAME}"
1863 # Helper function to return the relative path of a location in SW Catalogs for an OKA
1864 function path_to_catalog() {
1866 local PROJECT_NAME="${2:-"osm_admin"}"
1868 # Corrects `osm_admin` project, since it uses the root folder
1869 PROJECT_NAME="${PROJECT_NAME}"
1870 [[ "${PROJECT_NAME}" == "osm_admin" ]] && PROJECT_NAME="."
1872 # Echoes the relate path from the SW-Catalogs root
1873 case "${OKA_TYPE,,}" in
1875 "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers")
1876 echo -n "${PROJECT_NAME}/infra-controllers"
1880 "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs")
1881 echo -n "${PROJECT_NAME}/infra-configs"
1885 "managed" | "resources" | "managed-resources" | "managed_resources" | "cloud-resources" | "cloud_resources")
1886 echo -n "${PROJECT_NAME}/cloud-resources"
1890 "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs")
1891 echo -n "${PROJECT_NAME}/apps"
1896 echo -n "------------ ERROR ------------"
1903 # Create OKA of a specific kind
1904 function create_oka() {
1907 local PROJECT_NAME="${3:-"."}"
1908 ## `SW_CATALOGS_REPO_DIR` is the result of:
1909 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
1910 local SW_CATALOGS_REPO_DIR="$4"
1911 local OKA_LOCATION="${5:-"."}"
1912 local TARBALL_FILE="${6:-"true"}"
1915 # Finds the corresponding catalog path from the SW-Catalogs root
1916 # and create the destination
1917 local CATALOG_PATH=$(\
1922 local DESTINATION="${SW_CATALOGS_REPO_DIR}/${CATALOG_PATH}/${OKA_NAME}"
1923 mkdir -p "${DESTINATION}"
1925 # When the OKA comes as a `tar.gz`
1926 if [[ "${TARBALL_FILE,,}" == "true" ]];
1928 tar xvfz "${OKA_LOCATION}/${OKA_NAME}.tar.gz" -C "${DESTINATION}"
1930 # Otherwise it must be a folder structure
1931 cp -var "${OKA_LOCATION}/${OKA_NAME}/*" "${DESTINATION}/"
1936 # Delete OKA of a specific kind
1937 function delete_oka() {
1940 local PROJECT_NAME="${3:-"."}"
1941 ## `SW_CATALOGS_REPO_DIR` is the result of:
1942 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
1943 local SW_CATALOGS_REPO_DIR="$4"
1946 # Finds the corresponding catalog path from the SW-Catalogs root
1947 # and determine the destination
1948 local CATALOG_PATH=$(\
1953 local DESTINATION="${SW_CATALOGS_REPO_DIR}/${CATALOG_PATH}/${OKA_NAME}"
1956 rm -rf "${DESTINATION}"
1960 # Update OKA of a specific kind
1961 function update_oka() {
1964 local PROJECT_NAME="${3:-"."}"
1965 ## `SW_CATALOGS_REPO_DIR` is the result of:
1966 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
1967 local SW_CATALOGS_REPO_DIR="$4"
1968 local OKA_LOCATION="${5:-"."}"
1969 local TARBALL_FILE="${6:-"true"}"
1972 # Finds the corresponding catalog path from the SW-Catalogs root
1973 # and determine the destination
1974 local CATALOG_PATH=$(\
1979 local DESTINATION="${SW_CATALOGS_REPO_DIR}/${CATALOG_PATH}/${OKA_NAME}"
1981 # Remove and re-create
1982 rm -rf "${DESTINATION}"
1987 "${SW_CATALOGS_REPO_DIR}" \