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