blob: 497ddee498bb90a11fab4b2c0b7566264a99965d [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 +020020function generator_encrypted_secret_cloud_credentials() {
21 local CLOUD_CREDENTIALS_FILENAME="$1"
22 local SECRET_NAME="$2"
23 local PUBLIC_KEY="$3"
24 local SECRET_MANIFEST_FILENAME="${4:-secret-${SECRET_NAME}.yaml}"
25
26 join_lists \
27 <(cat) \
28 <(cat "${CREDENTIALS_DIR}/${CLOUD_CREDENTIALS_FILENAME}" | \
29 kubectl create secret generic ${SECRET_NAME} \
30 --namespace crossplane-system \
31 --from-file creds=/dev/stdin \
32 -o yaml --dry-run=client | \
33 encrypt_secret_from_stdin "${PUBLIC_KEY_MGMT}" | \
34 manifest2list | \
35 set_filename_to_items "${SECRET_MANIFEST_FILENAME}")
36}
37
38
39# Create ProviderConfig for Azure
40function add_providerconfig_for_azure() {
41 # Inputs
42 local CLOUD_CREDENTIALS="$1"
43 local NEW_SECRET_NAME="$2"
44 local PROVIDERCONFIG_NAME="${3:-default}"
45 local PUBLIC_KEY="${4:-${PUBLIC_KEY_MGMT}}"
46 local TARGET_FOLDER="${5:-${MGMT_ADDON_CONFIG_DIR}}"
47
48 # Path to folder with base templates
49 local TEMPLATES="${SW_CATALOGS_REPO_DIR}/infra-configs/crossplane/providers/azure/templates/"
50
51 # Pipeline
52 folder2list \
53 "${TEMPLATES}" | \
54 patch_replace \
55 ".metadata.name" \
56 "${PROVIDERCONFIG_NAME}" \
57 "| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")" | \
58 patch_replace \
59 ".spec.credentials.secretRef.name" \
60 "${NEW_SECRET_NAME}" \
61 "| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")" | \
62 rename_file_in_items \
63 "crossplane-providerconfig-azure.yaml" \
64 "crossplane-providerconfig-azure-${PROVIDERCONFIG_NAME}.yaml" | \
65 generator_encrypted_secret_cloud_credentials \
66 "${CLOUD_CREDENTIALS}" \
67 "${NEW_SECRET_NAME}" \
68 "${PUBLIC_KEY}" | \
69 list2folder_cp_over \
70 "${TARGET_FOLDER}"
71}
72
73
74# Create ProviderConfig for GCP
75function add_providerconfig_for_gcp() {
76 # Inputs
77 local CLOUD_CREDENTIALS="$1"
78 local NEW_SECRET_NAME="$2"
79 local GCP_PROJECT="$3"
80 local PROVIDERCONFIG_NAME="${4:-default}"
81 local PUBLIC_KEY="${5:-${PUBLIC_KEY_MGMT}}"
82 local TARGET_FOLDER="${6:-${MGMT_ADDON_CONFIG_DIR}}"
83
84 # Path to folder with base templates
85 local TEMPLATES="${SW_CATALOGS_REPO_DIR}/infra-configs/crossplane/providers/gcp/templates/"
86
87 # Pipeline
88 folder2list \
89 "${TEMPLATES}" | \
90 patch_replace \
91 ".metadata.name" \
92 "${PROVIDERCONFIG_NAME}" \
93 "| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")" | \
94 patch_replace \
95 ".spec.credentials.secretRef.name" \
96 "${NEW_SECRET_NAME}" \
97 "| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")" | \
98 patch_replace \
99 ".spec.projectID" \
100 "${GCP_PROJECT}" \
101 "| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")" | \
102 rename_file_in_items \
103 "crossplane-providerconfig-gcp.yaml" \
104 "crossplane-providerconfig-gcp-${PROVIDERCONFIG_NAME}.yaml" | \
105 generator_encrypted_secret_cloud_credentials \
106 "${CLOUD_CREDENTIALS}" \
107 "${NEW_SECRET_NAME}" \
108 "${PUBLIC_KEY}" | \
109 list2folder_cp_over \
110 "${TARGET_FOLDER}"
111}
112
113
yshah20619072025-06-13 14:56:25 +0000114# Create remote NodeGroup in AWS
115function create_nodegroup() {
116 local NODEGROUP_NAME="$1"
117 local NODEGROUP_KUSTOMIZATION_NAME="$2"
118 local CLUSTER_NAME="$3"
119 local CLUSTER_TYPE="$4"
120 local PROVIDERCONFIG_NAME="${5:-default}"
121 local VM_SIZE="$6"
122 local NODE_COUNT="$7"
123 local CLUSTER_LOCATION="$8"
124 local CONFIGMAP_NAME="${9}"
125 local NODEGROUP_ROLE="${10}"
126 local PUBLIC_KEY_MGMT="${11:-"${PUBLIC_KEY_MGMT}"}"
127 local PUBLIC_KEY_NEW_CLUSTER="${12}"
128 local PRIVATE_KEY_NEW_CLUSTER="${13:-"${PRIVATE_KEY_NEW_CLUSTER}"}"
129 local AKS_RG_NAME="${14:-""}"
130 local GKE_PREEMPTIBLE_NODES="${15:-""}"
131 local FLEET_REPO_DIR="${16:-"${FLEET_REPO_DIR}"}"
132 local FLEET_REPO_URL="${17:-""}"
133 local SW_CATALOGS_REPO_DIR="${18:-"${SW_CATALOGS_REPO_DIR}"}"
134 local SW_CATALOGS_REPO_URL="${19:-""}"
135 local SKIP_BOOTSTRAP="${20:"false"}"
136 local MGMT_PROJECT_NAME="${21:-"osm_admin"}"
137 local MGMT_CLUSTER_NAME="${22:-"_management"}"
138 local BASE_TEMPLATES_PATH="${23:-"cloud-resources"}"
139 local TEMPLATE_MANIFEST_FILENAME="${24:-"nodegroup.yaml"}"
140 local MANIFEST_FILENAME="${25:-"${NODEGROUP_NAME}.yaml"}"
141
142 # Is the provider type supported?
143 local VALID_PROVIDERS=("eks" "aks" "gke")
144 CLUSTER_TYPE="${CLUSTER_TYPE,,}"
145 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${CLUSTER_TYPE}")) ]] && return 1
146
147 # Determines the source dir for the templates and the target folder in Fleet
148 local TEMPLATES_DIR="${SW_CATALOGS_REPO_DIR}/${BASE_TEMPLATES_PATH}/eks-nodegroup/templates"
149 local TARGET_FOLDER="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}/${CLUSTER_NAME}"
150
151 local IS_NODEGROUP_ROLE=$([[ "${NODEGROUP_ROLE}" != "default" ]]; echo $?)
152 local IS_DEFAULT_NODEGROUP_ROLE=$([[ "${NODEGROUP_ROLE}" == "default" ]]; echo $?)
153
154 local PATCH_VALUE=""
155 local COMPONENT=()
156 if [[ "${IS_NODEGROUP_ROLE}" == "0" ]];
157 then
158 PATCH_VALUE=$(cat <<EOF
159patch: |
160 apiVersion: eks.aws.upbound.io/v1beta1
161 kind: NodeGroup
162 metadata:
163 name: \${nodegroup_name}
164 spec:
165 forProvider:
166 nodeRoleArn: \${role}
167EOF
168)
169 else
170 COMPONENT=("../role")
171 fi
172
173 # Pipeline of transformations to create the cluster resource
174 export NODEGROUP_KUSTOMIZATION_NAME
175 # export OVERLAY_FOLDER
176 folder2list \
177 "${TEMPLATES_DIR}" | \
178 replace_env_vars \
179 '${NODEGROUP_KUSTOMIZATION_NAME}' | \
180 patch_replace \
181 ".spec.postBuild.substitute.nodegroup_name" \
182 "${NODEGROUP_NAME}" \
183 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${NODEGROUP_KUSTOMIZATION_NAME}\")" | \
184 patch_replace \
185 ".spec.postBuild.substitute.cluster_name" \
186 "${CLUSTER_NAME}" \
187 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${NODEGROUP_KUSTOMIZATION_NAME}\")" | \
188 patch_replace \
189 ".spec.postBuild.substitute.cluster_location" \
190 "${CLUSTER_LOCATION}" \
191 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${NODEGROUP_KUSTOMIZATION_NAME}\")" | \
192 patch_replace \
193 ".spec.postBuild.substitute.vm_size" \
194 "${VM_SIZE}" \
195 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${NODEGROUP_KUSTOMIZATION_NAME}\")" | \
196 patch_replace \
197 ".spec.postBuild.substitute.node_count" \
198 "${NODE_COUNT}" \
199 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${NODEGROUP_KUSTOMIZATION_NAME}\")" | \
200 patch_replace \
201 ".spec.postBuild.substitute.providerconfig_name" \
202 "${PROVIDERCONFIG_NAME}" \
203 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${NODEGROUP_KUSTOMIZATION_NAME}\")" | \
204 patch_replace \
205 ".spec.postBuild.substituteFrom[0].name" \
206 "${CONFIGMAP_NAME}" \
207 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${NODEGROUP_KUSTOMIZATION_NAME}\")" | \
208 patch_replace \
209 ".spec.postBuild.substitute.role" \
210 "${NODEGROUP_ROLE}" \
211 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${NODEGROUP_KUSTOMIZATION_NAME}\")" | \
212 transform_if \
213 "${IS_NODEGROUP_ROLE}" \
214 add_patch_to_kustomization_as_list \
215 "${NODEGROUP_KUSTOMIZATION_NAME}" \
216 "${PATCH_VALUE}" | \
217 transform_if \
218 "${IS_DEFAULT_NODEGROUP_ROLE}" \
219 add_component_to_kustomization_as_list \
220 "${NODEGROUP_KUSTOMIZATION_NAME}" \
221 "${COMPONENT}" | \
222 rename_file_in_items \
223 "${TEMPLATE_MANIFEST_FILENAME}" \
224 "${MANIFEST_FILENAME}" | \
225 prepend_folder_path "${NODEGROUP_KUSTOMIZATION_NAME}/" | \
226 list2folder_cp_over \
227 "${TARGET_FOLDER}"
228}
229
garciadeblas8a28f6d2025-06-11 11:11:56 +0200230
yshah20619072025-06-13 14:56:25 +0000231function 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
garciadeblas8a28f6d2025-06-11 11:11:56 +0200254 # Is the provider type supported?
yshah20619072025-06-13 14:56:25 +0000255 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
garciadeblas8a28f6d2025-06-11 11:11:56 +0200274
yshah20619072025-06-13 14:56:25 +0000275# Delete nodegroup
276function delete_nodegroup() {
277 local NODEGROUP_KUSTOMIZATION_NAME="$1"
278 local CLUSTER_NAME="$2"
279 local PROJECT_NAME="${3:-"${MGMT_PROJECT_NAME}"}"
280 local FLEET_REPO_DIR="${4:-"${FLEET_REPO_DIR}"}"
281 local MGMT_RESOURCES_DIR="${5:-"${MGMT_RESOURCES_DIR}"}"
yshah20619072025-06-13 14:56:25 +0000282 local NODEGROUP_DIR="${MGMT_RESOURCES_DIR}/${CLUSTER_NAME}/${NODEGROUP_KUSTOMIZATION_NAME}"
yshah20619072025-06-13 14:56:25 +0000283 # Delete node Kustomizations
284 rm -rf "${NODEGROUP_DIR}"
285}
286
garciadeblas8a28f6d2025-06-11 11:11:56 +0200287
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}"}"
garciadeblasb4769452025-08-06 18:08:18 +0200508 # Path for the source templates
509 local TEMPLATES="${6:-"${SW_CATALOGS_REPO_DIR}/cloud-resources/flux-remote-bootstrap/cluster-base/templates"}"
510
garciadeblas70461c52024-07-03 09:17:56 +0200511 # Optional inputs:
512 # Paths for each profile in the Git repo
garciadeblasb4769452025-08-06 18:08:18 +0200513 local INFRA_CONTROLLERS_PATH="${7:-"${PROJECT_NAME}/infra-controller-profiles/${CLUSTER_KUSTOMIZATION_NAME}"}"
514 local INFRA_CONFIGS_PATH="${8:-"${PROJECT_NAME}/infra-config-profiles/${CLUSTER_KUSTOMIZATION_NAME}"}"
515 local MANAGED_RESOURCES_PATH="${9:-"${PROJECT_NAME}/managed-resources/${CLUSTER_KUSTOMIZATION_NAME}"}"
516 local APPS_PATH="${10:-"${PROJECT_NAME}/app-profiles/${CLUSTER_KUSTOMIZATION_NAME}"}"
garciadeblas70461c52024-07-03 09:17:56 +0200517
518 # Generate
519 export CLUSTER_KUSTOMIZATION_NAME
520 export FLEET_REPO_URL
521 export SW_CATALOGS_REPO_URL
522 export INFRA_CONTROLLERS_PATH
523 export INFRA_CONFIGS_PATH
524 export MANAGED_RESOURCES_PATH
525 export APPS_PATH
526 join_lists \
527 <(cat) \
528 <(
529 folder2list \
530 "${TEMPLATES}" | \
531 replace_env_vars \
532 '${CLUSTER_KUSTOMIZATION_NAME},${FLEET_REPO_URL},${SW_CATALOGS_REPO_URL},${INFRA_CONTROLLERS_PATH},${INFRA_CONFIGS_PATH},${MANAGED_RESOURCES_PATH},${APPS_PATH}'
533 )
534}
535
536
537# Create SOPS configuration file for the root folder of the cluster
538function create_sops_configuration_file_new_cluster() {
539 local PUBLIC_KEY="$1"
540
541 MANIFEST="creation_rules:
542 - encrypted_regex: ^(data|stringData)$
543 age: ${PUBLIC_KEY}
544 # - path_regex: .*.yaml
545 # encrypted_regex: ^(data|stringData)$
546 # age: ${PUBLIC_KEY}"
547
548 # Generate SOPS configuration file for the root folder
549 echo "${MANIFEST}"
550}
551
552
553# Generate K8s secret for management cluster storing secret age key for the new cluster
554function generator_k8s_age_secret_new_cluster() {
555 local PRIVATE_KEY_NEW_CLUSTER="$1"
556 local PUBLIC_KEY_MGMT="$2"
557 local CLUSTER_AGE_SECRET_NAME="${3:-$(safe_name "sops-age-${CLUSTER_KUSTOMIZATION_NAME}")}"
garciadeblas8a28f6d2025-06-11 11:11:56 +0200558 local CLUSTER_AGE_SECRET_NAMESPACE="${4:-"managed-resources"}"
garciadeblas70461c52024-07-03 09:17:56 +0200559
560 join_lists \
561 <(cat) \
562 <(
563 echo "${PRIVATE_KEY_NEW_CLUSTER}" | \
564 grep -v '^#' | \
565 kubectl create secret generic "${CLUSTER_AGE_SECRET_NAME}" \
garciadeblas8a28f6d2025-06-11 11:11:56 +0200566 --namespace="${CLUSTER_AGE_SECRET_NAMESPACE}" \
garciadeblas70461c52024-07-03 09:17:56 +0200567 --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}"}"
garciadeblasb4769452025-08-06 18:08:18 +0200583 local BOOTSTRAP_KUSTOMIZATION_NAMESPACE="${5:-"managed-resources"}"
584 local CLUSTER_KUSTOMIZATION_NAMESPACE="${6:-"managed-resources"}"
585 local BOOTSTRAP_SECRET_NAMESPACE="${7:-"managed-resources"}"
garciadeblas70461c52024-07-03 09:17:56 +0200586
587 # Paths and names for the templates
garciadeblasb4769452025-08-06 18:08:18 +0200588 local MANIFEST_FILENAME="${8:-"cluster-bootstrap-${CLUSTER_KUSTOMIZATION_NAME}.yaml"}"
589 local TEMPLATES="${9:-"${SW_CATALOGS_REPO_DIR}/cloud-resources/flux-remote-bootstrap/bootstrap/templates"}"
590 local TEMPLATE_MANIFEST_FILENAME="${10:-"remote-cluster-bootstrap.yaml"}"
591
592 # Variables for kubeconfig secret configuration
593 local CLUSTER_KUBECONFIG_SECRET_KEY=${CLUSTER_KUBECONFIG_SECRET_KEY:-"kubeconfig"}
594 local CLUSTER_KUBECONFIG_SECRET_NAME=${CLUSTER_KUBECONFIG_SECRET_NAME:-"kubeconfig-${CLUSTER_KUSTOMIZATION_NAME}"}
garciadeblas70461c52024-07-03 09:17:56 +0200595
596 # Generate manifests
597 export CLUSTER_KUSTOMIZATION_NAME
598 export CLUSTER_NAME
599 export CLUSTER_AGE_SECRET_NAME
garciadeblasb4769452025-08-06 18:08:18 +0200600 export CLUSTER_KUBECONFIG_SECRET_KEY
601 export CLUSTER_KUBECONFIG_SECRET_NAME
602 export BOOTSTRAP_KUSTOMIZATION_NAMESPACE
603 export CLUSTER_KUSTOMIZATION_NAMESPACE
604 export BOOTSTRAP_SECRET_NAMESPACE
garciadeblas70461c52024-07-03 09:17:56 +0200605
606 join_lists \
607 <(cat) \
608 <(
609 folder2list \
610 "${TEMPLATES}" | \
611 rename_file_in_items \
612 "${TEMPLATE_MANIFEST_FILENAME}" \
613 "${MANIFEST_FILENAME}" | \
614 replace_env_vars \
garciadeblasb4769452025-08-06 18:08:18 +0200615 '${CLUSTER_KUSTOMIZATION_NAME},${CLUSTER_NAME},${CLUSTER_AGE_SECRET_NAME},${CLUSTER_KUBECONFIG_SECRET_KEY},${CLUSTER_KUBECONFIG_SECRET_NAME},${CLUSTER_KUSTOMIZATION_NAMESPACE},${BOOTSTRAP_KUSTOMIZATION_NAMESPACE},${BOOTSTRAP_SECRET_NAMESPACE}'
garciadeblas70461c52024-07-03 09:17:56 +0200616 )
617}
618
619
620# Auxiliary function to create kustomization manifests
621function manifest_kustomization() {
622 local KS_NAME="$1"
623 local KS_NS="$2"
624 local SOURCE_REPO="$3"
625 local MANIFESTS_PATH="$4"
626 local SOURCE_SYNC_INTERVAL="$5"
627 local HEALTH_CHECK_TO="$6"
628 local DEPENDS_ON="${7:-""}"
629 local OPTIONS="${8:-""}"
630
631 # Calculated inputs
632 local OPTION_FOR_DEPENDS_ON="$(
633 if [[ -z "${DEPENDS_ON}" ]];
634 then
635 echo ""
636 else
637 echo "--depends-on=${DEPENDS_ON}"
638 fi
639 )"
640 local OPTIONS="\
641 "${OPTIONS}" \
642 "${OPTION_FOR_DEPENDS_ON}" \
643 "
644
645 # Create Kustomization manifest
646 flux create kustomization "${KS_NAME}" \
647 --namespace="${KS_NS}" \
648 --source="${SOURCE_REPO}" \
649 --path="${MANIFESTS_PATH}" \
650 --interval="${SOURCE_SYNC_INTERVAL}" \
651 --health-check-timeout="${HEALTH_CHECK_TO}" \
652 ${OPTIONS} --export
653}
654
655
656# Helper function to generate a Kustomization
657function generator_kustomization() {
658 local MANIFEST_FILENAME="$1"
659 local ALL_PARAMS=( "${@}" )
660 local PARAMS=( "${ALL_PARAMS[@]:1}" )
661
662 # Use manifest creator to become a generator
663 make_generator \
664 "${MANIFEST_FILENAME}" \
665 manifest_kustomization \
666 "${PARAMS[@]}"
667}
668
669# ----- END of Helper functions for remote cluster bootstrap -----
670
671
672# Create bootstrap for remote cluster
673function create_bootstrap_for_remote_cluster() {
674 local CLUSTER_NAME="$1"
675 local CLUSTER_KUSTOMIZATION_NAME="$2"
676 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
677 local SW_CATALOGS_REPO_DIR="${4:-"${SW_CATALOGS_REPO_DIR}"}"
678 local FLEET_REPO_URL="${5:-""}"
679 local SW_CATALOGS_REPO_URL="${6:-""}"
680 local MGMT_PROJECT_NAME="${7:-${MGMT_PROJECT_NAME}}"
681 local PUBLIC_KEY_MGMT="${8:-"${PUBLIC_KEY_MGMT}"}"
682 local PUBLIC_KEY_NEW_CLUSTER="$9"
683 local PRIVATE_KEY_NEW_CLUSTER="${10:-${PRIVATE_KEY_NEW_CLUSTER}}"
garciadeblas36da51d2024-11-13 14:58:53 +0100684 local IMPORTED_CLUSTER="${11:-"false"}"
garciadeblasb4769452025-08-06 18:08:18 +0200685 local MGMT_CLUSTER_NAME="${12:-"_management"}"
686 local CLUSTER_KUBECONFIG_SECRET_NAME=${13:-"kubeconfig-${CLUSTER_KUSTOMIZATION_NAME}"}
687 local CLUSTER_KUBECONFIG_SECRET_KEY=${14:-"kubeconfig"}
688 local TEMPLATES_DIR="${15:-"${SW_CATALOGS_REPO_DIR}/cloud-resources/flux-remote-bootstrap/cluster-base/templates"}"
689 local BOOTSTRAP_KUSTOMIZATION_NAMESPACE="${16:-"managed-resources"}"
690 local CLUSTER_KUSTOMIZATION_NAMESPACE="${17:-"managed-resources"}"
691 local BOOTSTRAP_SECRET_NAMESPACE="${18:-"${BOOTSTRAP_KUSTOMIZATION_NAMESPACE}"}"
garciadeblas36da51d2024-11-13 14:58:53 +0100692
garciadeblas8a28f6d2025-06-11 11:11:56 +0200693 # Calculates the folder where managed resources are defined
garciadeblasb4769452025-08-06 18:08:18 +0200694 local MGMT_RESOURCES_DIR="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}"
garciadeblas70461c52024-07-03 09:17:56 +0200695
garciadeblas70461c52024-07-03 09:17:56 +0200696 # Create profile folders
697 echo "" | \
698 generator_profile_folders_new_cluster \
699 "${CLUSTER_KUSTOMIZATION_NAME}" \
700 "${FLEET_REPO_URL}" \
701 "${MGMT_PROJECT_NAME}" | \
702 list2folder_cp_over \
703 "${FLEET_REPO_DIR}"
704
705 # Create base Kustomizations for the new cluster
706 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
707 echo "" | \
708 generator_base_kustomizations_new_cluster \
709 "${CLUSTER_KUSTOMIZATION_NAME}" \
710 "${FLEET_REPO_URL}" \
711 "${SW_CATALOGS_REPO_URL}" \
712 "${MGMT_PROJECT_NAME}" \
garciadeblasb4769452025-08-06 18:08:18 +0200713 "${SW_CATALOGS_REPO_DIR}" \
714 "${TEMPLATES_DIR}" | \
garciadeblas70461c52024-07-03 09:17:56 +0200715 list2folder_cp_over \
716 "${CLUSTER_FOLDER}"
717
718 # Add SOPS configuration at the root folder of the cluster
719 # NOTE: This file cannot be generated by pure KRM functions since it begins by a dot ('.')
720 create_sops_configuration_file_new_cluster \
721 "${PUBLIC_KEY_NEW_CLUSTER}" \
722 > "${CLUSTER_FOLDER}/.sops.yaml"
723
724 # Add also the public SOPS key to the repository so that others who clone the repo can encrypt new files
725 # NOTE: This file cannot be generated by pure KRM functions since it begins by a dot ('.')
726 echo "${PUBLIC_KEY_NEW_CLUSTER}" \
727 > "${CLUSTER_FOLDER}/.sops.pub.asc"
728
729 # Prepare everything to perform a Flux bootstrap of the new remote cluster from the management cluster.
730 # 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
731 local CLUSTER_AGE_SECRET_NAME=$(safe_name "sops-age-${CLUSTER_KUSTOMIZATION_NAME}")
732 echo "" |
733 generator_bootstrap_new_cluster \
734 "${CLUSTER_NAME}" \
735 "${CLUSTER_KUSTOMIZATION_NAME}" \
736 "${CLUSTER_AGE_SECRET_NAME}" \
garciadeblasb4769452025-08-06 18:08:18 +0200737 "${SW_CATALOGS_REPO_DIR}" \
738 "${BOOTSTRAP_KUSTOMIZATION_NAMESPACE}" \
739 "${CLUSTER_KUSTOMIZATION_NAMESPACE}" \
740 "${BOOTSTRAP_SECRET_NAMESPACE}" | \
garciadeblas70461c52024-07-03 09:17:56 +0200741 generator_k8s_age_secret_new_cluster \
742 "${PRIVATE_KEY_NEW_CLUSTER}" \
743 "${PUBLIC_KEY_MGMT}" \
garciadeblasb4769452025-08-06 18:08:18 +0200744 "${CLUSTER_AGE_SECRET_NAME}" \
745 "${BOOTSTRAP_SECRET_NAMESPACE}" | \
garciadeblas70461c52024-07-03 09:17:56 +0200746 prepend_folder_path "${CLUSTER_KUSTOMIZATION_NAME}/" | \
747 list2folder_cp_over \
748 "${MGMT_RESOURCES_DIR}"
garciadeblas36da51d2024-11-13 14:58:53 +0100749
750 # If it is an imported cluster, we must create a placeholder Kustomization
751 if [[ "${IMPORTED_CLUSTER,,}" == "true" ]];
752 then
753 TEMPLATES_DIR="${SW_CATALOGS_REPO_DIR}/cloud-resources/flux-remote-bootstrap/empty-kustomization/templates"
754
755 export CLUSTER_KUSTOMIZATION_NAME
756 folder2list \
757 "${TEMPLATES_DIR}" | \
758 replace_env_vars \
759 '${CLUSTER_KUSTOMIZATION_NAME}' | \
760 prepend_folder_path "${CLUSTER_KUSTOMIZATION_NAME}/" | \
761 list2folder_cp_over \
762 "${MGMT_RESOURCES_DIR}"
763 fi
764}
765
766
767# Disconnect Flux of remote cluster
768function disconnect_flux_remote_cluster() {
769 local CLUSTER_KUSTOMIZATION_NAME="$1"
770 local FLEET_REPO_DIR="${2:-"${FLEET_REPO_DIR}"}"
771 local MGMT_PROJECT_NAME="${3:-${MGMT_PROJECT_NAME}}"
772
773
774 # Calculates key folders
775 ## Base folder with Kustomizations for the new cluster
garciadeblasc4afd542025-06-18 17:37:59 +0200776 # local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
garciadeblas36da51d2024-11-13 14:58:53 +0100777 ## Folder where managed resources are defined in the management cluster
778 local MGMT_RESOURCES_CLUSTER_DIR="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/_management/${CLUSTER_KUSTOMIZATION_NAME}"
779
garciadeblasc4afd542025-06-18 17:37:59 +0200780 # Delete Flux resources synchronized directly from remote cluster
781 # rm -rf "${CLUSTER_FOLDER}/flux-system"
garciadeblas36da51d2024-11-13 14:58:53 +0100782
783 # Delete Flux resources bootstraped remotely
784 rm -rf "${MGMT_RESOURCES_CLUSTER_DIR}/cluster-bootstrap-${CLUSTER_KUSTOMIZATION_NAME}.yaml"
garciadeblas70461c52024-07-03 09:17:56 +0200785}
786
787
788# Create remote CrossPlane cluster (generic for any cloud)
789function create_crossplane_cluster() {
790 local CLUSTER_KUSTOMIZATION_NAME="$1"
791 local CLUSTER_NAME="$2"
792 # As of today, one among `aks`, `eks` or `gke`:
793 local CLUSTER_TYPE="$3"
794 local PROVIDERCONFIG_NAME="${4:-default}"
795 local VM_SIZE="$5"
796 local NODE_COUNT="$6"
797 local CLUSTER_LOCATION="$7"
798 local K8S_VERSION="${8:-"'1.28'"}"
799 local PUBLIC_KEY_MGMT="${9:-"${PUBLIC_KEY_MGMT}"}"
800 local PUBLIC_KEY_NEW_CLUSTER="${10}"
801 local PRIVATE_KEY_NEW_CLUSTER="${11:-"${PRIVATE_KEY_NEW_CLUSTER}"}"
802 # AKS only
803 local AKS_RG_NAME="${12:-""}"
804 # GKE only
805 local GKE_PREEMPTIBLE_NODES="${13:-""}"
806 ## `FLEET_REPO_DIR` is the result of:
807 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
808 local FLEET_REPO_DIR="${14:-"${FLEET_REPO_DIR}"}"
809 local FLEET_REPO_URL="${15:-""}"
810 ## `SW_CATALOGS_REPO_DIR` is the result of:
811 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
812 local SW_CATALOGS_REPO_DIR="${16:-"${SW_CATALOGS_REPO_DIR}"}"
813 local SW_CATALOGS_REPO_URL="${17:-""}"
814 # Perform bootstrap unless asked otherwise
815 local SKIP_BOOTSTRAP="${18:"false"}"
816 # Only change if absolutely needeed
817 local MGMT_PROJECT_NAME="${19:-"osm_admin"}"
818 local MGMT_CLUSTER_NAME="${20:-"_management"}"
819 local BASE_TEMPLATES_PATH="${21:-"cloud-resources"}"
rshri4240a7d2025-06-13 11:30:35 +0000820 # EKS only
821 local CLUSTER_IAM_ROLE="${22}"
822 local CLUSTER_PRIVATE_SUBNETS_ID="${23}"
823 local CLUSTER_PUBLIC_SUBNETS_ID="${24}"
824 local CONFIGMAP_NAME="${25}"
825 local TEMPLATE_MANIFEST_FILENAME="${26:-"${CLUSTER_TYPE,,}01.yaml"}"
826 local MANIFEST_FILENAME="${27:-"${CLUSTER_TYPE,,}-${CLUSTER_NAME}.yaml"}"
garciadeblas70461c52024-07-03 09:17:56 +0200827
garciadeblas70461c52024-07-03 09:17:56 +0200828 # Is the provider type supported?
829 local VALID_PROVIDERS=("eks" "aks" "gke")
830 CLUSTER_TYPE="${CLUSTER_TYPE,,}"
831 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${CLUSTER_TYPE}")) ]] && return 1
832
rshri4240a7d2025-06-13 11:30:35 +0000833 # Determine which optional steps may be needed
834 local IS_EKS=$([[ "${CLUSTER_TYPE}" == "eks" ]]; echo $?)
835 local IS_AKS=$([[ "${CLUSTER_TYPE}" == "aks" ]]; echo $?)
836 local IS_GCP=$([[ "${CLUSTER_TYPE}" == "gcp" ]]; echo $?)
837
838 local IS_EKS_AND_IAM=1
839 local IAM_COMPONENTS=()
840 local PATCH_SUBNET=0
841 local PATCH_IAM=0
842 local PATCH_VALUE=""
843 local PATCH=1
844 local CONFIG=1
845
846 if [[ "$IS_EKS" -eq 0 ]]; then
847
848 # Check for subnet config
849 if [[ "$CLUSTER_PRIVATE_SUBNETS_ID" == "default" ]]; then
850 IS_EKS_AND_IAM=0
851 IAM_COMPONENTS+=("../network")
852 else
853 PATCH_SUBNET=1
854 fi
855
856 # Check for IAM role config
857 if [[ "$CLUSTER_IAM_ROLE" == "default" ]]; then
858 IS_EKS_AND_IAM=0
859 IAM_COMPONENTS+=("../iam")
860 else
861 PATCH_IAM=1
862 fi
863
864 # Set PATCH flag if patch is required
865 if [[ $PATCH_SUBNET -eq 1 || $PATCH_IAM -eq 1 ]]; then
866 # PATCH=1
867 echo "Generating patch..."
868
869 PATCH_VALUE=$(cat <<EOF
870 patch: |
871 apiVersion: eks.aws.upbound.io/v1beta1
872 kind: Cluster
873 metadata:
874 name: \${cluster_resource_name}-cluster
875 spec:
876 forProvider:
877EOF
878 )
879
880 # Append subnet block if needed
881 if [[ $PATCH_SUBNET -eq 1 ]]; then
882 PATCH_VALUE+=$(cat <<EOF
883
884 vpcConfig:
885 - endpointPrivateAccess: true
886 endpointPublicAccess: true
887 subnetIds: \${private_subnets}
888EOF
889 )
890 fi
891
892 # Append IAM role block if needed
893 if [[ $PATCH_IAM -eq 1 ]]; then
894 PATCH_VALUE+=$(cat <<EOF
895
896 roleArn: \${cluster_iam_role}
897EOF
898 )
899 fi
900 fi
901
902 # Set PATCH flag
903 if [[ "$PATCH_SUBNET" -eq 1 || "$PATCH_IAM" -eq 1 ]]; then
904 PATCH=0
905 fi
906
907 # Set CONFIG flag
908 if [[ "$CONFIGMAP_NAME" != "default" ]]; then
909 CONFIG=0
910 fi
911 fi
912
garciadeblas70461c52024-07-03 09:17:56 +0200913 # Determines the source dir for the templates and the target folder in Fleet
914 local TEMPLATES_DIR="${SW_CATALOGS_REPO_DIR}/${BASE_TEMPLATES_PATH}/${CLUSTER_TYPE}/templates"
915 local TARGET_FOLDER="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}"
916
garciadeblas70461c52024-07-03 09:17:56 +0200917 # Pipeline of transformations to create the cluster resource
918 export CLUSTER_KUSTOMIZATION_NAME
919 folder2list \
920 "${TEMPLATES_DIR}" | \
921 replace_env_vars \
922 '${CLUSTER_KUSTOMIZATION_NAME}' | \
923 patch_replace \
924 ".spec.postBuild.substitute.cluster_name" \
925 "${CLUSTER_NAME}" \
926 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
927 patch_replace \
928 ".spec.postBuild.substitute.vm_size" \
929 "${VM_SIZE}" \
930 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
931 patch_replace \
932 ".spec.postBuild.substitute.node_count" \
933 "${NODE_COUNT}" \
934 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
935 patch_replace \
936 ".spec.postBuild.substitute.cluster_location" \
937 "${CLUSTER_LOCATION}" \
938 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
939 patch_replace \
940 ".spec.postBuild.substitute.k8s_version" \
941 "${K8S_VERSION}" \
942 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
943 patch_replace \
rshri4240a7d2025-06-13 11:30:35 +0000944 ".spec.postBuild.substitute.cluster_iam_role" \
945 "${CLUSTER_IAM_ROLE}" \
946 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
947 patch_replace \
garciadeblas70461c52024-07-03 09:17:56 +0200948 ".spec.postBuild.substitute.providerconfig_name" \
949 "${PROVIDERCONFIG_NAME}" \
950 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
951 transform_if \
952 "${IS_AKS}" \
953 patch_replace \
954 ".spec.postBuild.substitute.rg_name" \
955 "${AKS_RG_NAME}" \
956 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
957 transform_if \
958 "${IS_GKE}" \
959 patch_replace \
960 ".spec.postBuild.substitute.preemptible_nodes" \
961 "${GKE_PREEMPTIBLE_NODES}" \
962 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
rshri4240a7d2025-06-13 11:30:35 +0000963 transform_if \
964 "${PATCH}" \
965 add_patch_to_kustomization_as_list \
966 "${CLUSTER_KUSTOMIZATION_NAME}" \
967 "${PATCH_VALUE}" | \
968 transform_if \
969 "${IS_EKS_AND_IAM}" \
970 add_component_to_kustomization_as_list \
971 "${CLUSTER_KUSTOMIZATION_NAME}" \
972 "${IAM_COMPONENTS[@]}" | \
973 transform_if \
974 "${CONFIG}" \
975 add_config_to_kustomization \
976 "${CLUSTER_KUSTOMIZATION_NAME}" | \
garciadeblas70461c52024-07-03 09:17:56 +0200977 rename_file_in_items \
978 "${TEMPLATE_MANIFEST_FILENAME}" \
979 "${MANIFEST_FILENAME}" | \
rshri4240a7d2025-06-13 11:30:35 +0000980 prepend_folder_path "${CLUSTER_KUSTOMIZATION_NAME}/clusterbase/" | \
garciadeblas70461c52024-07-03 09:17:56 +0200981 list2folder_cp_over \
982 "${TARGET_FOLDER}"
983
984 # Bootstrap (unless asked to skip)
985 if [[ "${SKIP_BOOTSTRAP,,}" == "true" ]]; then
986 return 0
987 fi
988 create_bootstrap_for_remote_cluster \
989 "${CLUSTER_NAME}" \
990 "${CLUSTER_KUSTOMIZATION_NAME}" \
991 "${FLEET_REPO_DIR}" \
992 "${SW_CATALOGS_REPO_DIR}" \
993 "${FLEET_REPO_URL}" \
994 "${SW_CATALOGS_REPO_URL}" \
995 "${MGMT_PROJECT_NAME}" \
996 "${PUBLIC_KEY_MGMT}" \
997 "${PUBLIC_KEY_NEW_CLUSTER}" \
998 "${PRIVATE_KEY_NEW_CLUSTER}"
999}
1000
1001
1002# Delete remote cluster (generic for any cloud)
1003function delete_remote_cluster() {
1004 local CLUSTER_KUSTOMIZATION_NAME="$1"
1005 local PROJECT_NAME="${2:-"${MGMT_PROJECT_NAME}"}"
1006 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
1007 local MGMT_RESOURCES_DIR="${4:-"${MGMT_RESOURCES_DIR}"}"
garciadeblas61e25062025-08-08 12:09:30 +02001008 local MGMT_CLUSTER_NAME="${5:-"${MGMT_CLUSTER_NAME}"}"
garciadeblas70461c52024-07-03 09:17:56 +02001009
1010 # Optional inputs: Paths for each profile in the Git repo
garciadeblasb4769452025-08-06 18:08:18 +02001011 local INFRA_CONTROLLERS_DIR="${6:-"${FLEET_REPO_DIR}/${PROJECT_NAME}/infra-controller-profiles/${CLUSTER_KUSTOMIZATION_NAME}"}"
1012 local INFRA_CONFIGS_DIR="${7:-"${FLEET_REPO_DIR}/${PROJECT_NAME}/infra-config-profiles/${CLUSTER_KUSTOMIZATION_NAME}"}"
1013 local MANAGED_RESOURCES_DIR="${8:-"${FLEET_REPO_DIR}/${PROJECT_NAME}/managed-resources/${CLUSTER_KUSTOMIZATION_NAME}"}"
garciadeblas61e25062025-08-08 12:09:30 +02001014 local APPS_DIR="${9:-"${FLEET_REPO_DIR}/${PROJECT_NAME}/app-profiles/${CLUSTER_KUSTOMIZATION_NAME}"}"
1015 local CLUSTER_DIR="${10:-"${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"}"
garciadeblas70461c52024-07-03 09:17:56 +02001016
garciadeblasc4afd542025-06-18 17:37:59 +02001017 # Optional input: Do I need a purge operation first?
garciadeblas61e25062025-08-08 12:09:30 +02001018 local PURGE="${11:-"false"}"
garciadeblasc4afd542025-06-18 17:37:59 +02001019
1020 # Perform the purge if needed
1021 if [[ "${PURGE,,}" == "true" ]]; then
1022 echo "Purging the remote Flux instalation..."
garciadeblasc4afd542025-06-18 17:37:59 +02001023 fi
1024
1025 echo "Deleting cluster profiles and (when applicable) its cloud resources..."
1026
garciadeblas70461c52024-07-03 09:17:56 +02001027 # Delete profile folders
1028 rm -rf "${INFRA_CONTROLLERS_DIR}"
1029 rm -rf "${INFRA_CONFIGS_DIR}"
1030 rm -rf "${MANAGED_RESOURCES_DIR}"
1031 rm -rf "${APPS_DIR}"
1032
garciadeblas61e25062025-08-08 12:09:30 +02001033 if [ -n "${MGMT_CLUSTER_NAME}" ]; then
1034 CLUSTER_DIR_IN_MGMT_CLUSTER="${9:-"${FLEET_REPO_DIR}/${PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_DIR}/${CLUSTER_KUSTOMIZATION_NAME}"}"
1035 rm -rf "${CLUSTER_DIR_IN_MGMT_CLUSTER}"
1036 fi
1037
garciadeblas70461c52024-07-03 09:17:56 +02001038 # Delete base cluster Kustomizations
1039 rm -rf "${CLUSTER_DIR}"
1040
garciadeblasc4afd542025-06-18 17:37:59 +02001041 # Delete cluster resources if managed by OSM (otherwise, this will be ignored)
garciadeblas70461c52024-07-03 09:17:56 +02001042 rm -rf "${MGMT_RESOURCES_DIR}/${CLUSTER_KUSTOMIZATION_NAME}"
1043}
1044
1045
1046# Update remote CrossPlane cluster (generic for any cloud)
1047function update_crossplane_cluster() {
1048 local CLUSTER_KUSTOMIZATION_NAME="$1"
1049 local CLUSTER_NAME="$2"
1050 # As of today, one among `aks`, `eks` or `gke`:
1051 local CLUSTER_TYPE="$3"
1052 local PROVIDERCONFIG_NAME="${4:-default}"
1053 local VM_SIZE="$5"
1054 local NODE_COUNT="$6"
1055 local CLUSTER_LOCATION="$7"
1056 local K8S_VERSION="${8:-"'1.28'"}"
1057 local PUBLIC_KEY_MGMT="${9:-"${PUBLIC_KEY_MGMT}"}"
1058 local PUBLIC_KEY_NEW_CLUSTER="${10}"
1059 local PRIVATE_KEY_NEW_CLUSTER="${11:-"${PRIVATE_KEY_NEW_CLUSTER}"}"
1060 # AKS only
1061 local AKS_RG_NAME="${12:-""}"
1062 # GKE only
1063 local GKE_PREEMPTIBLE_NODES="${13:-""}"
1064 ## `FLEET_REPO_DIR` is the result of:
1065 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
1066 local FLEET_REPO_DIR="${14:-"${FLEET_REPO_DIR}"}"
1067 local FLEET_REPO_URL="${15:-""}"
1068 ## `SW_CATALOGS_REPO_DIR` is the result of:
1069 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
1070 local SW_CATALOGS_REPO_DIR="${16:-"${SW_CATALOGS_REPO_DIR}"}"
1071 local SW_CATALOGS_REPO_URL="${17:-""}"
1072 # Prevent a new bootstrap by default
1073 local SKIP_BOOTSTRAP="${18:"true"}"
1074 # Only change if absolutely needeed
1075 local MGMT_PROJECT_NAME="${19:-"osm_admin"}"
1076 local MGMT_CLUSTER_NAME="${20:-"_management"}"
1077 local BASE_TEMPLATES_PATH="${21:-"cloud-resources"}"
1078 local TEMPLATE_MANIFEST_FILENAME="${22:-"${CLUSTER_TYPE,,}01.yaml"}"
1079 local MANIFEST_FILENAME="${23:-"${CLUSTER_TYPE,,}-${CLUSTER_NAME}.yaml"}"
1080
garciadeblasb4769452025-08-06 18:08:18 +02001081
garciadeblas70461c52024-07-03 09:17:56 +02001082 # Is the provider type supported?
1083 local VALID_PROVIDERS=("eks" "aks" "gke")
1084 CLUSTER_TYPE="${CLUSTER_TYPE,,}"
1085 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${CLUSTER_TYPE}")) ]] && return 1
1086
1087 # Determine key folders in Fleet
1088 local MGMT_RESOURCES_DIR="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}"
1089
1090 # First, delete cluster's CrossPlane resources
1091 # NOTE: We only delete de Kustomization referring to CrossPlane resources,
1092 # not the bootstrap resources or the profiles. Thus we avoid that KSUs are
1093 # affected or a potential second unnecesary bootstrap.
1094 rm -rf "${MGMT_RESOURCES_DIR}/${CLUSTER_KUSTOMIZATION_NAME}/${MANIFEST_FILENAME}"
1095
1096 # Then, recreate the manifests with updated values
1097 create_crossplane_cluster \
1098 "${CLUSTER_KUSTOMIZATION_NAME}" \
1099 "${CLUSTER_NAME}" \
1100 "${CLUSTER_TYPE}" \
1101 "${PROVIDERCONFIG_NAME}" \
1102 "${VM_SIZE}" \
1103 "${NODE_COUNT}" \
1104 "${CLUSTER_LOCATION}" \
1105 "${K8S_VERSION}" \
1106 "${PUBLIC_KEY_MGMT}" \
1107 "${PUBLIC_KEY_NEW_CLUSTER}" \
1108 "${PRIVATE_KEY_NEW_CLUSTER}" \
1109 "${AKS_RG_NAME}" \
1110 "${GKE_PREEMPTIBLE_NODES}" \
1111 "${FLEET_REPO_DIR}" \
1112 "${FLEET_REPO_URL}" \
1113 "${SW_CATALOGS_REPO_DIR}" \
1114 "${SW_CATALOGS_REPO_URL}" \
1115 "${SKIP_BOOTSTRAP}" \
1116 "${MGMT_PROJECT_NAME}" \
1117 "${MGMT_CLUSTER_NAME}" \
1118 "${BASE_TEMPLATES_PATH}" \
1119 "${TEMPLATE_MANIFEST_FILENAME}" \
1120 "${MANIFEST_FILENAME}"
1121}
1122
garciadeblas8a28f6d2025-06-11 11:11:56 +02001123# Create remote CAPI cluster for Openstack
1124function create_capi_openstack_cluster() {
1125 local CLUSTER_KUSTOMIZATION_NAME="${1}"
1126 local CLUSTER_NAME="${2}"
1127 local VM_SIZE="${3}"
1128 local VM_SIZE_CONTROL_PLANE="${4:-"${VM_SIZE}"}"
1129 local NODE_COUNT="${5}"
1130 local NODE_COUNT_CONTROLPLANE="${6:-"1"}"
1131 local K8S_VERSION="${7}"
1132 # OpenStack specific
1133 local OPENSTACK_CLOUD_NAME="${8}"
1134 local OPENSTACK_DNS_NAMESERVERS="${9}"
1135 local OPENSTACK_EXTERNAL_NETWORK_ID="${10}"
1136 local OPENSTACK_FAILURE_DOMAIN="${11}"
1137 local OPENSTACK_SSH_KEY_NAME="${12}"
1138 local CNI="${13:-"calico"}"
1139 local OPENSTACK_WORKER_IMAGE_NAME="${14:-"osm-capo-node-${K8S_VERSION}"}"
1140 local OPENSTACK_CONTROL_PLANE_IMAGE_NAME="${15:-"${OPENSTACK_WORKER_IMAGE_NAME}"}"
1141 # SOPS-AGE related
1142 local PUBLIC_KEY_MGMT="${16:-"${PUBLIC_KEY_MGMT}"}"
1143 local PUBLIC_KEY_NEW_CLUSTER="${17:-"${PUBLIC_KEY_NEW_CLUSTER}"}"
1144 local PRIVATE_KEY_NEW_CLUSTER="${18:-"${PRIVATE_KEY_NEW_CLUSTER}"}"
1145 # GitOps retaled
1146 local FLEET_REPO_DIR="${19:-"${FLEET_REPO_DIR}"}"
1147 local FLEET_REPO_URL="${20:-"${FLEET_REPO_URL}"}"
1148 local SW_CATALOGS_REPO_DIR="${21:-"${SW_CATALOGS_REPO_DIR}"}"
1149 local SW_CATALOGS_REPO_URL="${22:-"${SW_CATALOGS_REPO_URL}"}"
1150 local SKIP_BOOTSTRAP="${23:-"false"}"
1151 local MGMT_PROJECT_NAME="${24:-"osm_admin"}"
1152 local MGMT_CLUSTER_NAME="${25:-"_management"}"
1153 local BASE_TEMPLATES_PATH="${26:-"cloud-resources/capi"}"
garciadeblas55c98952025-08-07 13:43:48 +02001154 local CAPO_RESOURCES_NAMESPACE="${27:-"managed-resources"}"
garciadeblas8a28f6d2025-06-11 11:11:56 +02001155
1156 # Varibles with valus from convention.
1157 local CLUSTER_TYPE="openstack"
1158 local TEMPLATE_MANIFEST_FILENAME="capi-cluster.yaml"
1159 local MANIFEST_FILENAME="openstack-${CLUSTER_NAME}.yaml"
1160 local CLOUD_CREDENTIALS="${OPENSTACK_CLOUD_NAME}-capo-config"
1161
1162 # Determines the source dir for the templates and the target folder in Fleet
1163 local TEMPLATES_DIR="${SW_CATALOGS_REPO_DIR}/${BASE_TEMPLATES_PATH}/openstack-kubeadm/templates"
1164 local TARGET_FOLDER="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}"
1165 export CNI=${CNI,,}
1166
1167 # Variables for kubeconfig secret reference
1168 export CLUSTER_KUBECONFIG_SECRET_NAME="${CLUSTER_KUSTOMIZATION_NAME}-kubeconfig"
1169 export CLUSTER_KUBECONFIG_SECRET_KEY="value"
1170
1171 export CLUSTER_KUSTOMIZATION_NAME
1172 export OPENSTACK_CLOUD_NAME
1173
1174 folder2list \
1175 "${TEMPLATES_DIR}" | \
1176 replace_env_vars \
1177 '${CLUSTER_KUSTOMIZATION_NAME},${CNI},${CLUSTER_KUBECONFIG_SECRET_NAME},${CLUSTER_KUBECONFIG_SECRET_KEY},${OPENSTACK_CLOUD_NAME}' | \
1178 patch_replace \
1179 ".spec.postBuild.substitute.cluster_name" \
1180 "${CLUSTER_NAME}" \
1181 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1182 patch_replace \
1183 ".spec.postBuild.substitute.cni" \
1184 "${CNI}" \
1185 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1186 patch_replace \
1187 ".spec.postBuild.substitute.control_plane_machine_count" \
1188 "${NODE_COUNT_CONTROLPLANE}" \
1189 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1190 patch_replace \
1191 ".spec.postBuild.substitute.kubernetes_version" \
1192 "v${K8S_VERSION}" \
1193 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1194 patch_replace \
1195 ".spec.postBuild.substitute.namespace" \
garciadeblas55c98952025-08-07 13:43:48 +02001196 "${CAPO_RESOURCES_NAMESPACE}" \
garciadeblas8a28f6d2025-06-11 11:11:56 +02001197 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1198 patch_replace \
1199 ".spec.postBuild.substitute.worker_machine_count" \
1200 "${NODE_COUNT}" \
1201 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1202 patch_replace \
1203 ".spec.postBuild.substitute.openstack_cloud" \
1204 "${OPENSTACK_CLOUD_NAME}" \
1205 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1206 patch_replace \
1207 ".spec.postBuild.substitute.openstack_cloud_conf" \
1208 "${CLOUD_CREDENTIALS}" \
1209 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1210 patch_replace \
1211 ".spec.postBuild.substitute.openstack_control_plane_machine_flavor" \
1212 "${VM_SIZE_CONTROL_PLANE}" \
1213 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1214 patch_replace \
1215 ".spec.postBuild.substitute.openstack_dns_nameservers" \
1216 "${OPENSTACK_DNS_NAMESERVERS}" \
1217 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1218 patch_replace \
1219 ".spec.postBuild.substitute.openstack_external_network_id" \
1220 "${OPENSTACK_EXTERNAL_NETWORK_ID}" \
1221 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1222 patch_replace \
1223 ".spec.postBuild.substitute.openstack_failure_domain" \
1224 "${OPENSTACK_FAILURE_DOMAIN}" \
1225 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1226 patch_replace \
1227 ".spec.postBuild.substitute.openstack_worker_image_name" \
1228 "${OPENSTACK_WORKER_IMAGE_NAME}" \
1229 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1230 patch_replace \
1231 ".spec.postBuild.substitute.openstack_control_plane_image_name" \
1232 "${OPENSTACK_CONTROL_PLANE_IMAGE_NAME}" \
1233 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1234 patch_replace \
1235 ".spec.postBuild.substitute.openstack_node_machine_flavor" \
1236 "${VM_SIZE}" \
1237 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1238 patch_replace \
1239 ".spec.postBuild.substitute.openstack_ssh_key_name" \
1240 "${OPENSTACK_SSH_KEY_NAME}" \
1241 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1242 rename_file_in_items \
1243 "${TEMPLATE_MANIFEST_FILENAME}" \
1244 "${MANIFEST_FILENAME}" | \
1245 prepend_folder_path "${CLUSTER_KUSTOMIZATION_NAME}/" | \
1246 list2folder_cp_over \
1247 "${TARGET_FOLDER}"
garciadeblasb4769452025-08-06 18:08:18 +02001248
garciadeblas8a28f6d2025-06-11 11:11:56 +02001249 # Bootstrap (unless asked to skip)
1250 if [[ "${SKIP_BOOTSTRAP,,}" == "true" ]]; then
1251 return 0
1252 fi
garciadeblasb4769452025-08-06 18:08:18 +02001253
garciadeblas8a28f6d2025-06-11 11:11:56 +02001254 create_bootstrap_for_remote_cluster \
1255 "${CLUSTER_NAME}" \
1256 "${CLUSTER_KUSTOMIZATION_NAME}" \
1257 "${FLEET_REPO_DIR}" \
1258 "${SW_CATALOGS_REPO_DIR}" \
1259 "${FLEET_REPO_URL}" \
1260 "${SW_CATALOGS_REPO_URL}" \
1261 "${MGMT_PROJECT_NAME}" \
1262 "${PUBLIC_KEY_MGMT}" \
1263 "${PUBLIC_KEY_NEW_CLUSTER}" \
1264 "${PRIVATE_KEY_NEW_CLUSTER}" \
1265 "false" \
1266 '' \
1267 "${CLUSTER_KUBECONFIG_SECRET_NAME}" \
1268 "${CLUSTER_KUBECONFIG_SECRET_KEY}"
1269
1270}
1271
1272# Update remote CAPI cluster for Openstack
1273function update_capi_openstack_cluster() {
1274 local CLUSTER_KUSTOMIZATION_NAME="${1}"
1275 local CLUSTER_NAME="${2}"
1276 local VM_SIZE="${3}"
garciadeblas55c98952025-08-07 13:43:48 +02001277 local VM_SIZE_CONTROL_PLANE="${4:-"${VM_SIZE}"}"
garciadeblas8a28f6d2025-06-11 11:11:56 +02001278 local NODE_COUNT="${5}"
garciadeblas55c98952025-08-07 13:43:48 +02001279 local NODE_COUNT_CONTROLPLANE="${6:-"1"}"
garciadeblas8a28f6d2025-06-11 11:11:56 +02001280 local K8S_VERSION="${7}"
1281 # OpenStack specific
1282 local OPENSTACK_CLOUD_NAME="${8}"
1283 local OPENSTACK_DNS_NAMESERVERS="${9}"
1284 local OPENSTACK_EXTERNAL_NETWORK_ID="${10}"
1285 local OPENSTACK_FAILURE_DOMAIN="${11}"
1286 local OPENSTACK_SSH_KEY_NAME="${12}"
1287 local CNI="${13:-"calico"}"
1288 local OPENSTACK_WORKER_IMAGE_NAME="${14:-"osm-capo-node-${K8S_VERSION}"}"
1289 local OPENSTACK_CONTROL_PLANE_IMAGE_NAME="${15:-"${OPENSTACK_WORKER_IMAGE_NAME}"}"
1290 # SOPS-AGE related
1291 local PUBLIC_KEY_MGMT="${16:-"${PUBLIC_KEY_MGMT}"}"
1292 local PUBLIC_KEY_NEW_CLUSTER="${17:-"${PUBLIC_KEY_NEW_CLUSTER}"}"
1293 local PRIVATE_KEY_NEW_CLUSTER="${18:-"${PRIVATE_KEY_NEW_CLUSTER}"}"
1294 # GitOps retaled
1295 local FLEET_REPO_DIR="${19:-"${FLEET_REPO_DIR}"}"
1296 local FLEET_REPO_URL="${20:-"${FLEET_REPO_URL}"}"
1297 local SW_CATALOGS_REPO_DIR="${21:-"${SW_CATALOGS_REPO_DIR}"}"
1298 local SW_CATALOGS_REPO_URL="${22:-"${SW_CATALOGS_REPO_URL}"}"
garciadeblas55c98952025-08-07 13:43:48 +02001299 local SKIP_BOOTSTRAP="${23:-"false"}"
1300 local MGMT_PROJECT_NAME="${24:-"osm_admin"}"
1301 local MGMT_CLUSTER_NAME="${25:-"_management"}"
1302 local BASE_TEMPLATES_PATH="${26:-"cloud-resources/capi"}"
1303 local CAPO_RESOURCES_NAMESPACE="${27:-"managed-resources"}"
garciadeblasb4769452025-08-06 18:08:18 +02001304
garciadeblas8a28f6d2025-06-11 11:11:56 +02001305 # Determine key folders in Fleet
1306 local MGMT_RESOURCES_DIR="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}"
1307
1308 # Updating no new cluster
1309 local SKIP_BOOTSTRAP="true"
garciadeblasb4769452025-08-06 18:08:18 +02001310
garciadeblas8a28f6d2025-06-11 11:11:56 +02001311 create_capi_openstack_cluster \
1312 "${CLUSTER_KUSTOMIZATION_NAME}" \
1313 "${CLUSTER_NAME}" \
1314 "${VM_SIZE}" \
1315 "${VM_SIZE_CONTROL_PLANE}" \
1316 "${NODE_COUNT}" \
1317 "${NODE_COUNT_CONTROLPLANE}" \
1318 "${K8S_VERSION}" \
1319 "${OPENSTACK_CLOUD_NAME}" \
1320 "${OPENSTACK_DNS_NAMESERVERS}" \
1321 "${OPENSTACK_EXTERNAL_NETWORK_ID}" \
1322 "${OPENSTACK_FAILURE_DOMAIN}" \
1323 "${OPENSTACK_SSH_KEY_NAME}" \
1324 "${CNI}" \
1325 "${OPENSTACK_WORKER_IMAGE_NAME}" \
1326 "${OPENSTACK_CONTROL_PLANE_IMAGE_NAME}" \
1327 "${PUBLIC_KEY_MGMT}" \
1328 "${PUBLIC_KEY_NEW_CLUSTER}" \
1329 "${PRIVATE_KEY_NEW_CLUSTER}" \
1330 "${FLEET_REPO_DIR}" \
1331 "${FLEET_REPO_URL}" \
1332 "${SW_CATALOGS_REPO_DIR}" \
1333 "${SW_CATALOGS_REPO_URL}" \
1334 "${SKIP_BOOTSTRAP}" \
1335 "${MGMT_PROJECT_NAME}" \
1336 "${MGMT_CLUSTER_NAME}" \
1337 "${BASE_TEMPLATES_PATH}" \
garciadeblas55c98952025-08-07 13:43:48 +02001338 "${CAPO_RESOURCES_NAMESPACE}"
garciadeblas8a28f6d2025-06-11 11:11:56 +02001339}
1340
1341# Create remote Openshift cluster via ACM
1342function create_openshift_cluster {
1343 local CLUSTER_KUSTOMIZATION_NAME="${1}"
1344 local CLUSTER_NAME="${2}"
1345 # This has to be void. Stored in database
1346 local K8S_VERSION="${3:-"''"}"
1347 # SOPS-AGE related
1348 local PUBLIC_KEY_ACM="${4}"
1349 local PUBLIC_KEY_NEW_CLUSTER="${5:-"${PUBLIC_KEY_NEW_CLUSTER}"}"
1350 local PRIVATE_KEY_NEW_CLUSTER="${6:-"${PRIVATE_KEY_NEW_CLUSTER}"}"
1351 # OpenShift
1352 local OPENSHIFT_RELEASE="${7}"
1353 local INFRA_PUBLIC_SSH_KEY="${8}"
1354 local CONTROL_PLANE_AVAILABILITY="${9}"
1355 local WORKER_COUNT="${10}"
1356 local WORKER_CORES="${11}"
1357 local WORKER_MEMORY="${12}"
1358 local WORKER_VOLUME_SIZE="${13}"
1359 local STORAGE_CLASS="${14}"
1360 local BASE_DOMAIN="${15}"
1361 local MGMT_CLUSTER_NAME="${16}"
1362 local HOSTED_CLUSTERS_PROJECT="${17:-"clusters"}"
1363 local ETCD_VOLUME_SIZE="${18:-"8"}"
1364 # GitOps retaled
1365 local FLEET_REPO_DIR="${19:-"${FLEET_REPO_DIR}"}"
1366 local FLEET_REPO_URL="${20:-"${FLEET_REPO_URL}"}"
1367 local SW_CATALOGS_REPO_DIR="${21:-"${SW_CATALOGS_REPO_DIR}"}"
1368 local SW_CATALOGS_REPO_URL="${22:-"${SW_CATALOGS_REPO_URL}"}"
1369 local SKIP_BOOTSTRAP="${23:-"false"}"
1370 # Only change if absolutely needeed
1371 local MGMT_PROJECT_NAME="${24:-"osm_admin"}"
1372 local BASE_TEMPLATES_PATH="${25:-"cloud-resources"}"
1373 local TEMPLATE_MANIFEST_FILENAME="${26:-"openshift01.yaml"}"
1374 local MANIFEST_FILENAME="${27:-"openshift-${CLUSTER_NAME}.yaml"}"
garciadeblasb4769452025-08-06 18:08:18 +02001375
garciadeblas8a28f6d2025-06-11 11:11:56 +02001376 local TEMPLATES_DIR="${SW_CATALOGS_REPO_DIR}/cloud-resources/openshift/templates"
1377 local TARGET_FOLDER="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}"
1378
1379 # Internally ACM creates several projects for each cluster.
1380 # Specifically the klusterletaddonconfig must land in a project with the same name as the cluster.
1381 # This will be specifically controlled by the variable `CLUSTER_PROJECT`.
1382 #
1383 # It must be notes that CLUSTER_NAME, CLUSTER_KUSTOMIZATION_NAME and CLUSTER_PROJECT have the same value,
1384 # but they are conceptually different.
1385 local CLUSTER_PROJECT="${CLUSTER_KUSTOMIZATION_NAME}"
1386
1387 export CLUSTER_KUSTOMIZATION_NAME
1388
1389 folder2list \
1390 "${TEMPLATES_DIR}" | \
1391 replace_env_vars \
1392 '${CLUSTER_KUSTOMIZATION_NAME}' | \
1393 patch_replace \
1394 ".spec.postBuild.substitute.base_domain" \
1395 "${BASE_DOMAIN}" \
1396 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1397 patch_replace \
1398 ".spec.postBuild.substitute.cluster_name" \
1399 "${CLUSTER_NAME}" \
1400 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1401 patch_replace \
1402 ".spec.postBuild.substitute.cluster_project" \
1403 "${CLUSTER_PROJECT}" \
1404 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1405 patch_replace \
1406 ".spec.postBuild.substitute.hosted_cluster_project" \
1407 "${HOSTED_CLUSTERS_PROJECT}" \
1408 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1409 patch_replace \
1410 ".spec.postBuild.substitute.etcd_volume_size" \
1411 "${ETCD_VOLUME_SIZE}Gi" \
1412 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1413 patch_replace \
1414 ".spec.postBuild.substitute.openshift_release" \
1415 "${OPENSHIFT_RELEASE}" \
1416 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1417 patch_replace \
1418 ".spec.postBuild.substitute.storage_class" \
1419 "${STORAGE_CLASS}" \
1420 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1421 patch_replace \
1422 ".spec.postBuild.substitute.control_plane_availability" \
1423 "${CONTROL_PLANE_AVAILABILITY}" \
1424 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1425 patch_replace \
1426 ".spec.postBuild.substitute.worker_count" \
1427 "${WORKER_COUNT}" \
1428 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1429 patch_replace \
1430 ".spec.postBuild.substitute.worker_cores" \
1431 "${WORKER_CORES}" \
1432 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1433 patch_replace \
1434 ".spec.postBuild.substitute.worker_memory" \
1435 "${WORKER_MEMORY}Gi" \
1436 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1437 patch_replace \
1438 ".spec.postBuild.substitute.worker_volume_size" \
1439 "${WORKER_VOLUME_SIZE}Gi" \
1440 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1441 patch_replace \
1442 ".spec.postBuild.substitute.cluster_project" \
1443 "${CLUSTER_PROJECT}" \
1444 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}-ns\")" | \
1445 rename_file_in_items \
1446 "${TEMPLATE_MANIFEST_FILENAME}" \
1447 "${MANIFEST_FILENAME}" | \
1448 prepend_folder_path "${CLUSTER_KUSTOMIZATION_NAME}/" | \
1449 list2folder_cp_over \
1450 "${TARGET_FOLDER}"
1451
1452 echo "" | \
1453 make_generator \
1454 "pull-secret.yaml" \
1455 kubectl_encrypt \
1456 "${PUBLIC_KEY_ACM}" \
1457 create \
1458 secret \
1459 generic \
1460 "pullsecret-cluster-${CLUSTER_NAME}" \
1461 --namespace="${HOSTED_CLUSTERS_PROJECT}" \
1462 --from-file=".dockerconfigjson"=<(echo "${DOCKERCONFIGJSON}") \
1463 -o=yaml \
1464 --dry-run=client | \
1465 make_generator \
1466 "ssh-key-secret.yaml" \
1467 kubectl_encrypt \
1468 "${PUBLIC_KEY_ACM}" \
1469 create \
1470 secret \
1471 generic \
1472 "sshkey-cluster-${CLUSTER_NAME}" \
1473 --namespace="${HOSTED_CLUSTERS_PROJECT}" \
1474 --from-file='id_rsa.pub'=<(echo "${INFRA_PUBLIC_SSH_KEY}") \
1475 -o=yaml \
1476 --dry-run=client | \
1477 list2folder_cp_over \
1478 "${TARGET_FOLDER}/${CLUSTER_KUSTOMIZATION_NAME}"
1479
1480 # Bootstrap (unless asked to skip)
1481 if [[ "${SKIP_BOOTSTRAP,,}" == "true" ]]; then
1482 return 0
1483 fi
1484
1485 local CLUSTER_KUBECONFIG_SECRET_NAME="${CLUSTER_KUSTOMIZATION_NAME}-admin-kubeconfig"
1486 local CLUSTER_KUBECONFIG_SECRET_KEY="kubeconfig"
1487 local BOOTSTRAP_KUSTOMIZATION_NAMESPACE="${HOSTED_CLUSTERS_PROJECT}"
1488 local CLUSTER_KUSTOMIZATION_NAMESPACE="managed-resources"
1489 local BOOTSTRAP_SECRET_NAMESPACE="managed-resources"
1490
1491 create_bootstrap_for_remote_cluster \
1492 "${CLUSTER_NAME}" \
1493 "${CLUSTER_KUSTOMIZATION_NAME}" \
1494 "${FLEET_REPO_DIR}" \
1495 "${SW_CATALOGS_REPO_DIR}" \
1496 "${FLEET_REPO_URL}" \
1497 "${SW_CATALOGS_REPO_URL}" \
1498 "${MGMT_PROJECT_NAME}" \
1499 "${PUBLIC_KEY_ACM}" \
1500 "${PUBLIC_KEY_NEW_CLUSTER}" \
1501 "${PRIVATE_KEY_NEW_CLUSTER}" \
1502 "false" \
1503 "${MGMT_CLUSTER_NAME}" \
1504 "${CLUSTER_KUBECONFIG_SECRET_NAME}" \
1505 "${CLUSTER_KUBECONFIG_SECRET_KEY}" \
1506 "${SW_CATALOGS_REPO_DIR}/cloud-resources/flux-remote-bootstrap/cluster-base-openshift/templates" \
1507 "${BOOTSTRAP_KUSTOMIZATION_NAMESPACE}" \
1508 "${CLUSTER_KUSTOMIZATION_NAMESPACE}" \
1509 "${BOOTSTRAP_SECRET_NAMESPACE}"
1510
1511}
1512
1513# Update remote Openshift cluster via ACM
1514function update_openshift_cluster {
1515 local CLUSTER_KUSTOMIZATION_NAME="${1}"
1516 local CLUSTER_NAME="${2}"
1517 # This has to be void. Stored in database
1518 local K8S_VERSION="${3:-"''"}"
1519 # SOPS-AGE related
1520 local PUBLIC_KEY_ACM="${4}"
1521 local PUBLIC_KEY_NEW_CLUSTER="${5:-"${PUBLIC_KEY_NEW_CLUSTER}"}"
1522 local PRIVATE_KEY_NEW_CLUSTER="${6:-"${PRIVATE_KEY_NEW_CLUSTER}"}"
1523 # OpenShift specific
1524 local OPENSHIFT_RELEASE="${7}"
1525 local INFRA_PUBLIC_SSH_KEY="${8}"
1526 local CONTROL_PLANE_AVAILABILITY="${9}"
1527 local WORKER_COUNT="${10}"
1528 local WORKER_CORES="${11}"
1529 local WORKER_MEMORY="${12}"
1530 local WORKER_VOLUME_SIZE="${13}"
1531 local STORAGE_CLASS="${14}"
1532 local BASE_DOMAIN="${15}"
1533 local MGMT_CLUSTER_NAME="${16}"
1534 local HOSTED_CLUSTERS_PROJECT="${17:-"clusters"}"
1535 local ETCD_VOLUME_SIZE="${18:-"8"}"
1536 # GitOps retaled
1537 local FLEET_REPO_DIR="${19:-"${FLEET_REPO_DIR}"}"
1538 local FLEET_REPO_URL="${20:-"${FLEET_REPO_URL}"}"
1539 local SW_CATALOGS_REPO_DIR="${21:-"${SW_CATALOGS_REPO_DIR}"}"
1540 local SW_CATALOGS_REPO_URL="${22:-"${SW_CATALOGS_REPO_URL}"}"
garciadeblas36b6ad92025-08-07 19:54:52 +02001541 local SKIP_BOOTSTRAP="${23:-"true"}"
garciadeblas8a28f6d2025-06-11 11:11:56 +02001542 # Only change if absolutely needeed
1543 local MGMT_PROJECT_NAME="${24:-"osm_admin"}"
garciadeblasb4769452025-08-06 18:08:18 +02001544
garciadeblas8a28f6d2025-06-11 11:11:56 +02001545 # Determine key folders in Fleet
1546 local MGMT_RESOURCES_DIR="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}"
1547
garciadeblas8a28f6d2025-06-11 11:11:56 +02001548
1549 create_openshift_cluster \
1550 "${CLUSTER_KUSTOMIZATION_NAME}" \
1551 "${CLUSTER_NAME}" \
1552 "${K8S_VERSION}" \
1553 "${PUBLIC_KEY_ACM}" \
1554 "${PUBLIC_KEY_NEW_CLUSTER}" \
1555 "${PRIVATE_KEY_NEW_CLUSTER}" \
1556 "${OPENSHIFT_RELEASE}" \
1557 "${INFRA_PUBLIC_SSH_KEY}" \
1558 "${CONTROL_PLANE_AVAILABILITY}" \
1559 "${WORKER_COUNT}" \
1560 "${WORKER_CORES}" \
1561 "${WORKER_MEMORY}" \
1562 "${WORKER_VOLUME_SIZE}" \
1563 "${STORAGE_CLASS}" \
1564 "${BASE_DOMAIN}" \
1565 "${MGMT_CLUSTER_NAME}" \
1566 "${HOSTED_CLUSTERS_PROJECT}" \
1567 "${ETCD_VOLUME_SIZE}" \
1568 "${FLEET_REPO_DIR}" \
1569 "${FLEET_REPO_URL}" \
1570 "${SW_CATALOGS_REPO_DIR}" \
1571 "${SW_CATALOGS_REPO_URL}" \
1572 "${SKIP_BOOTSTRAP}" \
1573 "${MGMT_PROJECT_NAME}"
1574}
garciadeblas70461c52024-07-03 09:17:56 +02001575
1576# ----- Helper functions for adding/removing a profile from a cluster -----
1577
1578# Helper function to find profiles of a given type already used in the cluster
1579function profiles_of_type_in_cluster() {
1580 local CLUSTER_KUSTOMIZATION_NAME="$1"
1581 local RELEVANT_PROFILE_TYPE="$2"
1582 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
1583
1584 # Calculated fields
1585 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
1586
1587 # Processing (echoes the list)
1588 folder2list \
1589 "${CLUSTER_FOLDER}" | \
1590 get_value_from_resourcelist \
1591 ".metadata.name" \
1592 "| select(.kind == \"Kustomization\")
1593 | select(.metadata.labels.osm_profile_type == \"${RELEVANT_PROFILE_TYPE}\")" | \
1594 multiline2commalist
1595}
1596
1597
1598# Function to list the profiles **this profile depends on**
1599function profiles_this_one_depends_on() {
1600 local CLUSTER_KUSTOMIZATION_NAME="$1"
1601 local PROFILE_TYPE="$2"
1602 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
1603
1604 case "${PROFILE_TYPE,,}" in
1605
1606 "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers")
1607 # Controllers do not depend on any other type of profiles
1608 echo ""
1609 return 0
1610 ;;
1611
1612 "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs")
1613 # Infra configs depend on controllers
1614 profiles_of_type_in_cluster \
1615 "${CLUSTER_KUSTOMIZATION_NAME}" \
1616 "infra-controllers" \
1617 "${FLEET_REPO_DIR}"
1618 return 0
1619 ;;
1620
1621 "managed" | "resources" | "managed-resources" | "managed_resources")
1622 # Managed resources depend on infra configs
1623 profiles_of_type_in_cluster \
1624 "${CLUSTER_KUSTOMIZATION_NAME}" \
1625 "infra-configs" \
1626 "${FLEET_REPO_DIR}"
1627 return 0
1628 ;;
1629
1630 "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs")
1631 # Apps (also) depend on infra configs
1632 profiles_of_type_in_cluster \
1633 "${CLUSTER_KUSTOMIZATION_NAME}" \
1634 "infra-configs" \
1635 "${FLEET_REPO_DIR}"
1636 return 0
1637 ;;
1638
1639 *)
1640 echo -n "------------ ERROR ------------"
1641 return 1
1642 ;;
1643 esac
1644}
1645
1646
1647# Function to list the profiles that **depend on this profile**
1648function profiles_depend_on_this_one() {
1649 local CLUSTER_KUSTOMIZATION_NAME="$1"
1650 local PROFILE_TYPE="$2"
1651 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
1652
1653 case "${PROFILE_TYPE,,}" in
1654
1655 "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers")
1656 # Infra configs depend on infra controllers
1657 profiles_of_type_in_cluster \
1658 "${CLUSTER_KUSTOMIZATION_NAME}" \
1659 "infra-configs" \
1660 "${FLEET_REPO_DIR}"
1661 return 0
1662 ;;
1663
1664 "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs")
1665 # Both managed resources and apps depend on configs
1666 local PROFILES=(
1667 $(
1668 profiles_of_type_in_cluster \
1669 "${CLUSTER_KUSTOMIZATION_NAME}" \
1670 "managed-resources" \
1671 "${FLEET_REPO_DIR}"
1672 ) \
1673 $(
1674 profiles_of_type_in_cluster \
1675 "${CLUSTER_KUSTOMIZATION_NAME}" \
1676 "apps" \
1677 "${FLEET_REPO_DIR}"
1678 )
1679 )
1680 printf '%s,' "${PROFILES[@]}" | sed 's/,$//g'
1681 return 0
1682 ;;
1683
1684 "managed" | "resources" | "managed-resources" | "managed_resources")
1685 # No other profiles depend on managed resources
1686 echo ""
1687 return 0
1688 ;;
1689
1690 "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs")
1691 # No other profiles depend on apps
1692 echo ""
1693 return 0
1694 ;;
1695
1696 *)
1697 echo -n "------------ ERROR ------------"
1698 return 1
1699 ;;
1700 esac
1701}
1702
1703
1704# Helper function to add a dependency to a Kustomization only if it does not exist already
1705function add_dependency_to_kustomization_safely() {
1706 local KUSTOMIZATION_NAME="$1"
1707 local KUSTOMIZATION_TO_ADD_AS_DEP="$2"
1708
1709 local INPUT=$(cat)
1710 local FILTER="| select(.kind == \"Kustomization\") | select(.metadata.name == \"${KUSTOMIZATION_NAME}\")"
1711
1712 # Check if the dependency was added already
1713 local TEST_RESULT=$(
1714 echo "${INPUT}" | \
1715 is_element_on_list \
1716 ".spec.dependsOn[].name" \
1717 "${KUSTOMIZATION_TO_ADD_AS_DEP}" \
1718 "${FILTER}"
1719 )
1720
1721 # If it existed already, returns the stream as is
1722 if [[ "${TEST_RESULT}" == "true" ]]
1723 then
1724 echo "${INPUT}"
1725 # Otherwise, processes the stream to add it
1726 else
1727 echo "${INPUT}" | \
1728 patch_add_to_list \
1729 ".spec.dependsOn" \
1730 "{name: ${KUSTOMIZATION_TO_ADD_AS_DEP}}" \
1731 "${FILTER}"
1732 fi
1733}
1734
1735
1736# Helper function to remove a dependency from a Kustomization
1737function remove_dependency_from_kustomization_safely() {
1738 local KUSTOMIZATION_NAME="$1"
1739 local KUSTOMIZATION_TO_REMOVE_AS_DEP="$2"
1740
1741 # Calculated inputs
1742 local KEY_PATH=".spec.dependsOn[] | select(.name == \"${KUSTOMIZATION_TO_REMOVE_AS_DEP}\")"
1743 local FILTER="| select(.kind == \"Kustomization\") | select(.metadata.name == \"${KUSTOMIZATION_NAME}\")"
1744
1745 # Remove the entry from the dependency list (if it exists)
1746 yq "del((.items[]${FILTER})${KEY_PATH})"
1747}
1748
1749
1750# Ensure list of Kustomizations depend on a given Kustomization
1751function add_dependency_to_set_of_kustomizations_safely() {
1752 local KS_NAME="$1"
1753 local THEY_DEPEND_ON_THIS="$2"
1754
1755 local INPUT="$(cat)"
1756 local OUTPUT=""
1757
1758 # For each of the Kustomizations on the comma-separated list, adds `KS_NAME` as one of their dependencies
1759 for KUST in ${THEY_DEPEND_ON_THIS//,/ }
1760 do
1761 local OUTPUT="$(
1762 echo "${INPUT}" | \
1763 add_dependency_to_kustomization_safely \
1764 "${KUST}" \
1765 "${KS_NAME}"
1766 )"
1767 local INPUT="${OUTPUT}"
1768 done
1769
1770 # Return the final `ResultList`, after all iterations
1771 echo "${OUTPUT}"
1772}
1773
1774
1775# Ensure list of Kustomizations no longer depend on a given Kustomization
1776function remove_dependency_from_set_of_kustomizations_safely() {
1777 local KS_NAME="$1"
1778 local THEY_NO_LONGER_DEPEND_ON_THIS="$2"
1779
1780 local INPUT="$(cat)"
1781 local OUTPUT=""
1782
1783 # For each of the Kustomizations on the comma-separated list, removes `KS_NAME` from their dependencies
1784 for KUST in ${THEY_NO_LONGER_DEPEND_ON_THIS//,/ }
1785 do
1786 local OUTPUT="$(
1787 echo "${INPUT}" | \
1788 remove_dependency_from_kustomization_safely \
1789 "${KUST}" \
1790 "${KS_NAME}"
1791 )"
1792 local INPUT="${OUTPUT}"
1793 done
1794
1795 # Return the final `ResultList`, after all iterations
1796 echo "${OUTPUT}"
1797}
1798
1799# ----- END of Helper functions for adding/removing a profile from a cluster -----
1800
1801
1802# Add an existing profile to a cluster
1803function attach_profile_to_cluster() {
1804 local PROFILE_NAME="$1"
1805 local PROFILE_TYPE="$2"
1806 local PROJECT_NAME="$3"
1807 local CLUSTER_KUSTOMIZATION_NAME="$4"
1808 local FLEET_REPO_DIR="${5:-"${FLEET_REPO_DIR}"}"
1809
1810 # Calculated inputs
1811 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
1812 local TARGET_PROFILE_PATH="$(
1813 path_to_profile \
1814 "${PROFILE_NAME}" \
1815 "${PROFILE_TYPE}" \
1816 "${PROJECT_NAME}"
1817 )"
1818
1819 # Finds out which profiles it should depend on... and which profiles should depend on it
1820 local DEPENDS_ON=$(
1821 profiles_this_one_depends_on \
1822 "${CLUSTER_KUSTOMIZATION_NAME}" \
1823 "${PROFILE_TYPE}" \
1824 "${FLEET_REPO_DIR}"
1825 )
1826
1827 local THEY_DEPEND_ON_THIS=$(
1828 profiles_depend_on_this_one \
1829 "${CLUSTER_KUSTOMIZATION_NAME}" \
1830 "${PROFILE_TYPE}" \
1831 "${FLEET_REPO_DIR}"
1832 )
1833
1834 # Parameters for the new Kustomization object to point to the profile
1835 local KS_NAME="$(safe_name "${PROFILE_TYPE}-${PROFILE_NAME}")"
1836 local MANIFEST_FILENAME="${KS_NAME}.yaml"
1837 local KS_NS=flux-system
1838 local MANIFESTS_PATH="${TARGET_PROFILE_PATH}"
1839 local SOURCE_REPO=GitRepository/fleet-repo.flux-system
1840 local SOURCE_SYNC_INTERVAL="60m"
1841 local HEALTH_CHECK_TO="3m"
1842 local RETRY_INTERVAL="1m"
1843 local TIMEOUT="5m"
1844 local OPTIONS="\
1845 --decryption-provider=sops \
1846 --decryption-secret=sops-age \
1847 --prune=true \
1848 --timeout="${TIMEOUT}" \
1849 --retry-interval="${RETRY_INTERVAL}" \
1850 --label osm_profile_type="${PROFILE_TYPE}"
1851 "
1852
1853 # Finally, we update the folder with all the required changes:
1854 # - Update pre-existing Kustomizations that should depend on the new profile (besides others).
1855 # - Create a new Kustomization pointing to the profile.
1856 # - Update Kustomize's `kustomization.yaml` at the root of the cluster folder to take into account the new Kustomization pointing to the profile.
1857 # - Update the cluster folder accordingly.
1858 folder2list \
1859 "${CLUSTER_FOLDER}" |
1860 add_dependency_to_set_of_kustomizations_safely \
1861 "${KS_NAME}" \
1862 "${THEY_DEPEND_ON_THIS}" | \
1863 generator_kustomization \
1864 "${MANIFEST_FILENAME}" \
1865 "${KS_NAME}" \
1866 "${KS_NS}" \
1867 "${SOURCE_REPO}" \
1868 "${MANIFESTS_PATH}" \
1869 "${SOURCE_SYNC_INTERVAL}" \
1870 "${HEALTH_CHECK_TO}" \
1871 "${DEPENDS_ON}" \
1872 "${OPTIONS}" | \
1873 patch_add_to_list \
1874 ".resources" \
1875 "${MANIFEST_FILENAME}" \
1876 "| select(.kind == \"Kustomization\") | select(.apiVersion == \"kustomize.config.k8s.io/v1beta1\") | select(.metadata.annotations.\"config.kubernetes.io/path\" == \"kustomization.yaml\")" | \
1877 list2folder_sync_replace \
1878 "${CLUSTER_FOLDER}"
1879}
1880
1881
1882# Remove an existing profile from a cluster
1883function detach_profile_from_cluster() {
1884 local PROFILE_NAME="$1"
1885 local PROFILE_TYPE="$2"
1886 local PROJECT_NAME="$3"
1887 local CLUSTER_KUSTOMIZATION_NAME="$4"
1888 local FLEET_REPO_DIR="${5:-"${FLEET_REPO_DIR}"}"
1889
1890 # Calculated inputs
1891 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
1892 local TARGET_PROFILE_PATH="$(
1893 path_to_profile \
1894 "${PROFILE_NAME}" \
1895 "${PROFILE_TYPE}" \
1896 "${PROJECT_NAME}"
1897 )"
1898
1899 # Finds out which profiles still depend on it
1900 local THEY_DEPEND_ON_THIS=$(
1901 profiles_depend_on_this_one \
1902 "${CLUSTER_KUSTOMIZATION_NAME}" \
1903 "${PROFILE_TYPE}" \
1904 "${FLEET_REPO_DIR}"
1905 )
1906
1907 # Parameters for the new Kustomization object to point to the profile
1908 local KS_NAME="$(safe_name "${PROFILE_TYPE}-${PROFILE_NAME}")"
1909
1910 # Finally, we update the folder with all the required changes:
1911 # - Update pre-existing Kustomizations that should depend on the new profile (besides others).
1912 # - Create a new Kustomization pointing to the profile.
1913 # - 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.
1914 # - Update the cluster folder accordingly.
1915 folder2list \
1916 "${CLUSTER_FOLDER}" |
1917 remove_dependency_from_set_of_kustomizations_safely \
1918 "${KS_NAME}" \
1919 "${THEY_DEPEND_ON_THIS}" | \
1920 delete_object \
1921 "${KS_NAME}" \
1922 "Kustomization" \
1923 "kustomize.toolkit.fluxcd.io/v1" | \
1924 patch_delete_from_list \
1925 ".resources[] | select(. == \"${MANIFEST_FILENAME}\") " \
1926 "| select(.kind == \"Kustomization\") | select(.apiVersion == \"kustomize.config.k8s.io/v1beta1\") | select(.metadata.annotations.\"config.kubernetes.io/path\" == \"kustomization.yaml\")" | \
1927 list2folder_sync_replace \
1928 "${CLUSTER_FOLDER}"
1929}
1930
1931
1932# Low-level function to add a KSU into a profile
1933function create_ksu_into_profile() {
1934 local KSU_NAME="$1"
1935 local TARGET_PROFILE_FOLDER="$2"
1936 local TEMPLATES_PATH="$3"
1937 local SW_CATALOGS_REPO_DIR="$4"
1938 local TRANSFORMER="${5:-noop_transformer}"
1939
1940 # Gathers all optional parameters for transformer funcion (if any) and puts them into an array for further use
1941 local ALL_PARAMS=( "${@}" )
1942 local TRANSFORMER_ARGS=( "${ALL_PARAMS[@]:5}" )
1943
1944 # Composes the route to the local templates folder
1945 local TEMPLATES_FOLDER="${SW_CATALOGS_REPO_DIR}/${TEMPLATES_PATH}"
1946
1947 folder2list \
1948 "${TEMPLATES_FOLDER}" | \
1949 "${TRANSFORMER}" \
1950 "${TRANSFORMER_ARGS[@]}" | \
1951 prepend_folder_path "${KSU_NAME}/" | \
1952 list2folder_cp_over \
1953 "${TARGET_PROFILE_FOLDER}"
1954}
1955
1956
1957# Function to render a KSU from a `ResourceList` into a profile
1958function render_ksu_into_profile() {
1959 local KSU_NAME="$1"
1960 local PROFILE_NAME="$2"
1961 local PROFILE_TYPE="$3"
1962 local PROJECT_NAME="${4:-"${MGMT_PROJECT_NAME}"}"
1963 local FLEET_REPO_DIR="$5"
1964 local SYNC="${6:-"false"}"
1965
1966 local TARGET_PROFILE_PATH=$(
1967 path_to_profile \
1968 "${PROFILE_NAME}" \
1969 "${PROFILE_TYPE}" \
1970 "${PROJECT_NAME}"
1971 )
1972
1973 local TARGET_PROFILE_FOLDER="${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
1974
1975 # Determines the appropriate function depending on rendering strategy
1976 # - Sync (and potentially delete files in target folder)
1977 # - Copy over (only overwrite changed files, keep the rest)
1978 RENDERER=""
1979 if [[ ${SYNC,,} == "true" ]];
1980 then
1981 RENDERER="list2folder_sync_replace"
1982 else
1983 RENDERER="list2folder_cp_over"
1984 fi
1985
1986 # Render with the selected strategy
1987 [[ "${DRY_RUN,,}" != "true" ]] && mkdir -p "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
1988 "${RENDERER}" \
1989 "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
1990 ## This is improves the behaviour of the following code,
1991 ## since avoids unintented deletions in parent folder due to sync
1992 # prepend_folder_path "${KSU_NAME}/" | \
1993 # "${RENDERER}" \
1994 # "${TARGET_PROFILE_FOLDER}"
1995}
1996
1997
1998# High-level function to add a KSU into a profile for the case where
1999# 1. It is originated from an OKA, and
2000# 2. It is based on a HelmRelease.
2001function create_hr_ksu_into_profile() {
2002 # Base KSU generation from template
2003 ## `TEMPLATES_DIR` is the result of:
2004 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}/{{inputs.parameters.templates_path}}"
2005 local TEMPLATES_DIR="$1"
2006 local SUBSTITUTE_ENVIRONMENT="${2:-"false"}"
2007 local SUBSTITUTION_FILTER="${3:-""}"
2008 local CUSTOM_ENV_VARS="${4:-""}"
2009 # Patch HelmRelease in KSU with inline values
2010 local KUSTOMIZATION_NAME="$5"
2011 local HELMRELEASE_NAME="$6"
2012 local INLINE_VALUES="${7:-""}"
2013 # Secret reference and generation (if required)
2014 local IS_PREEXISTING_SECRET="${8:-"false"}"
2015 local TARGET_NS="$9"
2016 local VALUES_SECRET_NAME="${10}"
2017 local SECRET_KEY="${11:-"values.yaml"}"
2018 local AGE_PUBLIC_KEY="${12}"
2019 ## `SECRET_VALUES` will be obtained from the
2020 ## secret named after the input parameter `reference_secret_for_values`,
2021 ## and from the key named after the input parameter `reference_key_for_values`
2022 local LOCAL_SECRET_VALUES="${13:-"${SECRET_VALUES}"}"
2023 # ConfigMap reference and generation (if required)
2024 local IS_PREEXISTING_CM="${14:-"false"}"
2025 local VALUES_CM_NAME="${15:-""}"
2026 local CM_KEY="${16:-""}"
2027 local CM_VALUES="${17:-""}"
2028 # KSU rendering
2029 local KSU_NAME="${18}"
2030 local PROFILE_NAME="${19}"
2031 local PROFILE_TYPE="${20}"
2032 local PROJECT_NAME="${21:-"osm_admin"}"
2033 ## `FLEET_REPO_DIR` is the result of:
2034 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
2035 local FLEET_REPO_DIR="${22:-"/fleet/fleet-osm/"}"
2036 local SYNC="${23:-"true"}"
2037
2038 # Decides which steps may be skipped
2039 HAS_INLINE_VALUES=$([[ -n "${INLINE_VALUES}" ]]; echo $?)
2040 HAS_REFERENCES=$([[ ( -n "${VALUES_SECRET_NAME}" ) || ( -n "${VALUES_CM_NAME}" ) ]]; echo $?)
2041 NEEDS_NEW_SECRET=$([[ ( -n "${VALUES_SECRET_NAME}" ) && ( "${IS_PREEXISTING_SECRET,,}" == "false" ) ]]; echo $?)
2042 NEEDS_NEW_CM=$([[ ( -n "${VALUES_CM_NAME}" ) && ( "${IS_PREEXISTING_CM,,}" == "false" ) ]]; echo $?)
2043 ECHO_RESOURCELIST=$([[ "${DEBUG,,}" == "true" ]]; echo $?)
2044
2045 # If applicable, loads additional environment variables
2046 if [[ -n "${CUSTOM_ENV_VARS}" ]];
2047 then
2048 set -a
2049 source <(echo "${CUSTOM_ENV_VARS}")
2050 set +a
2051 fi
2052
2053 # Runs workflow
2054 folder2list_generator \
2055 "${TEMPLATES_DIR}" \
2056 "${SUBSTITUTE_ENVIRONMENT}" \
2057 "${SUBSTITUTION_FILTER}" | \
2058 transform_if \
2059 "${HAS_INLINE_VALUES}" \
2060 add_values_to_helmrelease_via_ks \
2061 "${KUSTOMIZATION_NAME}" \
2062 "${HELMRELEASE_NAME}" \
2063 "${INLINE_VALUES}" | \
2064 transform_if \
2065 "${HAS_REFERENCES}" \
2066 add_ref_values_to_hr_via_ks \
2067 "${KUSTOMIZATION_NAME}" \
2068 "${HELMRELEASE_NAME}" \
2069 "${VALUES_SECRET_NAME}" \
2070 "${VALUES_CM_NAME}" | \
2071 transform_if \
2072 "${NEEDS_NEW_SECRET}" \
2073 make_generator \
2074 "hr-values-secret.yaml" \
2075 kubectl_encrypt \
2076 "${AGE_PUBLIC_KEY}" \
2077 create \
2078 secret \
2079 generic \
2080 "${VALUES_SECRET_NAME}" \
2081 --namespace="${TARGET_NS}" \
2082 --from-file="${SECRET_KEY}"=<(echo "${LOCAL_SECRET_VALUES}") \
2083 -o=yaml \
2084 --dry-run=client | \
2085 transform_if \
2086 "${NEEDS_NEW_CM}" \
2087 make_generator \
2088 "hr-values-configmap.yaml" \
2089 kubectl \
2090 create \
2091 configmap \
2092 "${VALUES_CM_NAME}" \
2093 --namespace="${TARGET_NS}" \
2094 --from-file="${SECRET_KEY}"=<(echo "${CM_VALUES}") \
2095 -o=yaml \
2096 --dry-run=client | \
2097 transform_if \
2098 "${ECHO_RESOURCELIST}" \
2099 tee /dev/stderr | \
2100 render_ksu_into_profile \
2101 "${KSU_NAME}" \
2102 "${PROFILE_NAME}" \
2103 "${PROFILE_TYPE}" \
2104 "${PROJECT_NAME}" \
2105 "${FLEET_REPO_DIR}" \
2106 "${SYNC}"
2107}
2108
2109
2110# High-level function to update a KSU for the case where
2111# 1. It is originated from an OKA, and
2112# 2. It is based on a HelmRelease.
2113# NOTE: It is an alias of `create_hr_ksu_into_profile`, setting `sync` to true
2114function update_hr_ksu_into_profile() {
2115 # Base KSU generation from template
2116 ## `TEMPLATES_DIR` is the result of:
2117 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}/{{inputs.parameters.templates_path}}"
2118 local TEMPLATES_DIR="$1"
2119 local SUBSTITUTE_ENVIRONMENT="${2:-"false"}"
2120 local SUBSTITUTION_FILTER="${3:-""}"
2121 local CUSTOM_ENV_VARS="${4:-""}"
2122 # Patch HelmRelease in KSU with inline values
2123 local KUSTOMIZATION_NAME="$5"
2124 local HELMRELEASE_NAME="$6"
2125 local INLINE_VALUES="${7:-""}"
2126 # Secret reference and generation (if required)
2127 local IS_PREEXISTING_SECRET="${8:-"false"}"
2128 local TARGET_NS="$9"
2129 local VALUES_SECRET_NAME="${10}"
2130 local SECRET_KEY="${11:-"values.yaml"}"
2131 local AGE_PUBLIC_KEY="${12}"
2132 ## `SECRET_VALUES` will be obtained from the
2133 ## secret named after the input parameter `reference_secret_for_values`,
2134 ## and from the key named after the input parameter `reference_key_for_values`
2135 local LOCAL_SECRET_VALUES="${13:-"${SECRET_VALUES}"}"
2136 # ConfigMap reference and generation (if required)
2137 local IS_PREEXISTING_CM="${14:-"false"}"
2138 local VALUES_CM_NAME="${15:-""}"
2139 local CM_KEY="${16:-""}"
2140 local CM_VALUES="${17:-""}"
2141 # KSU rendering
2142 local KSU_NAME="${18}"
2143 local PROFILE_NAME="${19}"
2144 local PROFILE_TYPE="${20}"
2145 local PROJECT_NAME="${21:-"osm_admin"}"
2146 ## `FLEET_REPO_DIR` is the result of:
2147 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
2148 local FLEET_REPO_DIR="${22:-"/fleet/fleet-osm/"}"
2149 # local SYNC="${23:-"true"}"
2150
2151
2152 # This function is just an alias of `create_hr_ksu_into_profile`
2153 # forcing synchronization over the KSU folder
2154 create_hr_ksu_into_profile \
2155 "${TEMPLATES_DIR}" \
2156 "${SUBSTITUTE_ENVIRONMENT}" \
2157 "${SUBSTITUTION_FILTER}" \
2158 "${CUSTOM_ENV_VARS}" \
2159 "${KUSTOMIZATION_NAME}" \
2160 "${HELMRELEASE_NAME}" \
2161 "${INLINE_VALUES}" \
2162 "${IS_PREEXISTING_SECRET}" \
2163 "${TARGET_NS}" \
2164 "${VALUES_SECRET_NAME}" \
2165 "${SECRET_KEY}" \
2166 "${AGE_PUBLIC_KEY}" \
2167 "${LOCAL_SECRET_VALUES}" \
2168 "${IS_PREEXISTING_CM}" \
2169 "${VALUES_CM_NAME}" \
2170 "${CM_KEY}" \
2171 "${CM_VALUES}" \
2172 "${KSU_NAME}" \
2173 "${PROFILE_NAME}" \
2174 "${PROFILE_TYPE}" \
2175 "${PROJECT_NAME}" \
2176 "${FLEET_REPO_DIR}" \
2177 "true"
2178}
2179
2180
2181# High-level function to create a "generated" KSU into a profile when:
2182# 1. There is no template (OKA) available.
2183# 2. The SW is based on a Helm Chart that we want to deploy.
2184function create_generated_ksu_from_helm_into_profile() {
2185 # HelmRelease generation
2186 local HELMRELEASE_NAME="$1"
2187 local CHART_NAME="$2"
2188 local CHART_VERSION="$3"
2189 local TARGET_NS="$4"
2190 local CREATE_NS="${5:-"true"}"
2191 # Repo source generation
2192 local IS_PREEXISTING_REPO="${6:-"false"}"
2193 local HELMREPO_NAME="$7"
2194 local HELMREPO_URL="${8:-""}"
2195 local HELMREPO_NS="${9:-"${TARGET_NS}"}"
2196 local HELMREPO_SECRET_REF="${10:-""}"
2197 # HelmRelease inline values (if any)
2198 local INLINE_VALUES="${11:-""}"
2199 # Secret reference and generation (if required)
2200 local IS_PREEXISTING_SECRET="${12:-"false"}"
2201 local VALUES_SECRET_NAME="${13}"
2202 local SECRET_KEY="${14:-"values.yaml"}"
2203 local AGE_PUBLIC_KEY="${15}"
2204 ## `SECRET_VALUES` will be obtained from the
2205 ## secret named after the input parameter `reference_secret_for_values`,
2206 ## and from the key named after the input parameter `reference_key_for_values`
2207 local LOCAL_SECRET_VALUES="${16:-"${SECRET_VALUES}"}"
2208 # ConfigMap reference and generation (if required)
2209 local IS_PREEXISTING_CM="${17:-"false"}"
2210 local VALUES_CM_NAME="${18:-""}"
2211 local CM_KEY="${19:-""}"
2212 local CM_VALUES="${20:-""}"
2213 # KSU rendering
2214 local KSU_NAME="${21}"
2215 local PROFILE_NAME="${22}"
2216 local PROFILE_TYPE="${23}"
2217 local PROJECT_NAME="${24:-"osm_admin"}"
2218 ## `FLEET_REPO_DIR` is the result of:
2219 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
2220 local FLEET_REPO_DIR="${25:-"/fleet/fleet-osm/"}"
2221 # By default, it will not syncronize, so that we can easily accumulate more than
2222 # one Helm chart into the same KSU if desired
2223 local SYNC="${26:-"false"}"
2224
2225 # Decides which steps may be skipped
2226 local NEEDS_NEW_NS=$([[ "${CREATE_NS,,}" == "true" ]]; echo $?)
2227 local NEEDS_NEW_REPO_SOURCE=$([[ "${IS_PREEXISTING_REPO,,}" == "false" ]]; echo $?)
2228 local NEEDS_NEW_SECRET=$([[ ( -n "${VALUES_SECRET_NAME}" ) && ( "${IS_PREEXISTING_SECRET,,}" == "false" ) ]]; echo $?)
2229 local NEEDS_NEW_CM=$([[ ( -n "${VALUES_CM_NAME}" ) && ( "${IS_PREEXISTING_CM,,}" == "false" ) ]]; echo $?)
2230 local ECHO_RESOURCELIST=$([[ "${DEBUG,,}" == "true" ]]; echo $?)
2231
2232 # Determine extra options for HelmRelease creation and define full command
2233 OPTION_CHART_VERSION=""
2234 [[ -n "${CHART_VERSION}" ]] && OPTION_CHART_VERSION='--chart-version=${CHART_VERSION}'
2235 OPTION_INLINE_VALUES=""
2236 [[ -n "${INLINE_VALUES}" ]] && OPTION_INLINE_VALUES='--values=<(
2237 echo "${INLINE_VALUES}"
2238 )'
2239 OPTION_REFERENCE_SECRET=""
2240 [[ -n "${VALUES_SECRET_NAME}" ]] && OPTION_REFERENCE_SECRET='--values-from=Secret/${VALUES_SECRET_NAME}'
2241 OPTION_REFERENCE_CM=""
2242 [[ -n "${VALUES_CM_NAME}" ]] && OPTION_REFERENCE_CM='--values-from=ConfigMap/${VALUES_CM_NAME}'
2243
2244 export HR_COMMAND="\
2245 flux \
2246 -n "${TARGET_NS}" \
2247 create hr "${HELMRELEASE_NAME}" \
2248 --chart="${CHART_NAME}" \
2249 --source=HelmRepository/"${HELMREPO_NAME}.${HELMREPO_NS}" \
2250 "${OPTION_CHART_VERSION}" \
2251 "${OPTION_INLINE_VALUES}" \
2252 "${OPTION_REFERENCE_SECRET}" \
2253 "${OPTION_REFERENCE_CM}" \
2254 --export
2255 "
2256
2257 # Determine extra options for Helm source repo creation and define full command
2258 OPTION_REPO_SECRET=""
2259 [[ -n "${HELMREPO_SECRET_REF}" ]] && OPTION_REPO_SECRET='--secret-ref=${HELMREPO_SECRET_REF}'
2260
2261 export REPO_COMMAND="\
2262 flux \
2263 -n "${HELMREPO_NS}" \
2264 create source helm "${HELMREPO_NAME}" \
2265 --url="${HELMREPO_URL}" \
2266 "${OPTION_REPO_SECRET}" \
2267 --export
2268 "
2269
2270 # Runs workflow
2271 echo "" | \
2272 make_generator \
2273 "helm-release.yaml" \
2274 eval "${HR_COMMAND}" | \
2275 transform_if \
2276 "${NEEDS_NEW_NS}" \
2277 make_generator \
2278 "ns-for-hr.yaml" \
2279 kubectl \
2280 create \
2281 namespace \
2282 "${TARGET_NS}" \
2283 -o=yaml \
2284 --dry-run=client | \
2285 transform_if \
2286 "${NEEDS_NEW_REPO_SOURCE}" \
2287 make_generator \
2288 "helm-repo.yaml" \
2289 eval "${REPO_COMMAND}" | \
2290 transform_if \
2291 "${NEEDS_NEW_SECRET}" \
2292 make_generator \
2293 "hr-values-secret.yaml" \
2294 kubectl_encrypt \
2295 "${AGE_PUBLIC_KEY}" \
2296 create \
2297 secret \
2298 generic \
2299 "${VALUES_SECRET_NAME}" \
2300 --namespace="${TARGET_NS}" \
2301 --from-file="${SECRET_KEY}"=<(echo "${LOCAL_SECRET_VALUES}") \
2302 -o=yaml \
2303 --dry-run=client | \
2304 transform_if \
2305 "${NEEDS_NEW_CM}" \
2306 make_generator \
2307 "hr-values-configmap.yaml" \
2308 kubectl \
2309 create \
2310 configmap \
2311 "${VALUES_CM_NAME}" \
2312 --namespace="${TARGET_NS}" \
2313 --from-file="${SECRET_KEY}"=<(echo "${CM_VALUES}") \
2314 -o=yaml \
2315 --dry-run=client | \
2316 transform_if \
2317 "${ECHO_RESOURCELIST}" \
2318 tee /dev/stderr | \
2319 render_ksu_into_profile \
2320 "${KSU_NAME}" \
2321 "${PROFILE_NAME}" \
2322 "${PROFILE_TYPE}" \
2323 "${PROJECT_NAME}" \
2324 "${FLEET_REPO_DIR}" \
2325 "${SYNC}"
2326}
2327
2328
2329# High-level function to update a "generated" KSU:
2330# 1. There is no template (OKA) available.
2331# 2. The SW is based on a Helm Chart that we want to deploy.
2332# NOTE: It is an alias of `create_generated_ksu_from_helm_into_profile`, setting `sync` to true
2333function update_generated_ksu_from_helm_into_profile() {
2334 # HelmRelease generation
2335 local HELMRELEASE_NAME="$1"
2336 local CHART_NAME="$2"
2337 local CHART_VERSION="$3"
2338 local TARGET_NS="$4"
2339 local CREATE_NS="${5:-"true"}"
2340 # Repo source generation
2341 local IS_PREEXISTING_REPO="${6:-"false"}"
2342 local HELMREPO_NAME="$7"
2343 local HELMREPO_URL="${8:-""}"
2344 local HELMREPO_NS="${9:-"${TARGET_NS}"}"
2345 local HELMREPO_SECRET_REF="${10:-""}"
2346 # HelmRelease inline values (if any)
2347 local INLINE_VALUES="${11:-""}"
2348 # Secret reference and generation (if required)
2349 local IS_PREEXISTING_SECRET="${12:-"false"}"
2350 local VALUES_SECRET_NAME="${13}"
2351 local SECRET_KEY="${14:-"values.yaml"}"
2352 local AGE_PUBLIC_KEY="${15}"
2353 ## `SECRET_VALUES` will be obtained from the
2354 ## secret named after the input parameter `reference_secret_for_values`,
2355 ## and from the key named after the input parameter `reference_key_for_values`
2356 local LOCAL_SECRET_VALUES="${16:-"${SECRET_VALUES}"}"
2357 # ConfigMap reference and generation (if required)
2358 local IS_PREEXISTING_CM="${17:-"false"}"
2359 local VALUES_CM_NAME="${18:-""}"
2360 local CM_KEY="${19:-""}"
2361 local CM_VALUES="${20:-""}"
2362 # KSU rendering
2363 local KSU_NAME="${21}"
2364 local PROFILE_NAME="${22}"
2365 local PROFILE_TYPE="${23}"
2366 local PROJECT_NAME="${24:-"osm_admin"}"
2367 ## `FLEET_REPO_DIR` is the result of:
2368 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
2369 local FLEET_REPO_DIR="${25:-"/fleet/fleet-osm/"}"
2370 # By default, it will not syncronize, so that we can easily accumulate more than
2371 # one Helm chart into the same KSU if desired
2372 # local SYNC="${26:-"false"}"
2373
2374 # Decides which steps may be skipped
2375 local NEEDS_NEW_NS=$([[ "${CREATE_NS,,}" == "true" ]]; echo $?)
2376 local NEEDS_NEW_REPO_SOURCE=$([[ "${IS_PREEXISTING_REPO,,}" == "false" ]]; echo $?)
2377 local NEEDS_NEW_SECRET=$([[ ( -n "${VALUES_SECRET_NAME}" ) && ( "${IS_PREEXISTING_SECRET,,}" == "false" ) ]]; echo $?)
2378 local NEEDS_NEW_CM=$([[ ( -n "${VALUES_CM_NAME}" ) && ( "${IS_PREEXISTING_CM,,}" == "false" ) ]]; echo $?)
2379 local ECHO_RESOURCELIST=$([[ "${DEBUG,,}" == "true" ]]; echo $?)
2380
2381
2382 # This function is just an alias of `create_generated_ksu_from_helm_into_profile`
2383 # forcing synchronization over the KSU folder
2384 create_generated_ksu_from_helm_into_profile \
2385 "${HELMRELEASE_NAME}" \
2386 "${CHART_NAME}" \
2387 "${CHART_VERSION}" \
2388 "${TARGET_NS}" \
2389 "${CREATE_NS}" \
2390 "${IS_PREEXISTING_REPO}" \
2391 "${HELMREPO_NAME}" \
2392 "${HELMREPO_URL}" \
2393 "${HELMREPO_NS}" \
2394 "${HELMREPO_SECRET_REF}" \
2395 "${INLINE_VALUES}" \
2396 "${IS_PREEXISTING_SECRET}" \
2397 "${VALUES_SECRET_NAME}" \
2398 "${SECRET_KEY}" \
2399 "${AGE_PUBLIC_KEY}" \
2400 "${LOCAL_SECRET_VALUES}" \
2401 "${IS_PREEXISTING_CM}" \
2402 "${VALUES_CM_NAME}" \
2403 "${CM_KEY}" \
2404 "${CM_VALUES}" \
2405 "${KSU_NAME}" \
2406 "${PROFILE_NAME}" \
2407 "${PROFILE_TYPE}" \
2408 "${PROJECT_NAME}" \
2409 "${FLEET_REPO_DIR}" \
2410 "true"
2411}
2412
2413
2414# Low-level function to delete a KSU from a profile
2415function delete_ksu_from_profile_path() {
2416 local KSU_NAME="$1"
2417 local TARGET_PROFILE_PATH="$2"
2418 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
2419
2420 # Calculate profile folder
2421 TARGET_PROFILE_FOLDER="${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
2422
2423 # Delete the KSU folder
2424 rm -rf "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
2425}
2426
2427
2428# High-level function to delete a KSU from a profile
2429function delete_ksu_from_profile() {
2430 local KSU_NAME="$1"
2431 local PROFILE_NAME="$2"
2432 local PROFILE_TYPE="$3"
2433 local PROJECT_NAME="${4:-"osm_admin"}"
2434 ## `FLEET_REPO_DIR` is the result of:
2435 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
2436 local FLEET_REPO_DIR="$5"
2437
2438 # Calculate profile folder
2439 local TARGET_PROFILE_PATH=$(
2440 path_to_profile \
2441 "${PROFILE_NAME}" \
2442 "${PROFILE_TYPE}" \
2443 "${PROJECT_NAME}"
2444 )
2445 TARGET_PROFILE_FOLDER="${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
2446
2447 # Delete the KSU folder
2448 rm -rf "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
2449}
2450
2451
2452# High-level function to clone a KSU from a profile to another
2453function clone_ksu() {
2454 local SOURCE_KSU_NAME="$1"
2455 local SOURCE_PROFILE_NAME="$2"
2456 local SOURCE_PROFILE_TYPE="$3"
2457 local SOURCE_PROJECT_NAME="${4:-"osm_admin"}"
2458 local DESTINATION_KSU_NAME="${5:-"${SOURCE_KSU_NAME}"}"
2459 local DESTINATION_PROFILE_NAME="${6:-"${SOURCE_PROFILE_NAME}"}"
2460 local DESTINATION_PROFILE_TYPE="${7:-"${SOURCE_PROFILE_TYPE}"}"
2461 local DESTINATION_PROJECT_NAME="${8:-"${SOURCE_PROJECT_NAME}"}"
2462 ## `FLEET_REPO_DIR` is the result of:
2463 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
2464 local FLEET_REPO_DIR="$9"
2465
2466
2467 # If source and destination are identical, aborts
2468 if [[
2469 ("${SOURCE_KSU_NAME}" == "${DESTINATION_KSU_NAME}") && \
2470 ("${SOURCE_PROFILE_NAME}" == "${DESTINATION_PROFILE_NAME}") && \
2471 ("${SOURCE_PROFILE_TYPE}" == "${DESTINATION_PROFILE_TYPE}") && \
2472 ("${SOURCE_PROJECT_NAME}" == "${DESTINATION_PROJECT_NAME}") \
2473 ]];
2474 then
2475 return 1
2476 fi
2477
2478 # Calculate profile folders
2479 local SOURCE_PROFILE_PATH=$(
2480 path_to_profile \
2481 "${SOURCE_PROFILE_NAME}" \
2482 "${SOURCE_PROFILE_TYPE}" \
2483 "${SOURCE_PROJECT_NAME}"
2484 )
2485 local SOURCE_PROFILE_FOLDER="${FLEET_REPO_DIR}/${SOURCE_PROFILE_PATH}"
2486 local DESTINATION_PROFILE_PATH=$(
2487 path_to_profile \
2488 "${DESTINATION_PROFILE_NAME}" \
2489 "${DESTINATION_PROFILE_TYPE}" \
2490 "${DESTINATION_PROJECT_NAME}"
2491 )
2492 local DESTINATION_PROFILE_FOLDER="${FLEET_REPO_DIR}/${DESTINATION_PROFILE_PATH}"
2493
2494 # Clone KSU folder
2495 cp -ar \
2496 "${SOURCE_PROFILE_FOLDER}/${SOURCE_KSU_NAME}" \
2497 "${DESTINATION_PROFILE_FOLDER}/${DESTINATION_KSU_NAME}"
2498}
2499
2500
2501# Create a `ProviderConfig` for a CrossPlane provider
2502function create_crossplane_providerconfig() {
2503 local PROVIDERCONFIG_NAME="$1"
2504 # As of today, one among `azure`, `aws` or `gcp`:
2505 local PROVIDER_TYPE="$2"
2506 local CRED_SECRET_NAME="$3"
2507 local CRED_SECRET_KEY="${4:-"creds"}"
2508 local CRED_SECRET_NS="${5:-"crossplane-system"}"
2509 # If empty, it assumes the secret already exists
2510 local CRED_SECRET_CONTENT="${6:-"${CRED_SECRET_CONTENT:-""}"}"
2511 local AGE_PUBLIC_KEY_MGMT="$7"
2512 ## `FLEET_REPO_DIR` is the result of:
2513 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
2514 local FLEET_REPO_DIR="${8:-"${FLEET_REPO_DIR}"}"
2515 ## `SW_CATALOGS_REPO_DIR` is the result of:
2516 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
2517 local SW_CATALOGS_REPO_DIR="${9:-"${SW_CATALOGS_REPO_DIR}"}"
2518 # Only when applicable
2519 local TARGET_GCP_PROJECT="${10:-""}"
2520 # Do not touch unless strictly needed
2521 local BASE_TEMPLATES_PATH="${11:-"infra-configs/crossplane/providers"}"
2522 local OSM_PROJECT_NAME="${12:-"osm_admin"}"
2523 local MGMT_CLUSTER_NAME="${13:-"_management"}"
2524
garciadeblasb4769452025-08-06 18:08:18 +02002525
garciadeblas70461c52024-07-03 09:17:56 +02002526 # Is the provider type supported?
2527 local VALID_PROVIDERS=("aws" "azure" "gcp")
2528 PROVIDER_TYPE="${PROVIDER_TYPE,,}"
2529 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${PROVIDER_TYPE}")) ]] && return 1
2530
2531 # Determines the source dir for the templates and the target folder in Fleet
2532 local TEMPLATES_DIR="${SW_CATALOGS_REPO_DIR}/${BASE_TEMPLATES_PATH}/${PROVIDER_TYPE}/templates"
2533 local TARGET_FOLDER="${FLEET_REPO_DIR}/${OSM_PROJECT_NAME}/infra-config-profiles/${MGMT_CLUSTER_NAME}/crossplane-providerconfigs/${PROVIDER_TYPE}"
2534
2535 # Determine which optional steps may be needed
2536 local NEEDS_NEW_SECRET=$([[ -n "${CRED_SECRET_CONTENT}" ]]; echo $?)
2537 local NEEDS_PROJECT_NAME=$([[ "${PROVIDER_TYPE}" == "gcp" ]]; echo $?)
2538
2539 # Renders the `ProviderConfig` manifest and the encrypted secret (if applicable)
2540 echo "" | \
2541 folder2list_generator \
2542 "${TEMPLATES_DIR}" | \
2543 patch_replace \
2544 ".metadata.name" \
2545 "${PROVIDERCONFIG_NAME}" \
2546 "| select(.kind == \"ProviderConfig\")" | \
2547 patch_replace \
2548 ".spec.credentials.secretRef.name" \
2549 "${CRED_SECRET_NAME}" \
2550 "| select(.kind == \"ProviderConfig\")" | \
2551 patch_replace \
2552 ".spec.credentials.secretRef.key" \
2553 "${CRED_SECRET_KEY}" \
2554 "| select(.kind == \"ProviderConfig\")" | \
2555 patch_replace \
2556 ".spec.credentials.secretRef.namespace" \
2557 "${CRED_SECRET_NS}" \
2558 "| select(.kind == \"ProviderConfig\")" | \
2559 transform_if \
2560 "${NEEDS_PROJECT_NAME}" \
2561 patch_replace \
2562 ".spec.projectID" \
2563 "${TARGET_GCP_PROJECT}" \
2564 "| select(.kind == \"ProviderConfig\")" | \
2565 transform_if \
2566 "${NEEDS_NEW_SECRET}" \
2567 make_generator \
2568 "credentials-secret.yaml" \
2569 kubectl_encrypt \
2570 "${AGE_PUBLIC_KEY_MGMT}" \
2571 create \
2572 secret \
2573 generic \
2574 "${CRED_SECRET_NAME}" \
2575 --namespace="${CRED_SECRET_NS}" \
2576 --from-file="${CRED_SECRET_KEY}"=<(echo "${CRED_SECRET_CONTENT}") \
2577 -o=yaml \
2578 --dry-run=client | \
2579 prepend_folder_path \
2580 "${PROVIDERCONFIG_NAME}/" | \
2581 list2folder_cp_over \
2582 "${TARGET_FOLDER}"
2583}
2584
2585
2586# Delete a `ProviderConfig` for a CrossPlane provider
2587function delete_crossplane_providerconfig() {
2588 local PROVIDERCONFIG_NAME="$1"
2589 # As of today, one among `azure`, `aws` or `gcp`:
2590 local PROVIDER_TYPE="$2"
2591 ## `FLEET_REPO_DIR` is the result of:
2592 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
2593 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
2594 # Do not touch unless strictly needed
2595 local OSM_PROJECT_NAME="${4:-"osm_admin"}"
2596 local MGMT_CLUSTER_NAME="${5:-"_management"}"
2597
garciadeblasb4769452025-08-06 18:08:18 +02002598
garciadeblas70461c52024-07-03 09:17:56 +02002599 # Is the provider type supported?
2600 local VALID_PROVIDERS=("aws" "azure" "gcp")
2601 PROVIDER_TYPE="${PROVIDER_TYPE,,}"
2602 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${PROVIDER_TYPE}")) ]] && return 1
2603
2604 # Determines the target folder in Fleet
2605 local PROVIDERCONFIG_FOLDER="${FLEET_REPO_DIR}/${OSM_PROJECT_NAME}/infra-config-profiles/${MGMT_CLUSTER_NAME}/crossplane-providerconfigs/${PROVIDER_TYPE}/${PROVIDERCONFIG_NAME}"
2606
2607 # Delete the folder
2608 rm -rf "${PROVIDERCONFIG_FOLDER}"
2609}
2610
2611
2612# Update a `ProviderConfig` for a CrossPlane provider
2613function update_crossplane_providerconfig() {
2614 local PROVIDERCONFIG_NAME="$1"
2615 # As of today, one among `azure`, `aws` or `gcp`:
2616 local PROVIDER_TYPE="$2"
2617 local CRED_SECRET_NAME="$3"
2618 local CRED_SECRET_KEY="${4:-"creds"}"
2619 local CRED_SECRET_NS="${5:-"crossplane-system"}"
2620 # If empty, it assumes the secret already exists
2621 local CRED_SECRET_CONTENT="${6:-"${CRED_SECRET_CONTENT:-""}"}"
2622 local AGE_PUBLIC_KEY_MGMT="$7"
2623 ## `FLEET_REPO_DIR` is the result of:
2624 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
2625 local FLEET_REPO_DIR="${8:-"${FLEET_REPO_DIR}"}"
2626 ## `SW_CATALOGS_REPO_DIR` is the result of:
2627 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
2628 local SW_CATALOGS_REPO_DIR="${9:-"${SW_CATALOGS_REPO_DIR}"}"
2629 # Only when applicable
2630 local TARGET_GCP_PROJECT="${10:-""}"
2631 # Do not touch unless strictly needed
2632 local BASE_TEMPLATES_PATH="${11:-"infra-configs/crossplane/providers"}"
2633 local OSM_PROJECT_NAME="${12:-"osm_admin"}"
2634 local MGMT_CLUSTER_NAME="${13:-"_management"}"
2635
garciadeblasb4769452025-08-06 18:08:18 +02002636
garciadeblas70461c52024-07-03 09:17:56 +02002637 # Is the provider type supported?
2638 local VALID_PROVIDERS=("aws" "azure" "gcp")
2639 PROVIDER_TYPE="${PROVIDER_TYPE,,}"
2640 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${PROVIDER_TYPE}")) ]] && return 1
2641
2642 # First, delete; then, re-create
2643 delete_crossplane_providerconfig \
2644 "${PROVIDERCONFIG_NAME}" \
2645 "${PROVIDER_TYPE}" \
2646 "${FLEET_REPO_DIR}" \
2647 "${OSM_PROJECT_NAME}" \
2648 "${MGMT_CLUSTER_NAME}"
2649
2650 create_crossplane_providerconfig \
2651 "${PROVIDERCONFIG_NAME}" \
2652 "${PROVIDER_TYPE}" \
2653 "${CRED_SECRET_NAME}" \
2654 "${CRED_SECRET_KEY}" \
2655 "${CRED_SECRET_NS}" \
2656 "${CRED_SECRET_CONTENT}" \
2657 "${AGE_PUBLIC_KEY_MGMT}" \
2658 "${FLEET_REPO_DIR}" \
2659 "${SW_CATALOGS_REPO_DIR}" \
2660 "${TARGET_GCP_PROJECT}" \
2661 "${BASE_TEMPLATES_PATH}" \
2662 "${OSM_PROJECT_NAME}" \
2663 "${MGMT_CLUSTER_NAME}"
2664}
2665
2666
garciadeblas8a28f6d2025-06-11 11:11:56 +02002667# Create a CloudConfig for CAPI provider
2668function create_capi_openstack_cloudconf() {
2669 local OPENSTACK_CLOUD_NAME="${1}"
2670 local PUBLIC_KEY="${2:-"${PUBLIC_KEY_MGMT}"}"
2671 local CONFIG_DIR="${3:-"${MGMT_ADDON_CONFIG_DIR}"}"
2672
2673 local NAMESPACE="managed-resources"
2674
2675 local CLOUDS_YAML="${OPENSTACK_CLOUDS_YAML}"
2676 local CACERT="${OPENSTACK_CACERT}"
2677
2678 local CLOUD_CREDENTIALS_SECRET_NAME="${OPENSTACK_CLOUD_NAME}-capo-config"
2679 local CLOUD_CREDENTIALS_CLOUDS_KEY="clouds.yaml"
2680 local CLOUD_CREDENTIALS_CACERT_KEY="cacert"
2681 local CLOUD_CREDENTIALS_FILENAME="credentials-secret.yaml"
garciadeblasb4769452025-08-06 18:08:18 +02002682
garciadeblas8a28f6d2025-06-11 11:11:56 +02002683 local CLOUD_CREDENTIALS_TOML_SECRET_NAME="${OPENSTACK_CLOUD_NAME}-capo-config-toml"
2684 local CLOUD_CREDENTIALS_TOML_FILENAME="credentials-toml-secret.yaml"
2685
2686 local TARGET_FOLDER="${CONFIG_DIR}/capi-providerconfigs/capo/${OPENSTACK_CLOUD_NAME}-config"
2687 mkdir -p "${TARGET_FOLDER}"
2688
2689 echo "" | \
2690 make_generator \
2691 "${CLOUD_CREDENTIALS_FILENAME}" \
2692 kubectl_encrypt \
2693 "${PUBLIC_KEY}" \
2694 create \
2695 secret \
2696 generic \
2697 "${CLOUD_CREDENTIALS_SECRET_NAME}" \
2698 --namespace="${NAMESPACE}" \
2699 --from-file="${CLOUD_CREDENTIALS_CLOUDS_KEY}"=<(echo "${CLOUDS_YAML}") \
2700 --from-file="${CLOUD_CREDENTIALS_CACERT_KEY}"=<(echo "${CACERT}") \
2701 -o=yaml \
2702 --dry-run=client | \
2703 make_generator \
2704 "${CLOUD_CREDENTIALS_TOML_FILENAME}" \
2705 kubectl_encrypt \
2706 "${PUBLIC_KEY}" \
2707 create \
2708 secret \
2709 generic \
2710 "${CLOUD_CREDENTIALS_TOML_SECRET_NAME}" \
2711 --namespace="${NAMESPACE}" \
2712 --from-file="os_auth_url"=<(echo "${OS_AUTH_URL}") \
2713 --from-file="os_region_name"=<(echo "${OS_REGION_NAME}") \
2714 --from-file="os_username"=<(echo "${OS_USERNAME}") \
2715 --from-file="os_password"=<(echo "${OS_PASSWORD}") \
2716 --from-file="os_project_id"=<(echo "${OS_PROJECT_ID}") \
2717 --from-file="os_project_domain_id"=<(echo "${OS_PROJECT_DOMAIN_ID}") \
2718 -o=yaml \
2719 --dry-run=client | \
2720 list2folder_cp_over \
2721 "${TARGET_FOLDER}"
2722}
2723
2724# Update a CloudConfig for CAPI provider
2725function update_capi_openstack_cloudconf() {
2726 local CLOUD_CONFIG_NAME="${1}"
2727 local PUBLIC_KEY="${2:-"${PUBLIC_KEY_MGMT}"}"
2728 local CONFIG_DIR="${3:-"${MGMT_ADDON_CONFIG_DIR}"}"
2729
2730 delete_capi_openstack_cloudconf \
2731 "${CLOUD_CONFIG_NAME}" \
2732 "${CONFIG_DIR}"
garciadeblasb4769452025-08-06 18:08:18 +02002733
garciadeblas8a28f6d2025-06-11 11:11:56 +02002734 create_capi_openstack_cloudconf \
2735 "${CLOUD_CONFIG_NAME}" \
2736 "${PUBLIC_KEY}" \
2737 "${CONFIG_DIR}"
2738}
2739
2740
2741# Delete a CloudConfig for CAPI provider
2742function delete_capi_openstack_cloudconf() {
2743 local OPENSTACK_CLOUD_NAME="$1"
2744 local CONFIG_DIR="${2:-"${MGMT_ADDON_CONFIG_DIR}"}"
2745
2746 local TARGET_FOLDER="${CONFIG_DIR}/capi-providerconfigs/capo/${OPENSTACK_CLOUD_NAME}-config"
garciadeblasb4769452025-08-06 18:08:18 +02002747
garciadeblas8a28f6d2025-06-11 11:11:56 +02002748 # Delete the encrypted secrets files.
2749 rm -rf "${TARGET_FOLDER}"
2750}
2751
garciadeblas70461c52024-07-03 09:17:56 +02002752# Helper function to return the relative path of a location in SW Catalogs for an OKA
2753function path_to_catalog() {
2754 local OKA_TYPE="$1"
2755 local PROJECT_NAME="${2:-"osm_admin"}"
2756
2757 # Corrects `osm_admin` project, since it uses the root folder
2758 PROJECT_NAME="${PROJECT_NAME}"
2759 [[ "${PROJECT_NAME}" == "osm_admin" ]] && PROJECT_NAME="."
2760
2761 # Echoes the relate path from the SW-Catalogs root
2762 case "${OKA_TYPE,,}" in
2763
2764 "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers")
2765 echo -n "${PROJECT_NAME}/infra-controllers"
2766 return 0
2767 ;;
2768
2769 "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs")
2770 echo -n "${PROJECT_NAME}/infra-configs"
2771 return 0
2772 ;;
2773
2774 "managed" | "resources" | "managed-resources" | "managed_resources" | "cloud-resources" | "cloud_resources")
2775 echo -n "${PROJECT_NAME}/cloud-resources"
2776 return 0
2777 ;;
2778
2779 "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs")
2780 echo -n "${PROJECT_NAME}/apps"
2781 return 0
2782 ;;
2783
2784 *)
2785 echo -n "------------ ERROR ------------"
2786 return 1
2787 ;;
2788 esac
2789}
2790
2791
2792# Create OKA of a specific kind
2793function create_oka() {
2794 local OKA_NAME="$1"
2795 local OKA_TYPE="$2"
2796 local PROJECT_NAME="${3:-"."}"
2797 ## `SW_CATALOGS_REPO_DIR` is the result of:
2798 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
2799 local SW_CATALOGS_REPO_DIR="$4"
2800 local OKA_LOCATION="${5:-"."}"
2801 local TARBALL_FILE="${6:-"true"}"
2802
2803
2804 # Finds the corresponding catalog path from the SW-Catalogs root
2805 # and create the destination
2806 local CATALOG_PATH=$(\
2807 path_to_catalog \
2808 "${OKA_TYPE}" \
2809 "${PROJECT_NAME}"
2810 )
2811 local DESTINATION="${SW_CATALOGS_REPO_DIR}/${CATALOG_PATH}/${OKA_NAME}"
2812 mkdir -p "${DESTINATION}"
2813
2814 # When the OKA comes as a `tar.gz`
2815 if [[ "${TARBALL_FILE,,}" == "true" ]];
2816 then
2817 tar xvfz "${OKA_LOCATION}/${OKA_NAME}.tar.gz" -C "${DESTINATION}"
2818 else
2819 # Otherwise it must be a folder structure
2820 cp -var "${OKA_LOCATION}/${OKA_NAME}/*" "${DESTINATION}/"
2821 fi
2822}
2823
2824
2825# Delete OKA of a specific kind
2826function delete_oka() {
2827 local OKA_NAME="$1"
2828 local OKA_TYPE="$2"
2829 local PROJECT_NAME="${3:-"."}"
2830 ## `SW_CATALOGS_REPO_DIR` is the result of:
2831 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
2832 local SW_CATALOGS_REPO_DIR="$4"
2833
2834
2835 # Finds the corresponding catalog path from the SW-Catalogs root
2836 # and determine the destination
2837 local CATALOG_PATH=$(\
2838 path_to_catalog \
2839 "${OKA_TYPE}" \
2840 "${PROJECT_NAME}"
2841 )
2842 local DESTINATION="${SW_CATALOGS_REPO_DIR}/${CATALOG_PATH}/${OKA_NAME}"
2843
2844 # Remove the folder
2845 rm -rf "${DESTINATION}"
2846}
2847
2848
2849# Update OKA of a specific kind
2850function update_oka() {
2851 local OKA_NAME="$1"
2852 local OKA_TYPE="$2"
2853 local PROJECT_NAME="${3:-"."}"
2854 ## `SW_CATALOGS_REPO_DIR` is the result of:
2855 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
2856 local SW_CATALOGS_REPO_DIR="$4"
2857 local OKA_LOCATION="${5:-"."}"
2858 local TARBALL_FILE="${6:-"true"}"
2859
2860
2861 # Finds the corresponding catalog path from the SW-Catalogs root
2862 # and determine the destination
2863 local CATALOG_PATH=$(\
2864 path_to_catalog \
2865 "${OKA_TYPE}" \
2866 "${PROJECT_NAME}"
2867 )
2868 local DESTINATION="${SW_CATALOGS_REPO_DIR}/${CATALOG_PATH}/${OKA_NAME}"
2869
2870 # Remove and re-create
2871 rm -rf "${DESTINATION}"
2872 create_oka \
2873 "${OKA_NAME}" \
2874 "${OKA_TYPE}" \
2875 "${PROJECT_NAME}" \
2876 "${SW_CATALOGS_REPO_DIR}" \
2877 "${OKA_LOCATION}" \
2878 "${TARBALL_FILE}"
2879}