blob: 2b5d6966146e45bdd602e3c8559abca38c1cadb0 [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"}"
1154 local NAMESPACE="${27:-"managed-resources"}"
1155
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" \
1196 "${NAMESPACE}" \
1197 "| 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}"
1277 local VM_SIZE_CONTROL_PLANE="${4}"
1278 local NODE_COUNT="${5}"
1279 local NODE_COUNT_CONTROLPLANE="${6}"
1280 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}"}"
1299 local MGMT_PROJECT_NAME="${23:-"osm_admin"}"
1300 local MGMT_CLUSTER_NAME="${24:-"_management"}"
1301 local BASE_TEMPLATES_PATH="${25:-"cloud-resources/capi"}"
1302 local NAMESPACE="${26:-"managed-resources"}"
garciadeblasb4769452025-08-06 18:08:18 +02001303
garciadeblas8a28f6d2025-06-11 11:11:56 +02001304 # Determine key folders in Fleet
1305 local MGMT_RESOURCES_DIR="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}"
1306
1307 # Updating no new cluster
1308 local SKIP_BOOTSTRAP="true"
garciadeblasb4769452025-08-06 18:08:18 +02001309
garciadeblas8a28f6d2025-06-11 11:11:56 +02001310 create_capi_openstack_cluster \
1311 "${CLUSTER_KUSTOMIZATION_NAME}" \
1312 "${CLUSTER_NAME}" \
1313 "${VM_SIZE}" \
1314 "${VM_SIZE_CONTROL_PLANE}" \
1315 "${NODE_COUNT}" \
1316 "${NODE_COUNT_CONTROLPLANE}" \
1317 "${K8S_VERSION}" \
1318 "${OPENSTACK_CLOUD_NAME}" \
1319 "${OPENSTACK_DNS_NAMESERVERS}" \
1320 "${OPENSTACK_EXTERNAL_NETWORK_ID}" \
1321 "${OPENSTACK_FAILURE_DOMAIN}" \
1322 "${OPENSTACK_SSH_KEY_NAME}" \
1323 "${CNI}" \
1324 "${OPENSTACK_WORKER_IMAGE_NAME}" \
1325 "${OPENSTACK_CONTROL_PLANE_IMAGE_NAME}" \
1326 "${PUBLIC_KEY_MGMT}" \
1327 "${PUBLIC_KEY_NEW_CLUSTER}" \
1328 "${PRIVATE_KEY_NEW_CLUSTER}" \
1329 "${FLEET_REPO_DIR}" \
1330 "${FLEET_REPO_URL}" \
1331 "${SW_CATALOGS_REPO_DIR}" \
1332 "${SW_CATALOGS_REPO_URL}" \
1333 "${SKIP_BOOTSTRAP}" \
1334 "${MGMT_PROJECT_NAME}" \
1335 "${MGMT_CLUSTER_NAME}" \
1336 "${BASE_TEMPLATES_PATH}" \
1337 "${NAMESPACE}"
1338}
1339
1340# Create remote Openshift cluster via ACM
1341function create_openshift_cluster {
1342 local CLUSTER_KUSTOMIZATION_NAME="${1}"
1343 local CLUSTER_NAME="${2}"
1344 # This has to be void. Stored in database
1345 local K8S_VERSION="${3:-"''"}"
1346 # SOPS-AGE related
1347 local PUBLIC_KEY_ACM="${4}"
1348 local PUBLIC_KEY_NEW_CLUSTER="${5:-"${PUBLIC_KEY_NEW_CLUSTER}"}"
1349 local PRIVATE_KEY_NEW_CLUSTER="${6:-"${PRIVATE_KEY_NEW_CLUSTER}"}"
1350 # OpenShift
1351 local OPENSHIFT_RELEASE="${7}"
1352 local INFRA_PUBLIC_SSH_KEY="${8}"
1353 local CONTROL_PLANE_AVAILABILITY="${9}"
1354 local WORKER_COUNT="${10}"
1355 local WORKER_CORES="${11}"
1356 local WORKER_MEMORY="${12}"
1357 local WORKER_VOLUME_SIZE="${13}"
1358 local STORAGE_CLASS="${14}"
1359 local BASE_DOMAIN="${15}"
1360 local MGMT_CLUSTER_NAME="${16}"
1361 local HOSTED_CLUSTERS_PROJECT="${17:-"clusters"}"
1362 local ETCD_VOLUME_SIZE="${18:-"8"}"
1363 # GitOps retaled
1364 local FLEET_REPO_DIR="${19:-"${FLEET_REPO_DIR}"}"
1365 local FLEET_REPO_URL="${20:-"${FLEET_REPO_URL}"}"
1366 local SW_CATALOGS_REPO_DIR="${21:-"${SW_CATALOGS_REPO_DIR}"}"
1367 local SW_CATALOGS_REPO_URL="${22:-"${SW_CATALOGS_REPO_URL}"}"
1368 local SKIP_BOOTSTRAP="${23:-"false"}"
1369 # Only change if absolutely needeed
1370 local MGMT_PROJECT_NAME="${24:-"osm_admin"}"
1371 local BASE_TEMPLATES_PATH="${25:-"cloud-resources"}"
1372 local TEMPLATE_MANIFEST_FILENAME="${26:-"openshift01.yaml"}"
1373 local MANIFEST_FILENAME="${27:-"openshift-${CLUSTER_NAME}.yaml"}"
garciadeblasb4769452025-08-06 18:08:18 +02001374
garciadeblas8a28f6d2025-06-11 11:11:56 +02001375 local TEMPLATES_DIR="${SW_CATALOGS_REPO_DIR}/cloud-resources/openshift/templates"
1376 local TARGET_FOLDER="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}"
1377
1378 # Internally ACM creates several projects for each cluster.
1379 # Specifically the klusterletaddonconfig must land in a project with the same name as the cluster.
1380 # This will be specifically controlled by the variable `CLUSTER_PROJECT`.
1381 #
1382 # It must be notes that CLUSTER_NAME, CLUSTER_KUSTOMIZATION_NAME and CLUSTER_PROJECT have the same value,
1383 # but they are conceptually different.
1384 local CLUSTER_PROJECT="${CLUSTER_KUSTOMIZATION_NAME}"
1385
1386 export CLUSTER_KUSTOMIZATION_NAME
1387
1388 folder2list \
1389 "${TEMPLATES_DIR}" | \
1390 replace_env_vars \
1391 '${CLUSTER_KUSTOMIZATION_NAME}' | \
1392 patch_replace \
1393 ".spec.postBuild.substitute.base_domain" \
1394 "${BASE_DOMAIN}" \
1395 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1396 patch_replace \
1397 ".spec.postBuild.substitute.cluster_name" \
1398 "${CLUSTER_NAME}" \
1399 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1400 patch_replace \
1401 ".spec.postBuild.substitute.cluster_project" \
1402 "${CLUSTER_PROJECT}" \
1403 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1404 patch_replace \
1405 ".spec.postBuild.substitute.hosted_cluster_project" \
1406 "${HOSTED_CLUSTERS_PROJECT}" \
1407 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1408 patch_replace \
1409 ".spec.postBuild.substitute.etcd_volume_size" \
1410 "${ETCD_VOLUME_SIZE}Gi" \
1411 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1412 patch_replace \
1413 ".spec.postBuild.substitute.openshift_release" \
1414 "${OPENSHIFT_RELEASE}" \
1415 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1416 patch_replace \
1417 ".spec.postBuild.substitute.storage_class" \
1418 "${STORAGE_CLASS}" \
1419 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1420 patch_replace \
1421 ".spec.postBuild.substitute.control_plane_availability" \
1422 "${CONTROL_PLANE_AVAILABILITY}" \
1423 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1424 patch_replace \
1425 ".spec.postBuild.substitute.worker_count" \
1426 "${WORKER_COUNT}" \
1427 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1428 patch_replace \
1429 ".spec.postBuild.substitute.worker_cores" \
1430 "${WORKER_CORES}" \
1431 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1432 patch_replace \
1433 ".spec.postBuild.substitute.worker_memory" \
1434 "${WORKER_MEMORY}Gi" \
1435 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1436 patch_replace \
1437 ".spec.postBuild.substitute.worker_volume_size" \
1438 "${WORKER_VOLUME_SIZE}Gi" \
1439 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \
1440 patch_replace \
1441 ".spec.postBuild.substitute.cluster_project" \
1442 "${CLUSTER_PROJECT}" \
1443 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}-ns\")" | \
1444 rename_file_in_items \
1445 "${TEMPLATE_MANIFEST_FILENAME}" \
1446 "${MANIFEST_FILENAME}" | \
1447 prepend_folder_path "${CLUSTER_KUSTOMIZATION_NAME}/" | \
1448 list2folder_cp_over \
1449 "${TARGET_FOLDER}"
1450
1451 echo "" | \
1452 make_generator \
1453 "pull-secret.yaml" \
1454 kubectl_encrypt \
1455 "${PUBLIC_KEY_ACM}" \
1456 create \
1457 secret \
1458 generic \
1459 "pullsecret-cluster-${CLUSTER_NAME}" \
1460 --namespace="${HOSTED_CLUSTERS_PROJECT}" \
1461 --from-file=".dockerconfigjson"=<(echo "${DOCKERCONFIGJSON}") \
1462 -o=yaml \
1463 --dry-run=client | \
1464 make_generator \
1465 "ssh-key-secret.yaml" \
1466 kubectl_encrypt \
1467 "${PUBLIC_KEY_ACM}" \
1468 create \
1469 secret \
1470 generic \
1471 "sshkey-cluster-${CLUSTER_NAME}" \
1472 --namespace="${HOSTED_CLUSTERS_PROJECT}" \
1473 --from-file='id_rsa.pub'=<(echo "${INFRA_PUBLIC_SSH_KEY}") \
1474 -o=yaml \
1475 --dry-run=client | \
1476 list2folder_cp_over \
1477 "${TARGET_FOLDER}/${CLUSTER_KUSTOMIZATION_NAME}"
1478
1479 # Bootstrap (unless asked to skip)
1480 if [[ "${SKIP_BOOTSTRAP,,}" == "true" ]]; then
1481 return 0
1482 fi
1483
1484 local CLUSTER_KUBECONFIG_SECRET_NAME="${CLUSTER_KUSTOMIZATION_NAME}-admin-kubeconfig"
1485 local CLUSTER_KUBECONFIG_SECRET_KEY="kubeconfig"
1486 local BOOTSTRAP_KUSTOMIZATION_NAMESPACE="${HOSTED_CLUSTERS_PROJECT}"
1487 local CLUSTER_KUSTOMIZATION_NAMESPACE="managed-resources"
1488 local BOOTSTRAP_SECRET_NAMESPACE="managed-resources"
1489
1490 create_bootstrap_for_remote_cluster \
1491 "${CLUSTER_NAME}" \
1492 "${CLUSTER_KUSTOMIZATION_NAME}" \
1493 "${FLEET_REPO_DIR}" \
1494 "${SW_CATALOGS_REPO_DIR}" \
1495 "${FLEET_REPO_URL}" \
1496 "${SW_CATALOGS_REPO_URL}" \
1497 "${MGMT_PROJECT_NAME}" \
1498 "${PUBLIC_KEY_ACM}" \
1499 "${PUBLIC_KEY_NEW_CLUSTER}" \
1500 "${PRIVATE_KEY_NEW_CLUSTER}" \
1501 "false" \
1502 "${MGMT_CLUSTER_NAME}" \
1503 "${CLUSTER_KUBECONFIG_SECRET_NAME}" \
1504 "${CLUSTER_KUBECONFIG_SECRET_KEY}" \
1505 "${SW_CATALOGS_REPO_DIR}/cloud-resources/flux-remote-bootstrap/cluster-base-openshift/templates" \
1506 "${BOOTSTRAP_KUSTOMIZATION_NAMESPACE}" \
1507 "${CLUSTER_KUSTOMIZATION_NAMESPACE}" \
1508 "${BOOTSTRAP_SECRET_NAMESPACE}"
1509
1510}
1511
1512# Update remote Openshift cluster via ACM
1513function update_openshift_cluster {
1514 local CLUSTER_KUSTOMIZATION_NAME="${1}"
1515 local CLUSTER_NAME="${2}"
1516 # This has to be void. Stored in database
1517 local K8S_VERSION="${3:-"''"}"
1518 # SOPS-AGE related
1519 local PUBLIC_KEY_ACM="${4}"
1520 local PUBLIC_KEY_NEW_CLUSTER="${5:-"${PUBLIC_KEY_NEW_CLUSTER}"}"
1521 local PRIVATE_KEY_NEW_CLUSTER="${6:-"${PRIVATE_KEY_NEW_CLUSTER}"}"
1522 # OpenShift specific
1523 local OPENSHIFT_RELEASE="${7}"
1524 local INFRA_PUBLIC_SSH_KEY="${8}"
1525 local CONTROL_PLANE_AVAILABILITY="${9}"
1526 local WORKER_COUNT="${10}"
1527 local WORKER_CORES="${11}"
1528 local WORKER_MEMORY="${12}"
1529 local WORKER_VOLUME_SIZE="${13}"
1530 local STORAGE_CLASS="${14}"
1531 local BASE_DOMAIN="${15}"
1532 local MGMT_CLUSTER_NAME="${16}"
1533 local HOSTED_CLUSTERS_PROJECT="${17:-"clusters"}"
1534 local ETCD_VOLUME_SIZE="${18:-"8"}"
1535 # GitOps retaled
1536 local FLEET_REPO_DIR="${19:-"${FLEET_REPO_DIR}"}"
1537 local FLEET_REPO_URL="${20:-"${FLEET_REPO_URL}"}"
1538 local SW_CATALOGS_REPO_DIR="${21:-"${SW_CATALOGS_REPO_DIR}"}"
1539 local SW_CATALOGS_REPO_URL="${22:-"${SW_CATALOGS_REPO_URL}"}"
1540 local SKIP_BOOTSTRAP="${23:-"false"}"
1541 # Only change if absolutely needeed
1542 local MGMT_PROJECT_NAME="${24:-"osm_admin"}"
garciadeblasb4769452025-08-06 18:08:18 +02001543
garciadeblas8a28f6d2025-06-11 11:11:56 +02001544 # Determine key folders in Fleet
1545 local MGMT_RESOURCES_DIR="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}"
1546
1547 # Updating no new cluster
1548 local SKIP_BOOTSTRAP="true"
1549
1550 create_openshift_cluster \
1551 "${CLUSTER_KUSTOMIZATION_NAME}" \
1552 "${CLUSTER_NAME}" \
1553 "${K8S_VERSION}" \
1554 "${PUBLIC_KEY_ACM}" \
1555 "${PUBLIC_KEY_NEW_CLUSTER}" \
1556 "${PRIVATE_KEY_NEW_CLUSTER}" \
1557 "${OPENSHIFT_RELEASE}" \
1558 "${INFRA_PUBLIC_SSH_KEY}" \
1559 "${CONTROL_PLANE_AVAILABILITY}" \
1560 "${WORKER_COUNT}" \
1561 "${WORKER_CORES}" \
1562 "${WORKER_MEMORY}" \
1563 "${WORKER_VOLUME_SIZE}" \
1564 "${STORAGE_CLASS}" \
1565 "${BASE_DOMAIN}" \
1566 "${MGMT_CLUSTER_NAME}" \
1567 "${HOSTED_CLUSTERS_PROJECT}" \
1568 "${ETCD_VOLUME_SIZE}" \
1569 "${FLEET_REPO_DIR}" \
1570 "${FLEET_REPO_URL}" \
1571 "${SW_CATALOGS_REPO_DIR}" \
1572 "${SW_CATALOGS_REPO_URL}" \
1573 "${SKIP_BOOTSTRAP}" \
1574 "${MGMT_PROJECT_NAME}"
1575}
garciadeblas70461c52024-07-03 09:17:56 +02001576
1577# ----- Helper functions for adding/removing a profile from a cluster -----
1578
1579# Helper function to find profiles of a given type already used in the cluster
1580function profiles_of_type_in_cluster() {
1581 local CLUSTER_KUSTOMIZATION_NAME="$1"
1582 local RELEVANT_PROFILE_TYPE="$2"
1583 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
1584
1585 # Calculated fields
1586 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
1587
1588 # Processing (echoes the list)
1589 folder2list \
1590 "${CLUSTER_FOLDER}" | \
1591 get_value_from_resourcelist \
1592 ".metadata.name" \
1593 "| select(.kind == \"Kustomization\")
1594 | select(.metadata.labels.osm_profile_type == \"${RELEVANT_PROFILE_TYPE}\")" | \
1595 multiline2commalist
1596}
1597
1598
1599# Function to list the profiles **this profile depends on**
1600function profiles_this_one_depends_on() {
1601 local CLUSTER_KUSTOMIZATION_NAME="$1"
1602 local PROFILE_TYPE="$2"
1603 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
1604
1605 case "${PROFILE_TYPE,,}" in
1606
1607 "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers")
1608 # Controllers do not depend on any other type of profiles
1609 echo ""
1610 return 0
1611 ;;
1612
1613 "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs")
1614 # Infra configs depend on controllers
1615 profiles_of_type_in_cluster \
1616 "${CLUSTER_KUSTOMIZATION_NAME}" \
1617 "infra-controllers" \
1618 "${FLEET_REPO_DIR}"
1619 return 0
1620 ;;
1621
1622 "managed" | "resources" | "managed-resources" | "managed_resources")
1623 # Managed resources depend on infra configs
1624 profiles_of_type_in_cluster \
1625 "${CLUSTER_KUSTOMIZATION_NAME}" \
1626 "infra-configs" \
1627 "${FLEET_REPO_DIR}"
1628 return 0
1629 ;;
1630
1631 "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs")
1632 # Apps (also) depend on infra configs
1633 profiles_of_type_in_cluster \
1634 "${CLUSTER_KUSTOMIZATION_NAME}" \
1635 "infra-configs" \
1636 "${FLEET_REPO_DIR}"
1637 return 0
1638 ;;
1639
1640 *)
1641 echo -n "------------ ERROR ------------"
1642 return 1
1643 ;;
1644 esac
1645}
1646
1647
1648# Function to list the profiles that **depend on this profile**
1649function profiles_depend_on_this_one() {
1650 local CLUSTER_KUSTOMIZATION_NAME="$1"
1651 local PROFILE_TYPE="$2"
1652 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
1653
1654 case "${PROFILE_TYPE,,}" in
1655
1656 "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers")
1657 # Infra configs depend on infra controllers
1658 profiles_of_type_in_cluster \
1659 "${CLUSTER_KUSTOMIZATION_NAME}" \
1660 "infra-configs" \
1661 "${FLEET_REPO_DIR}"
1662 return 0
1663 ;;
1664
1665 "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs")
1666 # Both managed resources and apps depend on configs
1667 local PROFILES=(
1668 $(
1669 profiles_of_type_in_cluster \
1670 "${CLUSTER_KUSTOMIZATION_NAME}" \
1671 "managed-resources" \
1672 "${FLEET_REPO_DIR}"
1673 ) \
1674 $(
1675 profiles_of_type_in_cluster \
1676 "${CLUSTER_KUSTOMIZATION_NAME}" \
1677 "apps" \
1678 "${FLEET_REPO_DIR}"
1679 )
1680 )
1681 printf '%s,' "${PROFILES[@]}" | sed 's/,$//g'
1682 return 0
1683 ;;
1684
1685 "managed" | "resources" | "managed-resources" | "managed_resources")
1686 # No other profiles depend on managed resources
1687 echo ""
1688 return 0
1689 ;;
1690
1691 "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs")
1692 # No other profiles depend on apps
1693 echo ""
1694 return 0
1695 ;;
1696
1697 *)
1698 echo -n "------------ ERROR ------------"
1699 return 1
1700 ;;
1701 esac
1702}
1703
1704
1705# Helper function to add a dependency to a Kustomization only if it does not exist already
1706function add_dependency_to_kustomization_safely() {
1707 local KUSTOMIZATION_NAME="$1"
1708 local KUSTOMIZATION_TO_ADD_AS_DEP="$2"
1709
1710 local INPUT=$(cat)
1711 local FILTER="| select(.kind == \"Kustomization\") | select(.metadata.name == \"${KUSTOMIZATION_NAME}\")"
1712
1713 # Check if the dependency was added already
1714 local TEST_RESULT=$(
1715 echo "${INPUT}" | \
1716 is_element_on_list \
1717 ".spec.dependsOn[].name" \
1718 "${KUSTOMIZATION_TO_ADD_AS_DEP}" \
1719 "${FILTER}"
1720 )
1721
1722 # If it existed already, returns the stream as is
1723 if [[ "${TEST_RESULT}" == "true" ]]
1724 then
1725 echo "${INPUT}"
1726 # Otherwise, processes the stream to add it
1727 else
1728 echo "${INPUT}" | \
1729 patch_add_to_list \
1730 ".spec.dependsOn" \
1731 "{name: ${KUSTOMIZATION_TO_ADD_AS_DEP}}" \
1732 "${FILTER}"
1733 fi
1734}
1735
1736
1737# Helper function to remove a dependency from a Kustomization
1738function remove_dependency_from_kustomization_safely() {
1739 local KUSTOMIZATION_NAME="$1"
1740 local KUSTOMIZATION_TO_REMOVE_AS_DEP="$2"
1741
1742 # Calculated inputs
1743 local KEY_PATH=".spec.dependsOn[] | select(.name == \"${KUSTOMIZATION_TO_REMOVE_AS_DEP}\")"
1744 local FILTER="| select(.kind == \"Kustomization\") | select(.metadata.name == \"${KUSTOMIZATION_NAME}\")"
1745
1746 # Remove the entry from the dependency list (if it exists)
1747 yq "del((.items[]${FILTER})${KEY_PATH})"
1748}
1749
1750
1751# Ensure list of Kustomizations depend on a given Kustomization
1752function add_dependency_to_set_of_kustomizations_safely() {
1753 local KS_NAME="$1"
1754 local THEY_DEPEND_ON_THIS="$2"
1755
1756 local INPUT="$(cat)"
1757 local OUTPUT=""
1758
1759 # For each of the Kustomizations on the comma-separated list, adds `KS_NAME` as one of their dependencies
1760 for KUST in ${THEY_DEPEND_ON_THIS//,/ }
1761 do
1762 local OUTPUT="$(
1763 echo "${INPUT}" | \
1764 add_dependency_to_kustomization_safely \
1765 "${KUST}" \
1766 "${KS_NAME}"
1767 )"
1768 local INPUT="${OUTPUT}"
1769 done
1770
1771 # Return the final `ResultList`, after all iterations
1772 echo "${OUTPUT}"
1773}
1774
1775
1776# Ensure list of Kustomizations no longer depend on a given Kustomization
1777function remove_dependency_from_set_of_kustomizations_safely() {
1778 local KS_NAME="$1"
1779 local THEY_NO_LONGER_DEPEND_ON_THIS="$2"
1780
1781 local INPUT="$(cat)"
1782 local OUTPUT=""
1783
1784 # For each of the Kustomizations on the comma-separated list, removes `KS_NAME` from their dependencies
1785 for KUST in ${THEY_NO_LONGER_DEPEND_ON_THIS//,/ }
1786 do
1787 local OUTPUT="$(
1788 echo "${INPUT}" | \
1789 remove_dependency_from_kustomization_safely \
1790 "${KUST}" \
1791 "${KS_NAME}"
1792 )"
1793 local INPUT="${OUTPUT}"
1794 done
1795
1796 # Return the final `ResultList`, after all iterations
1797 echo "${OUTPUT}"
1798}
1799
1800# ----- END of Helper functions for adding/removing a profile from a cluster -----
1801
1802
1803# Add an existing profile to a cluster
1804function attach_profile_to_cluster() {
1805 local PROFILE_NAME="$1"
1806 local PROFILE_TYPE="$2"
1807 local PROJECT_NAME="$3"
1808 local CLUSTER_KUSTOMIZATION_NAME="$4"
1809 local FLEET_REPO_DIR="${5:-"${FLEET_REPO_DIR}"}"
1810
1811 # Calculated inputs
1812 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
1813 local TARGET_PROFILE_PATH="$(
1814 path_to_profile \
1815 "${PROFILE_NAME}" \
1816 "${PROFILE_TYPE}" \
1817 "${PROJECT_NAME}"
1818 )"
1819
1820 # Finds out which profiles it should depend on... and which profiles should depend on it
1821 local DEPENDS_ON=$(
1822 profiles_this_one_depends_on \
1823 "${CLUSTER_KUSTOMIZATION_NAME}" \
1824 "${PROFILE_TYPE}" \
1825 "${FLEET_REPO_DIR}"
1826 )
1827
1828 local THEY_DEPEND_ON_THIS=$(
1829 profiles_depend_on_this_one \
1830 "${CLUSTER_KUSTOMIZATION_NAME}" \
1831 "${PROFILE_TYPE}" \
1832 "${FLEET_REPO_DIR}"
1833 )
1834
1835 # Parameters for the new Kustomization object to point to the profile
1836 local KS_NAME="$(safe_name "${PROFILE_TYPE}-${PROFILE_NAME}")"
1837 local MANIFEST_FILENAME="${KS_NAME}.yaml"
1838 local KS_NS=flux-system
1839 local MANIFESTS_PATH="${TARGET_PROFILE_PATH}"
1840 local SOURCE_REPO=GitRepository/fleet-repo.flux-system
1841 local SOURCE_SYNC_INTERVAL="60m"
1842 local HEALTH_CHECK_TO="3m"
1843 local RETRY_INTERVAL="1m"
1844 local TIMEOUT="5m"
1845 local OPTIONS="\
1846 --decryption-provider=sops \
1847 --decryption-secret=sops-age \
1848 --prune=true \
1849 --timeout="${TIMEOUT}" \
1850 --retry-interval="${RETRY_INTERVAL}" \
1851 --label osm_profile_type="${PROFILE_TYPE}"
1852 "
1853
1854 # Finally, we update the folder with all the required changes:
1855 # - Update pre-existing Kustomizations that should depend on the new profile (besides others).
1856 # - Create a new Kustomization pointing to the profile.
1857 # - Update Kustomize's `kustomization.yaml` at the root of the cluster folder to take into account the new Kustomization pointing to the profile.
1858 # - Update the cluster folder accordingly.
1859 folder2list \
1860 "${CLUSTER_FOLDER}" |
1861 add_dependency_to_set_of_kustomizations_safely \
1862 "${KS_NAME}" \
1863 "${THEY_DEPEND_ON_THIS}" | \
1864 generator_kustomization \
1865 "${MANIFEST_FILENAME}" \
1866 "${KS_NAME}" \
1867 "${KS_NS}" \
1868 "${SOURCE_REPO}" \
1869 "${MANIFESTS_PATH}" \
1870 "${SOURCE_SYNC_INTERVAL}" \
1871 "${HEALTH_CHECK_TO}" \
1872 "${DEPENDS_ON}" \
1873 "${OPTIONS}" | \
1874 patch_add_to_list \
1875 ".resources" \
1876 "${MANIFEST_FILENAME}" \
1877 "| select(.kind == \"Kustomization\") | select(.apiVersion == \"kustomize.config.k8s.io/v1beta1\") | select(.metadata.annotations.\"config.kubernetes.io/path\" == \"kustomization.yaml\")" | \
1878 list2folder_sync_replace \
1879 "${CLUSTER_FOLDER}"
1880}
1881
1882
1883# Remove an existing profile from a cluster
1884function detach_profile_from_cluster() {
1885 local PROFILE_NAME="$1"
1886 local PROFILE_TYPE="$2"
1887 local PROJECT_NAME="$3"
1888 local CLUSTER_KUSTOMIZATION_NAME="$4"
1889 local FLEET_REPO_DIR="${5:-"${FLEET_REPO_DIR}"}"
1890
1891 # Calculated inputs
1892 local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}"
1893 local TARGET_PROFILE_PATH="$(
1894 path_to_profile \
1895 "${PROFILE_NAME}" \
1896 "${PROFILE_TYPE}" \
1897 "${PROJECT_NAME}"
1898 )"
1899
1900 # Finds out which profiles still depend on it
1901 local THEY_DEPEND_ON_THIS=$(
1902 profiles_depend_on_this_one \
1903 "${CLUSTER_KUSTOMIZATION_NAME}" \
1904 "${PROFILE_TYPE}" \
1905 "${FLEET_REPO_DIR}"
1906 )
1907
1908 # Parameters for the new Kustomization object to point to the profile
1909 local KS_NAME="$(safe_name "${PROFILE_TYPE}-${PROFILE_NAME}")"
1910
1911 # Finally, we update the folder with all the required changes:
1912 # - Update pre-existing Kustomizations that should depend on the new profile (besides others).
1913 # - Create a new Kustomization pointing to the profile.
1914 # - 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.
1915 # - Update the cluster folder accordingly.
1916 folder2list \
1917 "${CLUSTER_FOLDER}" |
1918 remove_dependency_from_set_of_kustomizations_safely \
1919 "${KS_NAME}" \
1920 "${THEY_DEPEND_ON_THIS}" | \
1921 delete_object \
1922 "${KS_NAME}" \
1923 "Kustomization" \
1924 "kustomize.toolkit.fluxcd.io/v1" | \
1925 patch_delete_from_list \
1926 ".resources[] | select(. == \"${MANIFEST_FILENAME}\") " \
1927 "| select(.kind == \"Kustomization\") | select(.apiVersion == \"kustomize.config.k8s.io/v1beta1\") | select(.metadata.annotations.\"config.kubernetes.io/path\" == \"kustomization.yaml\")" | \
1928 list2folder_sync_replace \
1929 "${CLUSTER_FOLDER}"
1930}
1931
1932
1933# Low-level function to add a KSU into a profile
1934function create_ksu_into_profile() {
1935 local KSU_NAME="$1"
1936 local TARGET_PROFILE_FOLDER="$2"
1937 local TEMPLATES_PATH="$3"
1938 local SW_CATALOGS_REPO_DIR="$4"
1939 local TRANSFORMER="${5:-noop_transformer}"
1940
1941 # Gathers all optional parameters for transformer funcion (if any) and puts them into an array for further use
1942 local ALL_PARAMS=( "${@}" )
1943 local TRANSFORMER_ARGS=( "${ALL_PARAMS[@]:5}" )
1944
1945 # Composes the route to the local templates folder
1946 local TEMPLATES_FOLDER="${SW_CATALOGS_REPO_DIR}/${TEMPLATES_PATH}"
1947
1948 folder2list \
1949 "${TEMPLATES_FOLDER}" | \
1950 "${TRANSFORMER}" \
1951 "${TRANSFORMER_ARGS[@]}" | \
1952 prepend_folder_path "${KSU_NAME}/" | \
1953 list2folder_cp_over \
1954 "${TARGET_PROFILE_FOLDER}"
1955}
1956
1957
1958# Function to render a KSU from a `ResourceList` into a profile
1959function render_ksu_into_profile() {
1960 local KSU_NAME="$1"
1961 local PROFILE_NAME="$2"
1962 local PROFILE_TYPE="$3"
1963 local PROJECT_NAME="${4:-"${MGMT_PROJECT_NAME}"}"
1964 local FLEET_REPO_DIR="$5"
1965 local SYNC="${6:-"false"}"
1966
1967 local TARGET_PROFILE_PATH=$(
1968 path_to_profile \
1969 "${PROFILE_NAME}" \
1970 "${PROFILE_TYPE}" \
1971 "${PROJECT_NAME}"
1972 )
1973
1974 local TARGET_PROFILE_FOLDER="${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
1975
1976 # Determines the appropriate function depending on rendering strategy
1977 # - Sync (and potentially delete files in target folder)
1978 # - Copy over (only overwrite changed files, keep the rest)
1979 RENDERER=""
1980 if [[ ${SYNC,,} == "true" ]];
1981 then
1982 RENDERER="list2folder_sync_replace"
1983 else
1984 RENDERER="list2folder_cp_over"
1985 fi
1986
1987 # Render with the selected strategy
1988 [[ "${DRY_RUN,,}" != "true" ]] && mkdir -p "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
1989 "${RENDERER}" \
1990 "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
1991 ## This is improves the behaviour of the following code,
1992 ## since avoids unintented deletions in parent folder due to sync
1993 # prepend_folder_path "${KSU_NAME}/" | \
1994 # "${RENDERER}" \
1995 # "${TARGET_PROFILE_FOLDER}"
1996}
1997
1998
1999# High-level function to add a KSU into a profile for the case where
2000# 1. It is originated from an OKA, and
2001# 2. It is based on a HelmRelease.
2002function create_hr_ksu_into_profile() {
2003 # Base KSU generation from template
2004 ## `TEMPLATES_DIR` is the result of:
2005 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}/{{inputs.parameters.templates_path}}"
2006 local TEMPLATES_DIR="$1"
2007 local SUBSTITUTE_ENVIRONMENT="${2:-"false"}"
2008 local SUBSTITUTION_FILTER="${3:-""}"
2009 local CUSTOM_ENV_VARS="${4:-""}"
2010 # Patch HelmRelease in KSU with inline values
2011 local KUSTOMIZATION_NAME="$5"
2012 local HELMRELEASE_NAME="$6"
2013 local INLINE_VALUES="${7:-""}"
2014 # Secret reference and generation (if required)
2015 local IS_PREEXISTING_SECRET="${8:-"false"}"
2016 local TARGET_NS="$9"
2017 local VALUES_SECRET_NAME="${10}"
2018 local SECRET_KEY="${11:-"values.yaml"}"
2019 local AGE_PUBLIC_KEY="${12}"
2020 ## `SECRET_VALUES` will be obtained from the
2021 ## secret named after the input parameter `reference_secret_for_values`,
2022 ## and from the key named after the input parameter `reference_key_for_values`
2023 local LOCAL_SECRET_VALUES="${13:-"${SECRET_VALUES}"}"
2024 # ConfigMap reference and generation (if required)
2025 local IS_PREEXISTING_CM="${14:-"false"}"
2026 local VALUES_CM_NAME="${15:-""}"
2027 local CM_KEY="${16:-""}"
2028 local CM_VALUES="${17:-""}"
2029 # KSU rendering
2030 local KSU_NAME="${18}"
2031 local PROFILE_NAME="${19}"
2032 local PROFILE_TYPE="${20}"
2033 local PROJECT_NAME="${21:-"osm_admin"}"
2034 ## `FLEET_REPO_DIR` is the result of:
2035 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
2036 local FLEET_REPO_DIR="${22:-"/fleet/fleet-osm/"}"
2037 local SYNC="${23:-"true"}"
2038
2039 # Decides which steps may be skipped
2040 HAS_INLINE_VALUES=$([[ -n "${INLINE_VALUES}" ]]; echo $?)
2041 HAS_REFERENCES=$([[ ( -n "${VALUES_SECRET_NAME}" ) || ( -n "${VALUES_CM_NAME}" ) ]]; echo $?)
2042 NEEDS_NEW_SECRET=$([[ ( -n "${VALUES_SECRET_NAME}" ) && ( "${IS_PREEXISTING_SECRET,,}" == "false" ) ]]; echo $?)
2043 NEEDS_NEW_CM=$([[ ( -n "${VALUES_CM_NAME}" ) && ( "${IS_PREEXISTING_CM,,}" == "false" ) ]]; echo $?)
2044 ECHO_RESOURCELIST=$([[ "${DEBUG,,}" == "true" ]]; echo $?)
2045
2046 # If applicable, loads additional environment variables
2047 if [[ -n "${CUSTOM_ENV_VARS}" ]];
2048 then
2049 set -a
2050 source <(echo "${CUSTOM_ENV_VARS}")
2051 set +a
2052 fi
2053
2054 # Runs workflow
2055 folder2list_generator \
2056 "${TEMPLATES_DIR}" \
2057 "${SUBSTITUTE_ENVIRONMENT}" \
2058 "${SUBSTITUTION_FILTER}" | \
2059 transform_if \
2060 "${HAS_INLINE_VALUES}" \
2061 add_values_to_helmrelease_via_ks \
2062 "${KUSTOMIZATION_NAME}" \
2063 "${HELMRELEASE_NAME}" \
2064 "${INLINE_VALUES}" | \
2065 transform_if \
2066 "${HAS_REFERENCES}" \
2067 add_ref_values_to_hr_via_ks \
2068 "${KUSTOMIZATION_NAME}" \
2069 "${HELMRELEASE_NAME}" \
2070 "${VALUES_SECRET_NAME}" \
2071 "${VALUES_CM_NAME}" | \
2072 transform_if \
2073 "${NEEDS_NEW_SECRET}" \
2074 make_generator \
2075 "hr-values-secret.yaml" \
2076 kubectl_encrypt \
2077 "${AGE_PUBLIC_KEY}" \
2078 create \
2079 secret \
2080 generic \
2081 "${VALUES_SECRET_NAME}" \
2082 --namespace="${TARGET_NS}" \
2083 --from-file="${SECRET_KEY}"=<(echo "${LOCAL_SECRET_VALUES}") \
2084 -o=yaml \
2085 --dry-run=client | \
2086 transform_if \
2087 "${NEEDS_NEW_CM}" \
2088 make_generator \
2089 "hr-values-configmap.yaml" \
2090 kubectl \
2091 create \
2092 configmap \
2093 "${VALUES_CM_NAME}" \
2094 --namespace="${TARGET_NS}" \
2095 --from-file="${SECRET_KEY}"=<(echo "${CM_VALUES}") \
2096 -o=yaml \
2097 --dry-run=client | \
2098 transform_if \
2099 "${ECHO_RESOURCELIST}" \
2100 tee /dev/stderr | \
2101 render_ksu_into_profile \
2102 "${KSU_NAME}" \
2103 "${PROFILE_NAME}" \
2104 "${PROFILE_TYPE}" \
2105 "${PROJECT_NAME}" \
2106 "${FLEET_REPO_DIR}" \
2107 "${SYNC}"
2108}
2109
2110
2111# High-level function to update a KSU for the case where
2112# 1. It is originated from an OKA, and
2113# 2. It is based on a HelmRelease.
2114# NOTE: It is an alias of `create_hr_ksu_into_profile`, setting `sync` to true
2115function update_hr_ksu_into_profile() {
2116 # Base KSU generation from template
2117 ## `TEMPLATES_DIR` is the result of:
2118 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}/{{inputs.parameters.templates_path}}"
2119 local TEMPLATES_DIR="$1"
2120 local SUBSTITUTE_ENVIRONMENT="${2:-"false"}"
2121 local SUBSTITUTION_FILTER="${3:-""}"
2122 local CUSTOM_ENV_VARS="${4:-""}"
2123 # Patch HelmRelease in KSU with inline values
2124 local KUSTOMIZATION_NAME="$5"
2125 local HELMRELEASE_NAME="$6"
2126 local INLINE_VALUES="${7:-""}"
2127 # Secret reference and generation (if required)
2128 local IS_PREEXISTING_SECRET="${8:-"false"}"
2129 local TARGET_NS="$9"
2130 local VALUES_SECRET_NAME="${10}"
2131 local SECRET_KEY="${11:-"values.yaml"}"
2132 local AGE_PUBLIC_KEY="${12}"
2133 ## `SECRET_VALUES` will be obtained from the
2134 ## secret named after the input parameter `reference_secret_for_values`,
2135 ## and from the key named after the input parameter `reference_key_for_values`
2136 local LOCAL_SECRET_VALUES="${13:-"${SECRET_VALUES}"}"
2137 # ConfigMap reference and generation (if required)
2138 local IS_PREEXISTING_CM="${14:-"false"}"
2139 local VALUES_CM_NAME="${15:-""}"
2140 local CM_KEY="${16:-""}"
2141 local CM_VALUES="${17:-""}"
2142 # KSU rendering
2143 local KSU_NAME="${18}"
2144 local PROFILE_NAME="${19}"
2145 local PROFILE_TYPE="${20}"
2146 local PROJECT_NAME="${21:-"osm_admin"}"
2147 ## `FLEET_REPO_DIR` is the result of:
2148 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
2149 local FLEET_REPO_DIR="${22:-"/fleet/fleet-osm/"}"
2150 # local SYNC="${23:-"true"}"
2151
2152
2153 # This function is just an alias of `create_hr_ksu_into_profile`
2154 # forcing synchronization over the KSU folder
2155 create_hr_ksu_into_profile \
2156 "${TEMPLATES_DIR}" \
2157 "${SUBSTITUTE_ENVIRONMENT}" \
2158 "${SUBSTITUTION_FILTER}" \
2159 "${CUSTOM_ENV_VARS}" \
2160 "${KUSTOMIZATION_NAME}" \
2161 "${HELMRELEASE_NAME}" \
2162 "${INLINE_VALUES}" \
2163 "${IS_PREEXISTING_SECRET}" \
2164 "${TARGET_NS}" \
2165 "${VALUES_SECRET_NAME}" \
2166 "${SECRET_KEY}" \
2167 "${AGE_PUBLIC_KEY}" \
2168 "${LOCAL_SECRET_VALUES}" \
2169 "${IS_PREEXISTING_CM}" \
2170 "${VALUES_CM_NAME}" \
2171 "${CM_KEY}" \
2172 "${CM_VALUES}" \
2173 "${KSU_NAME}" \
2174 "${PROFILE_NAME}" \
2175 "${PROFILE_TYPE}" \
2176 "${PROJECT_NAME}" \
2177 "${FLEET_REPO_DIR}" \
2178 "true"
2179}
2180
2181
2182# High-level function to create a "generated" KSU into a profile when:
2183# 1. There is no template (OKA) available.
2184# 2. The SW is based on a Helm Chart that we want to deploy.
2185function create_generated_ksu_from_helm_into_profile() {
2186 # HelmRelease generation
2187 local HELMRELEASE_NAME="$1"
2188 local CHART_NAME="$2"
2189 local CHART_VERSION="$3"
2190 local TARGET_NS="$4"
2191 local CREATE_NS="${5:-"true"}"
2192 # Repo source generation
2193 local IS_PREEXISTING_REPO="${6:-"false"}"
2194 local HELMREPO_NAME="$7"
2195 local HELMREPO_URL="${8:-""}"
2196 local HELMREPO_NS="${9:-"${TARGET_NS}"}"
2197 local HELMREPO_SECRET_REF="${10:-""}"
2198 # HelmRelease inline values (if any)
2199 local INLINE_VALUES="${11:-""}"
2200 # Secret reference and generation (if required)
2201 local IS_PREEXISTING_SECRET="${12:-"false"}"
2202 local VALUES_SECRET_NAME="${13}"
2203 local SECRET_KEY="${14:-"values.yaml"}"
2204 local AGE_PUBLIC_KEY="${15}"
2205 ## `SECRET_VALUES` will be obtained from the
2206 ## secret named after the input parameter `reference_secret_for_values`,
2207 ## and from the key named after the input parameter `reference_key_for_values`
2208 local LOCAL_SECRET_VALUES="${16:-"${SECRET_VALUES}"}"
2209 # ConfigMap reference and generation (if required)
2210 local IS_PREEXISTING_CM="${17:-"false"}"
2211 local VALUES_CM_NAME="${18:-""}"
2212 local CM_KEY="${19:-""}"
2213 local CM_VALUES="${20:-""}"
2214 # KSU rendering
2215 local KSU_NAME="${21}"
2216 local PROFILE_NAME="${22}"
2217 local PROFILE_TYPE="${23}"
2218 local PROJECT_NAME="${24:-"osm_admin"}"
2219 ## `FLEET_REPO_DIR` is the result of:
2220 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
2221 local FLEET_REPO_DIR="${25:-"/fleet/fleet-osm/"}"
2222 # By default, it will not syncronize, so that we can easily accumulate more than
2223 # one Helm chart into the same KSU if desired
2224 local SYNC="${26:-"false"}"
2225
2226 # Decides which steps may be skipped
2227 local NEEDS_NEW_NS=$([[ "${CREATE_NS,,}" == "true" ]]; echo $?)
2228 local NEEDS_NEW_REPO_SOURCE=$([[ "${IS_PREEXISTING_REPO,,}" == "false" ]]; echo $?)
2229 local NEEDS_NEW_SECRET=$([[ ( -n "${VALUES_SECRET_NAME}" ) && ( "${IS_PREEXISTING_SECRET,,}" == "false" ) ]]; echo $?)
2230 local NEEDS_NEW_CM=$([[ ( -n "${VALUES_CM_NAME}" ) && ( "${IS_PREEXISTING_CM,,}" == "false" ) ]]; echo $?)
2231 local ECHO_RESOURCELIST=$([[ "${DEBUG,,}" == "true" ]]; echo $?)
2232
2233 # Determine extra options for HelmRelease creation and define full command
2234 OPTION_CHART_VERSION=""
2235 [[ -n "${CHART_VERSION}" ]] && OPTION_CHART_VERSION='--chart-version=${CHART_VERSION}'
2236 OPTION_INLINE_VALUES=""
2237 [[ -n "${INLINE_VALUES}" ]] && OPTION_INLINE_VALUES='--values=<(
2238 echo "${INLINE_VALUES}"
2239 )'
2240 OPTION_REFERENCE_SECRET=""
2241 [[ -n "${VALUES_SECRET_NAME}" ]] && OPTION_REFERENCE_SECRET='--values-from=Secret/${VALUES_SECRET_NAME}'
2242 OPTION_REFERENCE_CM=""
2243 [[ -n "${VALUES_CM_NAME}" ]] && OPTION_REFERENCE_CM='--values-from=ConfigMap/${VALUES_CM_NAME}'
2244
2245 export HR_COMMAND="\
2246 flux \
2247 -n "${TARGET_NS}" \
2248 create hr "${HELMRELEASE_NAME}" \
2249 --chart="${CHART_NAME}" \
2250 --source=HelmRepository/"${HELMREPO_NAME}.${HELMREPO_NS}" \
2251 "${OPTION_CHART_VERSION}" \
2252 "${OPTION_INLINE_VALUES}" \
2253 "${OPTION_REFERENCE_SECRET}" \
2254 "${OPTION_REFERENCE_CM}" \
2255 --export
2256 "
2257
2258 # Determine extra options for Helm source repo creation and define full command
2259 OPTION_REPO_SECRET=""
2260 [[ -n "${HELMREPO_SECRET_REF}" ]] && OPTION_REPO_SECRET='--secret-ref=${HELMREPO_SECRET_REF}'
2261
2262 export REPO_COMMAND="\
2263 flux \
2264 -n "${HELMREPO_NS}" \
2265 create source helm "${HELMREPO_NAME}" \
2266 --url="${HELMREPO_URL}" \
2267 "${OPTION_REPO_SECRET}" \
2268 --export
2269 "
2270
2271 # Runs workflow
2272 echo "" | \
2273 make_generator \
2274 "helm-release.yaml" \
2275 eval "${HR_COMMAND}" | \
2276 transform_if \
2277 "${NEEDS_NEW_NS}" \
2278 make_generator \
2279 "ns-for-hr.yaml" \
2280 kubectl \
2281 create \
2282 namespace \
2283 "${TARGET_NS}" \
2284 -o=yaml \
2285 --dry-run=client | \
2286 transform_if \
2287 "${NEEDS_NEW_REPO_SOURCE}" \
2288 make_generator \
2289 "helm-repo.yaml" \
2290 eval "${REPO_COMMAND}" | \
2291 transform_if \
2292 "${NEEDS_NEW_SECRET}" \
2293 make_generator \
2294 "hr-values-secret.yaml" \
2295 kubectl_encrypt \
2296 "${AGE_PUBLIC_KEY}" \
2297 create \
2298 secret \
2299 generic \
2300 "${VALUES_SECRET_NAME}" \
2301 --namespace="${TARGET_NS}" \
2302 --from-file="${SECRET_KEY}"=<(echo "${LOCAL_SECRET_VALUES}") \
2303 -o=yaml \
2304 --dry-run=client | \
2305 transform_if \
2306 "${NEEDS_NEW_CM}" \
2307 make_generator \
2308 "hr-values-configmap.yaml" \
2309 kubectl \
2310 create \
2311 configmap \
2312 "${VALUES_CM_NAME}" \
2313 --namespace="${TARGET_NS}" \
2314 --from-file="${SECRET_KEY}"=<(echo "${CM_VALUES}") \
2315 -o=yaml \
2316 --dry-run=client | \
2317 transform_if \
2318 "${ECHO_RESOURCELIST}" \
2319 tee /dev/stderr | \
2320 render_ksu_into_profile \
2321 "${KSU_NAME}" \
2322 "${PROFILE_NAME}" \
2323 "${PROFILE_TYPE}" \
2324 "${PROJECT_NAME}" \
2325 "${FLEET_REPO_DIR}" \
2326 "${SYNC}"
2327}
2328
2329
2330# High-level function to update a "generated" KSU:
2331# 1. There is no template (OKA) available.
2332# 2. The SW is based on a Helm Chart that we want to deploy.
2333# NOTE: It is an alias of `create_generated_ksu_from_helm_into_profile`, setting `sync` to true
2334function update_generated_ksu_from_helm_into_profile() {
2335 # HelmRelease generation
2336 local HELMRELEASE_NAME="$1"
2337 local CHART_NAME="$2"
2338 local CHART_VERSION="$3"
2339 local TARGET_NS="$4"
2340 local CREATE_NS="${5:-"true"}"
2341 # Repo source generation
2342 local IS_PREEXISTING_REPO="${6:-"false"}"
2343 local HELMREPO_NAME="$7"
2344 local HELMREPO_URL="${8:-""}"
2345 local HELMREPO_NS="${9:-"${TARGET_NS}"}"
2346 local HELMREPO_SECRET_REF="${10:-""}"
2347 # HelmRelease inline values (if any)
2348 local INLINE_VALUES="${11:-""}"
2349 # Secret reference and generation (if required)
2350 local IS_PREEXISTING_SECRET="${12:-"false"}"
2351 local VALUES_SECRET_NAME="${13}"
2352 local SECRET_KEY="${14:-"values.yaml"}"
2353 local AGE_PUBLIC_KEY="${15}"
2354 ## `SECRET_VALUES` will be obtained from the
2355 ## secret named after the input parameter `reference_secret_for_values`,
2356 ## and from the key named after the input parameter `reference_key_for_values`
2357 local LOCAL_SECRET_VALUES="${16:-"${SECRET_VALUES}"}"
2358 # ConfigMap reference and generation (if required)
2359 local IS_PREEXISTING_CM="${17:-"false"}"
2360 local VALUES_CM_NAME="${18:-""}"
2361 local CM_KEY="${19:-""}"
2362 local CM_VALUES="${20:-""}"
2363 # KSU rendering
2364 local KSU_NAME="${21}"
2365 local PROFILE_NAME="${22}"
2366 local PROFILE_TYPE="${23}"
2367 local PROJECT_NAME="${24:-"osm_admin"}"
2368 ## `FLEET_REPO_DIR` is the result of:
2369 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
2370 local FLEET_REPO_DIR="${25:-"/fleet/fleet-osm/"}"
2371 # By default, it will not syncronize, so that we can easily accumulate more than
2372 # one Helm chart into the same KSU if desired
2373 # local SYNC="${26:-"false"}"
2374
2375 # Decides which steps may be skipped
2376 local NEEDS_NEW_NS=$([[ "${CREATE_NS,,}" == "true" ]]; echo $?)
2377 local NEEDS_NEW_REPO_SOURCE=$([[ "${IS_PREEXISTING_REPO,,}" == "false" ]]; echo $?)
2378 local NEEDS_NEW_SECRET=$([[ ( -n "${VALUES_SECRET_NAME}" ) && ( "${IS_PREEXISTING_SECRET,,}" == "false" ) ]]; echo $?)
2379 local NEEDS_NEW_CM=$([[ ( -n "${VALUES_CM_NAME}" ) && ( "${IS_PREEXISTING_CM,,}" == "false" ) ]]; echo $?)
2380 local ECHO_RESOURCELIST=$([[ "${DEBUG,,}" == "true" ]]; echo $?)
2381
2382
2383 # This function is just an alias of `create_generated_ksu_from_helm_into_profile`
2384 # forcing synchronization over the KSU folder
2385 create_generated_ksu_from_helm_into_profile \
2386 "${HELMRELEASE_NAME}" \
2387 "${CHART_NAME}" \
2388 "${CHART_VERSION}" \
2389 "${TARGET_NS}" \
2390 "${CREATE_NS}" \
2391 "${IS_PREEXISTING_REPO}" \
2392 "${HELMREPO_NAME}" \
2393 "${HELMREPO_URL}" \
2394 "${HELMREPO_NS}" \
2395 "${HELMREPO_SECRET_REF}" \
2396 "${INLINE_VALUES}" \
2397 "${IS_PREEXISTING_SECRET}" \
2398 "${VALUES_SECRET_NAME}" \
2399 "${SECRET_KEY}" \
2400 "${AGE_PUBLIC_KEY}" \
2401 "${LOCAL_SECRET_VALUES}" \
2402 "${IS_PREEXISTING_CM}" \
2403 "${VALUES_CM_NAME}" \
2404 "${CM_KEY}" \
2405 "${CM_VALUES}" \
2406 "${KSU_NAME}" \
2407 "${PROFILE_NAME}" \
2408 "${PROFILE_TYPE}" \
2409 "${PROJECT_NAME}" \
2410 "${FLEET_REPO_DIR}" \
2411 "true"
2412}
2413
2414
2415# Low-level function to delete a KSU from a profile
2416function delete_ksu_from_profile_path() {
2417 local KSU_NAME="$1"
2418 local TARGET_PROFILE_PATH="$2"
2419 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
2420
2421 # Calculate profile folder
2422 TARGET_PROFILE_FOLDER="${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
2423
2424 # Delete the KSU folder
2425 rm -rf "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
2426}
2427
2428
2429# High-level function to delete a KSU from a profile
2430function delete_ksu_from_profile() {
2431 local KSU_NAME="$1"
2432 local PROFILE_NAME="$2"
2433 local PROFILE_TYPE="$3"
2434 local PROJECT_NAME="${4:-"osm_admin"}"
2435 ## `FLEET_REPO_DIR` is the result of:
2436 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
2437 local FLEET_REPO_DIR="$5"
2438
2439 # Calculate profile folder
2440 local TARGET_PROFILE_PATH=$(
2441 path_to_profile \
2442 "${PROFILE_NAME}" \
2443 "${PROFILE_TYPE}" \
2444 "${PROJECT_NAME}"
2445 )
2446 TARGET_PROFILE_FOLDER="${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}"
2447
2448 # Delete the KSU folder
2449 rm -rf "${TARGET_PROFILE_FOLDER}/${KSU_NAME}"
2450}
2451
2452
2453# High-level function to clone a KSU from a profile to another
2454function clone_ksu() {
2455 local SOURCE_KSU_NAME="$1"
2456 local SOURCE_PROFILE_NAME="$2"
2457 local SOURCE_PROFILE_TYPE="$3"
2458 local SOURCE_PROJECT_NAME="${4:-"osm_admin"}"
2459 local DESTINATION_KSU_NAME="${5:-"${SOURCE_KSU_NAME}"}"
2460 local DESTINATION_PROFILE_NAME="${6:-"${SOURCE_PROFILE_NAME}"}"
2461 local DESTINATION_PROFILE_TYPE="${7:-"${SOURCE_PROFILE_TYPE}"}"
2462 local DESTINATION_PROJECT_NAME="${8:-"${SOURCE_PROJECT_NAME}"}"
2463 ## `FLEET_REPO_DIR` is the result of:
2464 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
2465 local FLEET_REPO_DIR="$9"
2466
2467
2468 # If source and destination are identical, aborts
2469 if [[
2470 ("${SOURCE_KSU_NAME}" == "${DESTINATION_KSU_NAME}") && \
2471 ("${SOURCE_PROFILE_NAME}" == "${DESTINATION_PROFILE_NAME}") && \
2472 ("${SOURCE_PROFILE_TYPE}" == "${DESTINATION_PROFILE_TYPE}") && \
2473 ("${SOURCE_PROJECT_NAME}" == "${DESTINATION_PROJECT_NAME}") \
2474 ]];
2475 then
2476 return 1
2477 fi
2478
2479 # Calculate profile folders
2480 local SOURCE_PROFILE_PATH=$(
2481 path_to_profile \
2482 "${SOURCE_PROFILE_NAME}" \
2483 "${SOURCE_PROFILE_TYPE}" \
2484 "${SOURCE_PROJECT_NAME}"
2485 )
2486 local SOURCE_PROFILE_FOLDER="${FLEET_REPO_DIR}/${SOURCE_PROFILE_PATH}"
2487 local DESTINATION_PROFILE_PATH=$(
2488 path_to_profile \
2489 "${DESTINATION_PROFILE_NAME}" \
2490 "${DESTINATION_PROFILE_TYPE}" \
2491 "${DESTINATION_PROJECT_NAME}"
2492 )
2493 local DESTINATION_PROFILE_FOLDER="${FLEET_REPO_DIR}/${DESTINATION_PROFILE_PATH}"
2494
2495 # Clone KSU folder
2496 cp -ar \
2497 "${SOURCE_PROFILE_FOLDER}/${SOURCE_KSU_NAME}" \
2498 "${DESTINATION_PROFILE_FOLDER}/${DESTINATION_KSU_NAME}"
2499}
2500
2501
2502# Create a `ProviderConfig` for a CrossPlane provider
2503function create_crossplane_providerconfig() {
2504 local PROVIDERCONFIG_NAME="$1"
2505 # As of today, one among `azure`, `aws` or `gcp`:
2506 local PROVIDER_TYPE="$2"
2507 local CRED_SECRET_NAME="$3"
2508 local CRED_SECRET_KEY="${4:-"creds"}"
2509 local CRED_SECRET_NS="${5:-"crossplane-system"}"
2510 # If empty, it assumes the secret already exists
2511 local CRED_SECRET_CONTENT="${6:-"${CRED_SECRET_CONTENT:-""}"}"
2512 local AGE_PUBLIC_KEY_MGMT="$7"
2513 ## `FLEET_REPO_DIR` is the result of:
2514 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
2515 local FLEET_REPO_DIR="${8:-"${FLEET_REPO_DIR}"}"
2516 ## `SW_CATALOGS_REPO_DIR` is the result of:
2517 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
2518 local SW_CATALOGS_REPO_DIR="${9:-"${SW_CATALOGS_REPO_DIR}"}"
2519 # Only when applicable
2520 local TARGET_GCP_PROJECT="${10:-""}"
2521 # Do not touch unless strictly needed
2522 local BASE_TEMPLATES_PATH="${11:-"infra-configs/crossplane/providers"}"
2523 local OSM_PROJECT_NAME="${12:-"osm_admin"}"
2524 local MGMT_CLUSTER_NAME="${13:-"_management"}"
2525
garciadeblasb4769452025-08-06 18:08:18 +02002526
garciadeblas70461c52024-07-03 09:17:56 +02002527 # Is the provider type supported?
2528 local VALID_PROVIDERS=("aws" "azure" "gcp")
2529 PROVIDER_TYPE="${PROVIDER_TYPE,,}"
2530 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${PROVIDER_TYPE}")) ]] && return 1
2531
2532 # Determines the source dir for the templates and the target folder in Fleet
2533 local TEMPLATES_DIR="${SW_CATALOGS_REPO_DIR}/${BASE_TEMPLATES_PATH}/${PROVIDER_TYPE}/templates"
2534 local TARGET_FOLDER="${FLEET_REPO_DIR}/${OSM_PROJECT_NAME}/infra-config-profiles/${MGMT_CLUSTER_NAME}/crossplane-providerconfigs/${PROVIDER_TYPE}"
2535
2536 # Determine which optional steps may be needed
2537 local NEEDS_NEW_SECRET=$([[ -n "${CRED_SECRET_CONTENT}" ]]; echo $?)
2538 local NEEDS_PROJECT_NAME=$([[ "${PROVIDER_TYPE}" == "gcp" ]]; echo $?)
2539
2540 # Renders the `ProviderConfig` manifest and the encrypted secret (if applicable)
2541 echo "" | \
2542 folder2list_generator \
2543 "${TEMPLATES_DIR}" | \
2544 patch_replace \
2545 ".metadata.name" \
2546 "${PROVIDERCONFIG_NAME}" \
2547 "| select(.kind == \"ProviderConfig\")" | \
2548 patch_replace \
2549 ".spec.credentials.secretRef.name" \
2550 "${CRED_SECRET_NAME}" \
2551 "| select(.kind == \"ProviderConfig\")" | \
2552 patch_replace \
2553 ".spec.credentials.secretRef.key" \
2554 "${CRED_SECRET_KEY}" \
2555 "| select(.kind == \"ProviderConfig\")" | \
2556 patch_replace \
2557 ".spec.credentials.secretRef.namespace" \
2558 "${CRED_SECRET_NS}" \
2559 "| select(.kind == \"ProviderConfig\")" | \
2560 transform_if \
2561 "${NEEDS_PROJECT_NAME}" \
2562 patch_replace \
2563 ".spec.projectID" \
2564 "${TARGET_GCP_PROJECT}" \
2565 "| select(.kind == \"ProviderConfig\")" | \
2566 transform_if \
2567 "${NEEDS_NEW_SECRET}" \
2568 make_generator \
2569 "credentials-secret.yaml" \
2570 kubectl_encrypt \
2571 "${AGE_PUBLIC_KEY_MGMT}" \
2572 create \
2573 secret \
2574 generic \
2575 "${CRED_SECRET_NAME}" \
2576 --namespace="${CRED_SECRET_NS}" \
2577 --from-file="${CRED_SECRET_KEY}"=<(echo "${CRED_SECRET_CONTENT}") \
2578 -o=yaml \
2579 --dry-run=client | \
2580 prepend_folder_path \
2581 "${PROVIDERCONFIG_NAME}/" | \
2582 list2folder_cp_over \
2583 "${TARGET_FOLDER}"
2584}
2585
2586
2587# Delete a `ProviderConfig` for a CrossPlane provider
2588function delete_crossplane_providerconfig() {
2589 local PROVIDERCONFIG_NAME="$1"
2590 # As of today, one among `azure`, `aws` or `gcp`:
2591 local PROVIDER_TYPE="$2"
2592 ## `FLEET_REPO_DIR` is the result of:
2593 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
2594 local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}"
2595 # Do not touch unless strictly needed
2596 local OSM_PROJECT_NAME="${4:-"osm_admin"}"
2597 local MGMT_CLUSTER_NAME="${5:-"_management"}"
2598
garciadeblasb4769452025-08-06 18:08:18 +02002599
garciadeblas70461c52024-07-03 09:17:56 +02002600 # Is the provider type supported?
2601 local VALID_PROVIDERS=("aws" "azure" "gcp")
2602 PROVIDER_TYPE="${PROVIDER_TYPE,,}"
2603 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${PROVIDER_TYPE}")) ]] && return 1
2604
2605 # Determines the target folder in Fleet
2606 local PROVIDERCONFIG_FOLDER="${FLEET_REPO_DIR}/${OSM_PROJECT_NAME}/infra-config-profiles/${MGMT_CLUSTER_NAME}/crossplane-providerconfigs/${PROVIDER_TYPE}/${PROVIDERCONFIG_NAME}"
2607
2608 # Delete the folder
2609 rm -rf "${PROVIDERCONFIG_FOLDER}"
2610}
2611
2612
2613# Update a `ProviderConfig` for a CrossPlane provider
2614function update_crossplane_providerconfig() {
2615 local PROVIDERCONFIG_NAME="$1"
2616 # As of today, one among `azure`, `aws` or `gcp`:
2617 local PROVIDER_TYPE="$2"
2618 local CRED_SECRET_NAME="$3"
2619 local CRED_SECRET_KEY="${4:-"creds"}"
2620 local CRED_SECRET_NS="${5:-"crossplane-system"}"
2621 # If empty, it assumes the secret already exists
2622 local CRED_SECRET_CONTENT="${6:-"${CRED_SECRET_CONTENT:-""}"}"
2623 local AGE_PUBLIC_KEY_MGMT="$7"
2624 ## `FLEET_REPO_DIR` is the result of:
2625 ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}"
2626 local FLEET_REPO_DIR="${8:-"${FLEET_REPO_DIR}"}"
2627 ## `SW_CATALOGS_REPO_DIR` is the result of:
2628 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
2629 local SW_CATALOGS_REPO_DIR="${9:-"${SW_CATALOGS_REPO_DIR}"}"
2630 # Only when applicable
2631 local TARGET_GCP_PROJECT="${10:-""}"
2632 # Do not touch unless strictly needed
2633 local BASE_TEMPLATES_PATH="${11:-"infra-configs/crossplane/providers"}"
2634 local OSM_PROJECT_NAME="${12:-"osm_admin"}"
2635 local MGMT_CLUSTER_NAME="${13:-"_management"}"
2636
garciadeblasb4769452025-08-06 18:08:18 +02002637
garciadeblas70461c52024-07-03 09:17:56 +02002638 # Is the provider type supported?
2639 local VALID_PROVIDERS=("aws" "azure" "gcp")
2640 PROVIDER_TYPE="${PROVIDER_TYPE,,}"
2641 [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${PROVIDER_TYPE}")) ]] && return 1
2642
2643 # First, delete; then, re-create
2644 delete_crossplane_providerconfig \
2645 "${PROVIDERCONFIG_NAME}" \
2646 "${PROVIDER_TYPE}" \
2647 "${FLEET_REPO_DIR}" \
2648 "${OSM_PROJECT_NAME}" \
2649 "${MGMT_CLUSTER_NAME}"
2650
2651 create_crossplane_providerconfig \
2652 "${PROVIDERCONFIG_NAME}" \
2653 "${PROVIDER_TYPE}" \
2654 "${CRED_SECRET_NAME}" \
2655 "${CRED_SECRET_KEY}" \
2656 "${CRED_SECRET_NS}" \
2657 "${CRED_SECRET_CONTENT}" \
2658 "${AGE_PUBLIC_KEY_MGMT}" \
2659 "${FLEET_REPO_DIR}" \
2660 "${SW_CATALOGS_REPO_DIR}" \
2661 "${TARGET_GCP_PROJECT}" \
2662 "${BASE_TEMPLATES_PATH}" \
2663 "${OSM_PROJECT_NAME}" \
2664 "${MGMT_CLUSTER_NAME}"
2665}
2666
2667
garciadeblas8a28f6d2025-06-11 11:11:56 +02002668# Create a CloudConfig for CAPI provider
2669function create_capi_openstack_cloudconf() {
2670 local OPENSTACK_CLOUD_NAME="${1}"
2671 local PUBLIC_KEY="${2:-"${PUBLIC_KEY_MGMT}"}"
2672 local CONFIG_DIR="${3:-"${MGMT_ADDON_CONFIG_DIR}"}"
2673
2674 local NAMESPACE="managed-resources"
2675
2676 local CLOUDS_YAML="${OPENSTACK_CLOUDS_YAML}"
2677 local CACERT="${OPENSTACK_CACERT}"
2678
2679 local CLOUD_CREDENTIALS_SECRET_NAME="${OPENSTACK_CLOUD_NAME}-capo-config"
2680 local CLOUD_CREDENTIALS_CLOUDS_KEY="clouds.yaml"
2681 local CLOUD_CREDENTIALS_CACERT_KEY="cacert"
2682 local CLOUD_CREDENTIALS_FILENAME="credentials-secret.yaml"
garciadeblasb4769452025-08-06 18:08:18 +02002683
garciadeblas8a28f6d2025-06-11 11:11:56 +02002684 local CLOUD_CREDENTIALS_TOML_SECRET_NAME="${OPENSTACK_CLOUD_NAME}-capo-config-toml"
2685 local CLOUD_CREDENTIALS_TOML_FILENAME="credentials-toml-secret.yaml"
2686
2687 local TARGET_FOLDER="${CONFIG_DIR}/capi-providerconfigs/capo/${OPENSTACK_CLOUD_NAME}-config"
2688 mkdir -p "${TARGET_FOLDER}"
2689
2690 echo "" | \
2691 make_generator \
2692 "${CLOUD_CREDENTIALS_FILENAME}" \
2693 kubectl_encrypt \
2694 "${PUBLIC_KEY}" \
2695 create \
2696 secret \
2697 generic \
2698 "${CLOUD_CREDENTIALS_SECRET_NAME}" \
2699 --namespace="${NAMESPACE}" \
2700 --from-file="${CLOUD_CREDENTIALS_CLOUDS_KEY}"=<(echo "${CLOUDS_YAML}") \
2701 --from-file="${CLOUD_CREDENTIALS_CACERT_KEY}"=<(echo "${CACERT}") \
2702 -o=yaml \
2703 --dry-run=client | \
2704 make_generator \
2705 "${CLOUD_CREDENTIALS_TOML_FILENAME}" \
2706 kubectl_encrypt \
2707 "${PUBLIC_KEY}" \
2708 create \
2709 secret \
2710 generic \
2711 "${CLOUD_CREDENTIALS_TOML_SECRET_NAME}" \
2712 --namespace="${NAMESPACE}" \
2713 --from-file="os_auth_url"=<(echo "${OS_AUTH_URL}") \
2714 --from-file="os_region_name"=<(echo "${OS_REGION_NAME}") \
2715 --from-file="os_username"=<(echo "${OS_USERNAME}") \
2716 --from-file="os_password"=<(echo "${OS_PASSWORD}") \
2717 --from-file="os_project_id"=<(echo "${OS_PROJECT_ID}") \
2718 --from-file="os_project_domain_id"=<(echo "${OS_PROJECT_DOMAIN_ID}") \
2719 -o=yaml \
2720 --dry-run=client | \
2721 list2folder_cp_over \
2722 "${TARGET_FOLDER}"
2723}
2724
2725# Update a CloudConfig for CAPI provider
2726function update_capi_openstack_cloudconf() {
2727 local CLOUD_CONFIG_NAME="${1}"
2728 local PUBLIC_KEY="${2:-"${PUBLIC_KEY_MGMT}"}"
2729 local CONFIG_DIR="${3:-"${MGMT_ADDON_CONFIG_DIR}"}"
2730
2731 delete_capi_openstack_cloudconf \
2732 "${CLOUD_CONFIG_NAME}" \
2733 "${CONFIG_DIR}"
garciadeblasb4769452025-08-06 18:08:18 +02002734
garciadeblas8a28f6d2025-06-11 11:11:56 +02002735 create_capi_openstack_cloudconf \
2736 "${CLOUD_CONFIG_NAME}" \
2737 "${PUBLIC_KEY}" \
2738 "${CONFIG_DIR}"
2739}
2740
2741
2742# Delete a CloudConfig for CAPI provider
2743function delete_capi_openstack_cloudconf() {
2744 local OPENSTACK_CLOUD_NAME="$1"
2745 local CONFIG_DIR="${2:-"${MGMT_ADDON_CONFIG_DIR}"}"
2746
2747 local TARGET_FOLDER="${CONFIG_DIR}/capi-providerconfigs/capo/${OPENSTACK_CLOUD_NAME}-config"
garciadeblasb4769452025-08-06 18:08:18 +02002748
garciadeblas8a28f6d2025-06-11 11:11:56 +02002749 # Delete the encrypted secrets files.
2750 rm -rf "${TARGET_FOLDER}"
2751}
2752
garciadeblas70461c52024-07-03 09:17:56 +02002753# Helper function to return the relative path of a location in SW Catalogs for an OKA
2754function path_to_catalog() {
2755 local OKA_TYPE="$1"
2756 local PROJECT_NAME="${2:-"osm_admin"}"
2757
2758 # Corrects `osm_admin` project, since it uses the root folder
2759 PROJECT_NAME="${PROJECT_NAME}"
2760 [[ "${PROJECT_NAME}" == "osm_admin" ]] && PROJECT_NAME="."
2761
2762 # Echoes the relate path from the SW-Catalogs root
2763 case "${OKA_TYPE,,}" in
2764
2765 "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers")
2766 echo -n "${PROJECT_NAME}/infra-controllers"
2767 return 0
2768 ;;
2769
2770 "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs")
2771 echo -n "${PROJECT_NAME}/infra-configs"
2772 return 0
2773 ;;
2774
2775 "managed" | "resources" | "managed-resources" | "managed_resources" | "cloud-resources" | "cloud_resources")
2776 echo -n "${PROJECT_NAME}/cloud-resources"
2777 return 0
2778 ;;
2779
2780 "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs")
2781 echo -n "${PROJECT_NAME}/apps"
2782 return 0
2783 ;;
2784
2785 *)
2786 echo -n "------------ ERROR ------------"
2787 return 1
2788 ;;
2789 esac
2790}
2791
2792
2793# Create OKA of a specific kind
2794function create_oka() {
2795 local OKA_NAME="$1"
2796 local OKA_TYPE="$2"
2797 local PROJECT_NAME="${3:-"."}"
2798 ## `SW_CATALOGS_REPO_DIR` is the result of:
2799 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
2800 local SW_CATALOGS_REPO_DIR="$4"
2801 local OKA_LOCATION="${5:-"."}"
2802 local TARBALL_FILE="${6:-"true"}"
2803
2804
2805 # Finds the corresponding catalog path from the SW-Catalogs root
2806 # and create the destination
2807 local CATALOG_PATH=$(\
2808 path_to_catalog \
2809 "${OKA_TYPE}" \
2810 "${PROJECT_NAME}"
2811 )
2812 local DESTINATION="${SW_CATALOGS_REPO_DIR}/${CATALOG_PATH}/${OKA_NAME}"
2813 mkdir -p "${DESTINATION}"
2814
2815 # When the OKA comes as a `tar.gz`
2816 if [[ "${TARBALL_FILE,,}" == "true" ]];
2817 then
2818 tar xvfz "${OKA_LOCATION}/${OKA_NAME}.tar.gz" -C "${DESTINATION}"
2819 else
2820 # Otherwise it must be a folder structure
2821 cp -var "${OKA_LOCATION}/${OKA_NAME}/*" "${DESTINATION}/"
2822 fi
2823}
2824
2825
2826# Delete OKA of a specific kind
2827function delete_oka() {
2828 local OKA_NAME="$1"
2829 local OKA_TYPE="$2"
2830 local PROJECT_NAME="${3:-"."}"
2831 ## `SW_CATALOGS_REPO_DIR` is the result of:
2832 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
2833 local SW_CATALOGS_REPO_DIR="$4"
2834
2835
2836 # Finds the corresponding catalog path from the SW-Catalogs root
2837 # and determine the destination
2838 local CATALOG_PATH=$(\
2839 path_to_catalog \
2840 "${OKA_TYPE}" \
2841 "${PROJECT_NAME}"
2842 )
2843 local DESTINATION="${SW_CATALOGS_REPO_DIR}/${CATALOG_PATH}/${OKA_NAME}"
2844
2845 # Remove the folder
2846 rm -rf "${DESTINATION}"
2847}
2848
2849
2850# Update OKA of a specific kind
2851function update_oka() {
2852 local OKA_NAME="$1"
2853 local OKA_TYPE="$2"
2854 local PROJECT_NAME="${3:-"."}"
2855 ## `SW_CATALOGS_REPO_DIR` is the result of:
2856 ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}"
2857 local SW_CATALOGS_REPO_DIR="$4"
2858 local OKA_LOCATION="${5:-"."}"
2859 local TARBALL_FILE="${6:-"true"}"
2860
2861
2862 # Finds the corresponding catalog path from the SW-Catalogs root
2863 # and determine the destination
2864 local CATALOG_PATH=$(\
2865 path_to_catalog \
2866 "${OKA_TYPE}" \
2867 "${PROJECT_NAME}"
2868 )
2869 local DESTINATION="${SW_CATALOGS_REPO_DIR}/${CATALOG_PATH}/${OKA_NAME}"
2870
2871 # Remove and re-create
2872 rm -rf "${DESTINATION}"
2873 create_oka \
2874 "${OKA_NAME}" \
2875 "${OKA_TYPE}" \
2876 "${PROJECT_NAME}" \
2877 "${SW_CATALOGS_REPO_DIR}" \
2878 "${OKA_LOCATION}" \
2879 "${TARBALL_FILE}"
2880}