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