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