| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 1 | #!/bin/bash |
| garciadeblas | 36da51d | 2024-11-13 14:58:53 +0100 | [diff] [blame] | 2 | ####################################################################################### |
| 3 | # Copyright ETSI Contributors and Others. |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 4 | # |
| garciadeblas | 36da51d | 2024-11-13 14:58:53 +0100 | [diff] [blame] | 5 | # 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 |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 8 | # |
| garciadeblas | 36da51d | 2024-11-13 14:58:53 +0100 | [diff] [blame] | 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 10 | # |
| garciadeblas | 36da51d | 2024-11-13 14:58:53 +0100 | [diff] [blame] | 11 | # 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 | |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 20 | function 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 |
| 40 | function 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 |
| 75 | function 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 | |
| yshah | 2061907 | 2025-06-13 14:56:25 +0000 | [diff] [blame] | 114 | # Create remote NodeGroup in AWS |
| 115 | function 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 |
| 159 | patch: | |
| 160 | apiVersion: eks.aws.upbound.io/v1beta1 |
| 161 | kind: NodeGroup |
| 162 | metadata: |
| 163 | name: \${nodegroup_name} |
| 164 | spec: |
| 165 | forProvider: |
| 166 | nodeRoleArn: \${role} |
| 167 | EOF |
| 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 | |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 230 | |
| yshah | 2061907 | 2025-06-13 14:56:25 +0000 | [diff] [blame] | 231 | function 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 | |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 254 | # Is the provider type supported? |
| yshah | 2061907 | 2025-06-13 14:56:25 +0000 | [diff] [blame] | 255 | local VALID_PROVIDERS=("eks" "aks" "gke") |
| 256 | CLUSTER_TYPE="${CLUSTER_TYPE,,}" |
| 257 | [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${CLUSTER_TYPE}")) ]] && return 1 |
| 258 | |
| 259 | # Determines the folder in Fleet |
| 260 | local TARGET_FOLDER="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}/${CLUSTER_NAME}/${NODEGROUP_KUSTOMIZATION_NAME}" |
| 261 | |
| 262 | # Pipeline of transformations to create the cluster resource |
| 263 | export NODEGROUP_KUSTOMIZATION_NAME |
| 264 | folder2list \ |
| 265 | "${TARGET_FOLDER}" | \ |
| 266 | patch_replace \ |
| 267 | ".spec.postBuild.substitute.node_count" \ |
| 268 | "${NODE_COUNT}" \ |
| 269 | "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${NODEGROUP_KUSTOMIZATION_NAME}\")" | \ |
| 270 | list2folder_cp_over \ |
| 271 | "${TARGET_FOLDER}" |
| 272 | } |
| 273 | |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 274 | |
| yshah | 2061907 | 2025-06-13 14:56:25 +0000 | [diff] [blame] | 275 | # Delete nodegroup |
| 276 | function 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}"}" |
| yshah | 2061907 | 2025-06-13 14:56:25 +0000 | [diff] [blame] | 282 | local NODEGROUP_DIR="${MGMT_RESOURCES_DIR}/${CLUSTER_NAME}/${NODEGROUP_KUSTOMIZATION_NAME}" |
| yshah | 2061907 | 2025-06-13 14:56:25 +0000 | [diff] [blame] | 283 | # Delete node Kustomizations |
| 284 | rm -rf "${NODEGROUP_DIR}" |
| 285 | } |
| 286 | |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 287 | |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 288 | # TODO: Deprecated |
| 289 | # Create AKS cluster (without bootstrap) |
| 290 | function 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 |
| 351 | function 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 |
| 376 | function 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 |
| 412 | function 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 |
| 440 | function 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 |
| 461 | function 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 |
| 502 | function 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}"}" |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 508 | # Path for the source templates |
| 509 | local TEMPLATES="${6:-"${SW_CATALOGS_REPO_DIR}/cloud-resources/flux-remote-bootstrap/cluster-base/templates"}" |
| 510 | |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 511 | # Optional inputs: |
| 512 | # Paths for each profile in the Git repo |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 513 | 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}"}" |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 517 | |
| 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 |
| 538 | function 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 |
| 554 | function 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}")}" |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 558 | local CLUSTER_AGE_SECRET_NAMESPACE="${4:-"managed-resources"}" |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 559 | |
| 560 | join_lists \ |
| 561 | <(cat) \ |
| 562 | <( |
| 563 | echo "${PRIVATE_KEY_NEW_CLUSTER}" | \ |
| 564 | grep -v '^#' | \ |
| 565 | kubectl create secret generic "${CLUSTER_AGE_SECRET_NAME}" \ |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 566 | --namespace="${CLUSTER_AGE_SECRET_NAMESPACE}" \ |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 567 | --from-file=agekey=/dev/stdin \ |
| 568 | -o yaml --dry-run=client | \ |
| 569 | encrypt_secret_from_stdin \ |
| 570 | "${PUBLIC_KEY_MGMT}" | |
| 571 | manifest2list | \ |
| 572 | set_filename_to_items "${CLUSTER_AGE_SECRET_NAME}.yaml" |
| 573 | ) |
| 574 | } |
| 575 | |
| 576 | |
| 577 | # Generate bootstrap manifests for new cluster from the management cluster |
| 578 | function generator_bootstrap_new_cluster() { |
| 579 | local CLUSTER_NAME="$1" |
| garciadeblas | 9cd70f5 | 2025-07-31 18:06:51 +0200 | [diff] [blame] | 580 | local CLUSTER_KUSTOMIZATION_NAME="${2:-$(safe_name ${CLUSTER_NAME})}" |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 581 | local CLUSTER_AGE_SECRET_NAME="${3:-$(safe_name "sops-age-${CLUSTER_KUSTOMIZATION_NAME}")}" |
| 582 | local SW_CATALOGS_REPO_DIR="${4:-"${SW_CATALOGS_REPO_DIR}"}" |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 583 | local BOOTSTRAP_KUSTOMIZATION_NAMESPACE="${5:-"managed-resources"}" |
| 584 | local CLUSTER_KUSTOMIZATION_NAMESPACE="${6:-"managed-resources"}" |
| 585 | local BOOTSTRAP_SECRET_NAMESPACE="${7:-"managed-resources"}" |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 586 | |
| 587 | # Paths and names for the templates |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 588 | 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}"} |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 595 | |
| 596 | # Generate manifests |
| 597 | export CLUSTER_KUSTOMIZATION_NAME |
| 598 | export CLUSTER_NAME |
| 599 | export CLUSTER_AGE_SECRET_NAME |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 600 | 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 |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 605 | |
| 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 \ |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 615 | '${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}' |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 616 | ) |
| 617 | } |
| 618 | |
| 619 | |
| 620 | # Auxiliary function to create kustomization manifests |
| 621 | function 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 |
| 657 | function 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 |
| 673 | function 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}}" |
| garciadeblas | 36da51d | 2024-11-13 14:58:53 +0100 | [diff] [blame] | 684 | local IMPORTED_CLUSTER="${11:-"false"}" |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 685 | 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}"}" |
| garciadeblas | 36da51d | 2024-11-13 14:58:53 +0100 | [diff] [blame] | 692 | |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 693 | # Calculates the folder where managed resources are defined |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 694 | local MGMT_RESOURCES_DIR="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}" |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 695 | |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 696 | # 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}" \ |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 713 | "${SW_CATALOGS_REPO_DIR}" \ |
| 714 | "${TEMPLATES_DIR}" | \ |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 715 | 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}" \ |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 737 | "${SW_CATALOGS_REPO_DIR}" \ |
| 738 | "${BOOTSTRAP_KUSTOMIZATION_NAMESPACE}" \ |
| 739 | "${CLUSTER_KUSTOMIZATION_NAMESPACE}" \ |
| 740 | "${BOOTSTRAP_SECRET_NAMESPACE}" | \ |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 741 | generator_k8s_age_secret_new_cluster \ |
| 742 | "${PRIVATE_KEY_NEW_CLUSTER}" \ |
| 743 | "${PUBLIC_KEY_MGMT}" \ |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 744 | "${CLUSTER_AGE_SECRET_NAME}" \ |
| 745 | "${BOOTSTRAP_SECRET_NAMESPACE}" | \ |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 746 | prepend_folder_path "${CLUSTER_KUSTOMIZATION_NAME}/" | \ |
| 747 | list2folder_cp_over \ |
| 748 | "${MGMT_RESOURCES_DIR}" |
| garciadeblas | 36da51d | 2024-11-13 14:58:53 +0100 | [diff] [blame] | 749 | |
| 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 |
| 768 | function 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 |
| garciadeblas | c4afd54 | 2025-06-18 17:37:59 +0200 | [diff] [blame] | 776 | # local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}" |
| garciadeblas | 36da51d | 2024-11-13 14:58:53 +0100 | [diff] [blame] | 777 | ## 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 | |
| garciadeblas | c4afd54 | 2025-06-18 17:37:59 +0200 | [diff] [blame] | 780 | # Delete Flux resources synchronized directly from remote cluster |
| 781 | # rm -rf "${CLUSTER_FOLDER}/flux-system" |
| garciadeblas | 36da51d | 2024-11-13 14:58:53 +0100 | [diff] [blame] | 782 | |
| 783 | # Delete Flux resources bootstraped remotely |
| 784 | rm -rf "${MGMT_RESOURCES_CLUSTER_DIR}/cluster-bootstrap-${CLUSTER_KUSTOMIZATION_NAME}.yaml" |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 785 | } |
| 786 | |
| 787 | |
| 788 | # Create remote CrossPlane cluster (generic for any cloud) |
| 789 | function 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"}" |
| rshri | 4240a7d | 2025-06-13 11:30:35 +0000 | [diff] [blame] | 820 | # 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"}" |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 827 | |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 828 | # 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 | |
| rshri | 4240a7d | 2025-06-13 11:30:35 +0000 | [diff] [blame] | 833 | # 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: |
| 877 | EOF |
| 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} |
| 888 | EOF |
| 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} |
| 897 | EOF |
| 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 | |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 913 | # 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 | |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 917 | # 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 \ |
| rshri | 4240a7d | 2025-06-13 11:30:35 +0000 | [diff] [blame] | 944 | ".spec.postBuild.substitute.cluster_iam_role" \ |
| 945 | "${CLUSTER_IAM_ROLE}" \ |
| 946 | "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \ |
| 947 | patch_replace \ |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 948 | ".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}\")" | \ |
| rshri | 4240a7d | 2025-06-13 11:30:35 +0000 | [diff] [blame] | 963 | 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}" | \ |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 977 | rename_file_in_items \ |
| 978 | "${TEMPLATE_MANIFEST_FILENAME}" \ |
| 979 | "${MANIFEST_FILENAME}" | \ |
| rshri | 4240a7d | 2025-06-13 11:30:35 +0000 | [diff] [blame] | 980 | prepend_folder_path "${CLUSTER_KUSTOMIZATION_NAME}/clusterbase/" | \ |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 981 | 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) |
| 1003 | function 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}"}" |
| garciadeblas | 61e2506 | 2025-08-08 12:09:30 +0200 | [diff] [blame] | 1008 | local MGMT_CLUSTER_NAME="${5:-"${MGMT_CLUSTER_NAME}"}" |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 1009 | |
| 1010 | # Optional inputs: Paths for each profile in the Git repo |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 1011 | 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}"}" |
| garciadeblas | 61e2506 | 2025-08-08 12:09:30 +0200 | [diff] [blame] | 1014 | 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}"}" |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 1016 | |
| garciadeblas | c4afd54 | 2025-06-18 17:37:59 +0200 | [diff] [blame] | 1017 | # Optional input: Do I need a purge operation first? |
| garciadeblas | 61e2506 | 2025-08-08 12:09:30 +0200 | [diff] [blame] | 1018 | local PURGE="${11:-"false"}" |
| garciadeblas | c4afd54 | 2025-06-18 17:37:59 +0200 | [diff] [blame] | 1019 | |
| 1020 | # Perform the purge if needed |
| 1021 | if [[ "${PURGE,,}" == "true" ]]; then |
| 1022 | echo "Purging the remote Flux instalation..." |
| garciadeblas | c4afd54 | 2025-06-18 17:37:59 +0200 | [diff] [blame] | 1023 | fi |
| 1024 | |
| 1025 | echo "Deleting cluster profiles and (when applicable) its cloud resources..." |
| 1026 | |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 1027 | # 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 | |
| garciadeblas | 61e2506 | 2025-08-08 12:09:30 +0200 | [diff] [blame] | 1033 | 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 | |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 1038 | # Delete base cluster Kustomizations |
| 1039 | rm -rf "${CLUSTER_DIR}" |
| 1040 | |
| garciadeblas | c4afd54 | 2025-06-18 17:37:59 +0200 | [diff] [blame] | 1041 | # Delete cluster resources if managed by OSM (otherwise, this will be ignored) |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 1042 | rm -rf "${MGMT_RESOURCES_DIR}/${CLUSTER_KUSTOMIZATION_NAME}" |
| 1043 | } |
| 1044 | |
| 1045 | |
| 1046 | # Update remote CrossPlane cluster (generic for any cloud) |
| 1047 | function 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 | |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 1081 | |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 1082 | # 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 | |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 1123 | # Create remote CAPI cluster for Openstack |
| 1124 | function 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"}" |
| garciadeblas | 55c9895 | 2025-08-07 13:43:48 +0200 | [diff] [blame] | 1154 | local CAPO_RESOURCES_NAMESPACE="${27:-"managed-resources"}" |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 1155 | |
| 1156 | # Varibles with valus from convention. |
| 1157 | local CLUSTER_TYPE="openstack" |
| 1158 | local TEMPLATE_MANIFEST_FILENAME="capi-cluster.yaml" |
| 1159 | local MANIFEST_FILENAME="openstack-${CLUSTER_NAME}.yaml" |
| 1160 | local CLOUD_CREDENTIALS="${OPENSTACK_CLOUD_NAME}-capo-config" |
| 1161 | |
| 1162 | # Determines the source dir for the templates and the target folder in Fleet |
| 1163 | local TEMPLATES_DIR="${SW_CATALOGS_REPO_DIR}/${BASE_TEMPLATES_PATH}/openstack-kubeadm/templates" |
| 1164 | local TARGET_FOLDER="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}" |
| 1165 | export CNI=${CNI,,} |
| 1166 | |
| 1167 | # Variables for kubeconfig secret reference |
| 1168 | export CLUSTER_KUBECONFIG_SECRET_NAME="${CLUSTER_KUSTOMIZATION_NAME}-kubeconfig" |
| 1169 | export CLUSTER_KUBECONFIG_SECRET_KEY="value" |
| 1170 | |
| 1171 | export CLUSTER_KUSTOMIZATION_NAME |
| 1172 | export OPENSTACK_CLOUD_NAME |
| 1173 | |
| 1174 | folder2list \ |
| 1175 | "${TEMPLATES_DIR}" | \ |
| 1176 | replace_env_vars \ |
| 1177 | '${CLUSTER_KUSTOMIZATION_NAME},${CNI},${CLUSTER_KUBECONFIG_SECRET_NAME},${CLUSTER_KUBECONFIG_SECRET_KEY},${OPENSTACK_CLOUD_NAME}' | \ |
| 1178 | patch_replace \ |
| 1179 | ".spec.postBuild.substitute.cluster_name" \ |
| 1180 | "${CLUSTER_NAME}" \ |
| 1181 | "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \ |
| 1182 | patch_replace \ |
| 1183 | ".spec.postBuild.substitute.cni" \ |
| 1184 | "${CNI}" \ |
| 1185 | "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \ |
| 1186 | patch_replace \ |
| 1187 | ".spec.postBuild.substitute.control_plane_machine_count" \ |
| 1188 | "${NODE_COUNT_CONTROLPLANE}" \ |
| 1189 | "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \ |
| 1190 | patch_replace \ |
| 1191 | ".spec.postBuild.substitute.kubernetes_version" \ |
| 1192 | "v${K8S_VERSION}" \ |
| 1193 | "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \ |
| 1194 | patch_replace \ |
| 1195 | ".spec.postBuild.substitute.namespace" \ |
| garciadeblas | 55c9895 | 2025-08-07 13:43:48 +0200 | [diff] [blame] | 1196 | "${CAPO_RESOURCES_NAMESPACE}" \ |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 1197 | "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \ |
| 1198 | patch_replace \ |
| 1199 | ".spec.postBuild.substitute.worker_machine_count" \ |
| 1200 | "${NODE_COUNT}" \ |
| 1201 | "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \ |
| 1202 | patch_replace \ |
| 1203 | ".spec.postBuild.substitute.openstack_cloud" \ |
| 1204 | "${OPENSTACK_CLOUD_NAME}" \ |
| 1205 | "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \ |
| 1206 | patch_replace \ |
| 1207 | ".spec.postBuild.substitute.openstack_cloud_conf" \ |
| 1208 | "${CLOUD_CREDENTIALS}" \ |
| 1209 | "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \ |
| 1210 | patch_replace \ |
| 1211 | ".spec.postBuild.substitute.openstack_control_plane_machine_flavor" \ |
| 1212 | "${VM_SIZE_CONTROL_PLANE}" \ |
| 1213 | "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \ |
| 1214 | patch_replace \ |
| 1215 | ".spec.postBuild.substitute.openstack_dns_nameservers" \ |
| 1216 | "${OPENSTACK_DNS_NAMESERVERS}" \ |
| 1217 | "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \ |
| 1218 | patch_replace \ |
| 1219 | ".spec.postBuild.substitute.openstack_external_network_id" \ |
| 1220 | "${OPENSTACK_EXTERNAL_NETWORK_ID}" \ |
| 1221 | "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \ |
| 1222 | patch_replace \ |
| 1223 | ".spec.postBuild.substitute.openstack_failure_domain" \ |
| 1224 | "${OPENSTACK_FAILURE_DOMAIN}" \ |
| 1225 | "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \ |
| 1226 | patch_replace \ |
| 1227 | ".spec.postBuild.substitute.openstack_worker_image_name" \ |
| 1228 | "${OPENSTACK_WORKER_IMAGE_NAME}" \ |
| 1229 | "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \ |
| 1230 | patch_replace \ |
| 1231 | ".spec.postBuild.substitute.openstack_control_plane_image_name" \ |
| 1232 | "${OPENSTACK_CONTROL_PLANE_IMAGE_NAME}" \ |
| 1233 | "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \ |
| 1234 | patch_replace \ |
| 1235 | ".spec.postBuild.substitute.openstack_node_machine_flavor" \ |
| 1236 | "${VM_SIZE}" \ |
| 1237 | "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \ |
| 1238 | patch_replace \ |
| 1239 | ".spec.postBuild.substitute.openstack_ssh_key_name" \ |
| 1240 | "${OPENSTACK_SSH_KEY_NAME}" \ |
| 1241 | "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${CLUSTER_KUSTOMIZATION_NAME}\")" | \ |
| 1242 | rename_file_in_items \ |
| 1243 | "${TEMPLATE_MANIFEST_FILENAME}" \ |
| 1244 | "${MANIFEST_FILENAME}" | \ |
| 1245 | prepend_folder_path "${CLUSTER_KUSTOMIZATION_NAME}/" | \ |
| 1246 | list2folder_cp_over \ |
| 1247 | "${TARGET_FOLDER}" |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 1248 | |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 1249 | # Bootstrap (unless asked to skip) |
| 1250 | if [[ "${SKIP_BOOTSTRAP,,}" == "true" ]]; then |
| 1251 | return 0 |
| 1252 | fi |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 1253 | |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 1254 | 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 |
| 1273 | function update_capi_openstack_cluster() { |
| 1274 | local CLUSTER_KUSTOMIZATION_NAME="${1}" |
| 1275 | local CLUSTER_NAME="${2}" |
| 1276 | local VM_SIZE="${3}" |
| garciadeblas | 55c9895 | 2025-08-07 13:43:48 +0200 | [diff] [blame] | 1277 | local VM_SIZE_CONTROL_PLANE="${4:-"${VM_SIZE}"}" |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 1278 | local NODE_COUNT="${5}" |
| garciadeblas | 55c9895 | 2025-08-07 13:43:48 +0200 | [diff] [blame] | 1279 | local NODE_COUNT_CONTROLPLANE="${6:-"1"}" |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 1280 | local K8S_VERSION="${7}" |
| 1281 | # OpenStack specific |
| 1282 | local OPENSTACK_CLOUD_NAME="${8}" |
| 1283 | local OPENSTACK_DNS_NAMESERVERS="${9}" |
| 1284 | local OPENSTACK_EXTERNAL_NETWORK_ID="${10}" |
| 1285 | local OPENSTACK_FAILURE_DOMAIN="${11}" |
| 1286 | local OPENSTACK_SSH_KEY_NAME="${12}" |
| 1287 | local CNI="${13:-"calico"}" |
| 1288 | local OPENSTACK_WORKER_IMAGE_NAME="${14:-"osm-capo-node-${K8S_VERSION}"}" |
| 1289 | local OPENSTACK_CONTROL_PLANE_IMAGE_NAME="${15:-"${OPENSTACK_WORKER_IMAGE_NAME}"}" |
| 1290 | # SOPS-AGE related |
| 1291 | local PUBLIC_KEY_MGMT="${16:-"${PUBLIC_KEY_MGMT}"}" |
| 1292 | local PUBLIC_KEY_NEW_CLUSTER="${17:-"${PUBLIC_KEY_NEW_CLUSTER}"}" |
| 1293 | local PRIVATE_KEY_NEW_CLUSTER="${18:-"${PRIVATE_KEY_NEW_CLUSTER}"}" |
| 1294 | # GitOps retaled |
| 1295 | local FLEET_REPO_DIR="${19:-"${FLEET_REPO_DIR}"}" |
| 1296 | local FLEET_REPO_URL="${20:-"${FLEET_REPO_URL}"}" |
| 1297 | local SW_CATALOGS_REPO_DIR="${21:-"${SW_CATALOGS_REPO_DIR}"}" |
| 1298 | local SW_CATALOGS_REPO_URL="${22:-"${SW_CATALOGS_REPO_URL}"}" |
| garciadeblas | 55c9895 | 2025-08-07 13:43:48 +0200 | [diff] [blame] | 1299 | 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"}" |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 1304 | |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 1305 | # 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" |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 1310 | |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 1311 | 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}" \ |
| garciadeblas | 55c9895 | 2025-08-07 13:43:48 +0200 | [diff] [blame] | 1338 | "${CAPO_RESOURCES_NAMESPACE}" |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 1339 | } |
| 1340 | |
| 1341 | # Create remote Openshift cluster via ACM |
| 1342 | function 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"}" |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 1375 | |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 1376 | 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 |
| 1514 | function 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}"}" |
| mesaj | d79959e | 2025-08-10 23:54:54 +0200 | [diff] [blame] | 1541 | local SKIP_BOOTSTRAP="${23:-"true"}" |
| 1542 | # Only change if absolutely needeed |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 1543 | local MGMT_PROJECT_NAME="${24:-"osm_admin"}" |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 1544 | |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 1545 | # Determine key folders in Fleet |
| 1546 | local MGMT_RESOURCES_DIR="${FLEET_REPO_DIR}/${MGMT_PROJECT_NAME}/managed-resources/${MGMT_CLUSTER_NAME}" |
| 1547 | |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 1548 | |
| 1549 | create_openshift_cluster \ |
| 1550 | "${CLUSTER_KUSTOMIZATION_NAME}" \ |
| 1551 | "${CLUSTER_NAME}" \ |
| 1552 | "${K8S_VERSION}" \ |
| 1553 | "${PUBLIC_KEY_ACM}" \ |
| 1554 | "${PUBLIC_KEY_NEW_CLUSTER}" \ |
| 1555 | "${PRIVATE_KEY_NEW_CLUSTER}" \ |
| 1556 | "${OPENSHIFT_RELEASE}" \ |
| 1557 | "${INFRA_PUBLIC_SSH_KEY}" \ |
| 1558 | "${CONTROL_PLANE_AVAILABILITY}" \ |
| 1559 | "${WORKER_COUNT}" \ |
| 1560 | "${WORKER_CORES}" \ |
| 1561 | "${WORKER_MEMORY}" \ |
| 1562 | "${WORKER_VOLUME_SIZE}" \ |
| 1563 | "${STORAGE_CLASS}" \ |
| 1564 | "${BASE_DOMAIN}" \ |
| 1565 | "${MGMT_CLUSTER_NAME}" \ |
| 1566 | "${HOSTED_CLUSTERS_PROJECT}" \ |
| 1567 | "${ETCD_VOLUME_SIZE}" \ |
| 1568 | "${FLEET_REPO_DIR}" \ |
| 1569 | "${FLEET_REPO_URL}" \ |
| 1570 | "${SW_CATALOGS_REPO_DIR}" \ |
| 1571 | "${SW_CATALOGS_REPO_URL}" \ |
| 1572 | "${SKIP_BOOTSTRAP}" \ |
| 1573 | "${MGMT_PROJECT_NAME}" |
| 1574 | } |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 1575 | |
| 1576 | # ----- Helper functions for adding/removing a profile from a cluster ----- |
| 1577 | |
| 1578 | # Helper function to find profiles of a given type already used in the cluster |
| 1579 | function profiles_of_type_in_cluster() { |
| 1580 | local CLUSTER_KUSTOMIZATION_NAME="$1" |
| 1581 | local RELEVANT_PROFILE_TYPE="$2" |
| 1582 | local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}" |
| 1583 | |
| 1584 | # Calculated fields |
| 1585 | local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}" |
| 1586 | |
| 1587 | # Processing (echoes the list) |
| 1588 | folder2list \ |
| 1589 | "${CLUSTER_FOLDER}" | \ |
| 1590 | get_value_from_resourcelist \ |
| 1591 | ".metadata.name" \ |
| 1592 | "| select(.kind == \"Kustomization\") |
| 1593 | | select(.metadata.labels.osm_profile_type == \"${RELEVANT_PROFILE_TYPE}\")" | \ |
| 1594 | multiline2commalist |
| 1595 | } |
| 1596 | |
| 1597 | |
| 1598 | # Function to list the profiles **this profile depends on** |
| 1599 | function profiles_this_one_depends_on() { |
| 1600 | local CLUSTER_KUSTOMIZATION_NAME="$1" |
| 1601 | local PROFILE_TYPE="$2" |
| 1602 | local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}" |
| 1603 | |
| 1604 | case "${PROFILE_TYPE,,}" in |
| 1605 | |
| 1606 | "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers") |
| 1607 | # Controllers do not depend on any other type of profiles |
| 1608 | echo "" |
| 1609 | return 0 |
| 1610 | ;; |
| 1611 | |
| 1612 | "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs") |
| 1613 | # Infra configs depend on controllers |
| 1614 | profiles_of_type_in_cluster \ |
| 1615 | "${CLUSTER_KUSTOMIZATION_NAME}" \ |
| 1616 | "infra-controllers" \ |
| 1617 | "${FLEET_REPO_DIR}" |
| 1618 | return 0 |
| 1619 | ;; |
| 1620 | |
| 1621 | "managed" | "resources" | "managed-resources" | "managed_resources") |
| 1622 | # Managed resources depend on infra configs |
| 1623 | profiles_of_type_in_cluster \ |
| 1624 | "${CLUSTER_KUSTOMIZATION_NAME}" \ |
| 1625 | "infra-configs" \ |
| 1626 | "${FLEET_REPO_DIR}" |
| 1627 | return 0 |
| 1628 | ;; |
| 1629 | |
| 1630 | "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs") |
| 1631 | # Apps (also) depend on infra configs |
| 1632 | profiles_of_type_in_cluster \ |
| 1633 | "${CLUSTER_KUSTOMIZATION_NAME}" \ |
| 1634 | "infra-configs" \ |
| 1635 | "${FLEET_REPO_DIR}" |
| 1636 | return 0 |
| 1637 | ;; |
| 1638 | |
| 1639 | *) |
| 1640 | echo -n "------------ ERROR ------------" |
| 1641 | return 1 |
| 1642 | ;; |
| 1643 | esac |
| 1644 | } |
| 1645 | |
| 1646 | |
| 1647 | # Function to list the profiles that **depend on this profile** |
| 1648 | function profiles_depend_on_this_one() { |
| 1649 | local CLUSTER_KUSTOMIZATION_NAME="$1" |
| 1650 | local PROFILE_TYPE="$2" |
| 1651 | local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}" |
| 1652 | |
| 1653 | case "${PROFILE_TYPE,,}" in |
| 1654 | |
| 1655 | "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers") |
| 1656 | # Infra configs depend on infra controllers |
| 1657 | profiles_of_type_in_cluster \ |
| 1658 | "${CLUSTER_KUSTOMIZATION_NAME}" \ |
| 1659 | "infra-configs" \ |
| 1660 | "${FLEET_REPO_DIR}" |
| 1661 | return 0 |
| 1662 | ;; |
| 1663 | |
| 1664 | "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs") |
| 1665 | # Both managed resources and apps depend on configs |
| 1666 | local PROFILES=( |
| 1667 | $( |
| 1668 | profiles_of_type_in_cluster \ |
| 1669 | "${CLUSTER_KUSTOMIZATION_NAME}" \ |
| 1670 | "managed-resources" \ |
| 1671 | "${FLEET_REPO_DIR}" |
| 1672 | ) \ |
| 1673 | $( |
| 1674 | profiles_of_type_in_cluster \ |
| 1675 | "${CLUSTER_KUSTOMIZATION_NAME}" \ |
| 1676 | "apps" \ |
| 1677 | "${FLEET_REPO_DIR}" |
| 1678 | ) |
| 1679 | ) |
| 1680 | printf '%s,' "${PROFILES[@]}" | sed 's/,$//g' |
| 1681 | return 0 |
| 1682 | ;; |
| 1683 | |
| 1684 | "managed" | "resources" | "managed-resources" | "managed_resources") |
| 1685 | # No other profiles depend on managed resources |
| 1686 | echo "" |
| 1687 | return 0 |
| 1688 | ;; |
| 1689 | |
| 1690 | "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs") |
| 1691 | # No other profiles depend on apps |
| 1692 | echo "" |
| 1693 | return 0 |
| 1694 | ;; |
| 1695 | |
| 1696 | *) |
| 1697 | echo -n "------------ ERROR ------------" |
| 1698 | return 1 |
| 1699 | ;; |
| 1700 | esac |
| 1701 | } |
| 1702 | |
| 1703 | |
| 1704 | # Helper function to add a dependency to a Kustomization only if it does not exist already |
| 1705 | function add_dependency_to_kustomization_safely() { |
| 1706 | local KUSTOMIZATION_NAME="$1" |
| 1707 | local KUSTOMIZATION_TO_ADD_AS_DEP="$2" |
| 1708 | |
| 1709 | local INPUT=$(cat) |
| 1710 | local FILTER="| select(.kind == \"Kustomization\") | select(.metadata.name == \"${KUSTOMIZATION_NAME}\")" |
| 1711 | |
| 1712 | # Check if the dependency was added already |
| 1713 | local TEST_RESULT=$( |
| 1714 | echo "${INPUT}" | \ |
| 1715 | is_element_on_list \ |
| 1716 | ".spec.dependsOn[].name" \ |
| 1717 | "${KUSTOMIZATION_TO_ADD_AS_DEP}" \ |
| 1718 | "${FILTER}" |
| 1719 | ) |
| 1720 | |
| 1721 | # If it existed already, returns the stream as is |
| 1722 | if [[ "${TEST_RESULT}" == "true" ]] |
| 1723 | then |
| 1724 | echo "${INPUT}" |
| 1725 | # Otherwise, processes the stream to add it |
| 1726 | else |
| 1727 | echo "${INPUT}" | \ |
| 1728 | patch_add_to_list \ |
| 1729 | ".spec.dependsOn" \ |
| 1730 | "{name: ${KUSTOMIZATION_TO_ADD_AS_DEP}}" \ |
| 1731 | "${FILTER}" |
| 1732 | fi |
| 1733 | } |
| 1734 | |
| 1735 | |
| 1736 | # Helper function to remove a dependency from a Kustomization |
| 1737 | function remove_dependency_from_kustomization_safely() { |
| 1738 | local KUSTOMIZATION_NAME="$1" |
| 1739 | local KUSTOMIZATION_TO_REMOVE_AS_DEP="$2" |
| 1740 | |
| 1741 | # Calculated inputs |
| 1742 | local KEY_PATH=".spec.dependsOn[] | select(.name == \"${KUSTOMIZATION_TO_REMOVE_AS_DEP}\")" |
| 1743 | local FILTER="| select(.kind == \"Kustomization\") | select(.metadata.name == \"${KUSTOMIZATION_NAME}\")" |
| 1744 | |
| 1745 | # Remove the entry from the dependency list (if it exists) |
| 1746 | yq "del((.items[]${FILTER})${KEY_PATH})" |
| 1747 | } |
| 1748 | |
| 1749 | |
| 1750 | # Ensure list of Kustomizations depend on a given Kustomization |
| 1751 | function add_dependency_to_set_of_kustomizations_safely() { |
| 1752 | local KS_NAME="$1" |
| 1753 | local THEY_DEPEND_ON_THIS="$2" |
| 1754 | |
| 1755 | local INPUT="$(cat)" |
| 1756 | local OUTPUT="" |
| 1757 | |
| 1758 | # For each of the Kustomizations on the comma-separated list, adds `KS_NAME` as one of their dependencies |
| 1759 | for KUST in ${THEY_DEPEND_ON_THIS//,/ } |
| 1760 | do |
| 1761 | local OUTPUT="$( |
| 1762 | echo "${INPUT}" | \ |
| 1763 | add_dependency_to_kustomization_safely \ |
| 1764 | "${KUST}" \ |
| 1765 | "${KS_NAME}" |
| 1766 | )" |
| 1767 | local INPUT="${OUTPUT}" |
| 1768 | done |
| 1769 | |
| 1770 | # Return the final `ResultList`, after all iterations |
| 1771 | echo "${OUTPUT}" |
| 1772 | } |
| 1773 | |
| 1774 | |
| 1775 | # Ensure list of Kustomizations no longer depend on a given Kustomization |
| 1776 | function remove_dependency_from_set_of_kustomizations_safely() { |
| 1777 | local KS_NAME="$1" |
| 1778 | local THEY_NO_LONGER_DEPEND_ON_THIS="$2" |
| 1779 | |
| 1780 | local INPUT="$(cat)" |
| 1781 | local OUTPUT="" |
| 1782 | |
| 1783 | # For each of the Kustomizations on the comma-separated list, removes `KS_NAME` from their dependencies |
| 1784 | for KUST in ${THEY_NO_LONGER_DEPEND_ON_THIS//,/ } |
| 1785 | do |
| 1786 | local OUTPUT="$( |
| 1787 | echo "${INPUT}" | \ |
| 1788 | remove_dependency_from_kustomization_safely \ |
| 1789 | "${KUST}" \ |
| 1790 | "${KS_NAME}" |
| 1791 | )" |
| 1792 | local INPUT="${OUTPUT}" |
| 1793 | done |
| 1794 | |
| 1795 | # Return the final `ResultList`, after all iterations |
| 1796 | echo "${OUTPUT}" |
| 1797 | } |
| 1798 | |
| 1799 | # ----- END of Helper functions for adding/removing a profile from a cluster ----- |
| 1800 | |
| 1801 | |
| 1802 | # Add an existing profile to a cluster |
| 1803 | function attach_profile_to_cluster() { |
| 1804 | local PROFILE_NAME="$1" |
| 1805 | local PROFILE_TYPE="$2" |
| 1806 | local PROJECT_NAME="$3" |
| 1807 | local CLUSTER_KUSTOMIZATION_NAME="$4" |
| 1808 | local FLEET_REPO_DIR="${5:-"${FLEET_REPO_DIR}"}" |
| 1809 | |
| 1810 | # Calculated inputs |
| 1811 | local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}" |
| 1812 | local TARGET_PROFILE_PATH="$( |
| 1813 | path_to_profile \ |
| 1814 | "${PROFILE_NAME}" \ |
| 1815 | "${PROFILE_TYPE}" \ |
| 1816 | "${PROJECT_NAME}" |
| 1817 | )" |
| 1818 | |
| 1819 | # Finds out which profiles it should depend on... and which profiles should depend on it |
| 1820 | local DEPENDS_ON=$( |
| 1821 | profiles_this_one_depends_on \ |
| 1822 | "${CLUSTER_KUSTOMIZATION_NAME}" \ |
| 1823 | "${PROFILE_TYPE}" \ |
| 1824 | "${FLEET_REPO_DIR}" |
| 1825 | ) |
| 1826 | |
| 1827 | local THEY_DEPEND_ON_THIS=$( |
| 1828 | profiles_depend_on_this_one \ |
| 1829 | "${CLUSTER_KUSTOMIZATION_NAME}" \ |
| 1830 | "${PROFILE_TYPE}" \ |
| 1831 | "${FLEET_REPO_DIR}" |
| 1832 | ) |
| 1833 | |
| 1834 | # Parameters for the new Kustomization object to point to the profile |
| 1835 | local KS_NAME="$(safe_name "${PROFILE_TYPE}-${PROFILE_NAME}")" |
| 1836 | local MANIFEST_FILENAME="${KS_NAME}.yaml" |
| 1837 | local KS_NS=flux-system |
| 1838 | local MANIFESTS_PATH="${TARGET_PROFILE_PATH}" |
| 1839 | local SOURCE_REPO=GitRepository/fleet-repo.flux-system |
| 1840 | local SOURCE_SYNC_INTERVAL="60m" |
| 1841 | local HEALTH_CHECK_TO="3m" |
| 1842 | local RETRY_INTERVAL="1m" |
| 1843 | local TIMEOUT="5m" |
| 1844 | local OPTIONS="\ |
| 1845 | --decryption-provider=sops \ |
| 1846 | --decryption-secret=sops-age \ |
| 1847 | --prune=true \ |
| 1848 | --timeout="${TIMEOUT}" \ |
| 1849 | --retry-interval="${RETRY_INTERVAL}" \ |
| 1850 | --label osm_profile_type="${PROFILE_TYPE}" |
| 1851 | " |
| 1852 | |
| 1853 | # Finally, we update the folder with all the required changes: |
| 1854 | # - Update pre-existing Kustomizations that should depend on the new profile (besides others). |
| 1855 | # - Create a new Kustomization pointing to the profile. |
| 1856 | # - Update Kustomize's `kustomization.yaml` at the root of the cluster folder to take into account the new Kustomization pointing to the profile. |
| 1857 | # - Update the cluster folder accordingly. |
| 1858 | folder2list \ |
| 1859 | "${CLUSTER_FOLDER}" | |
| 1860 | add_dependency_to_set_of_kustomizations_safely \ |
| 1861 | "${KS_NAME}" \ |
| 1862 | "${THEY_DEPEND_ON_THIS}" | \ |
| 1863 | generator_kustomization \ |
| 1864 | "${MANIFEST_FILENAME}" \ |
| 1865 | "${KS_NAME}" \ |
| 1866 | "${KS_NS}" \ |
| 1867 | "${SOURCE_REPO}" \ |
| 1868 | "${MANIFESTS_PATH}" \ |
| 1869 | "${SOURCE_SYNC_INTERVAL}" \ |
| 1870 | "${HEALTH_CHECK_TO}" \ |
| 1871 | "${DEPENDS_ON}" \ |
| 1872 | "${OPTIONS}" | \ |
| 1873 | patch_add_to_list \ |
| 1874 | ".resources" \ |
| 1875 | "${MANIFEST_FILENAME}" \ |
| 1876 | "| select(.kind == \"Kustomization\") | select(.apiVersion == \"kustomize.config.k8s.io/v1beta1\") | select(.metadata.annotations.\"config.kubernetes.io/path\" == \"kustomization.yaml\")" | \ |
| 1877 | list2folder_sync_replace \ |
| 1878 | "${CLUSTER_FOLDER}" |
| 1879 | } |
| 1880 | |
| 1881 | |
| 1882 | # Remove an existing profile from a cluster |
| 1883 | function detach_profile_from_cluster() { |
| 1884 | local PROFILE_NAME="$1" |
| 1885 | local PROFILE_TYPE="$2" |
| 1886 | local PROJECT_NAME="$3" |
| 1887 | local CLUSTER_KUSTOMIZATION_NAME="$4" |
| 1888 | local FLEET_REPO_DIR="${5:-"${FLEET_REPO_DIR}"}" |
| 1889 | |
| 1890 | # Calculated inputs |
| 1891 | local CLUSTER_FOLDER="${FLEET_REPO_DIR}/clusters/${CLUSTER_KUSTOMIZATION_NAME}" |
| 1892 | local TARGET_PROFILE_PATH="$( |
| 1893 | path_to_profile \ |
| 1894 | "${PROFILE_NAME}" \ |
| 1895 | "${PROFILE_TYPE}" \ |
| 1896 | "${PROJECT_NAME}" |
| 1897 | )" |
| 1898 | |
| 1899 | # Finds out which profiles still depend on it |
| 1900 | local THEY_DEPEND_ON_THIS=$( |
| 1901 | profiles_depend_on_this_one \ |
| 1902 | "${CLUSTER_KUSTOMIZATION_NAME}" \ |
| 1903 | "${PROFILE_TYPE}" \ |
| 1904 | "${FLEET_REPO_DIR}" |
| 1905 | ) |
| 1906 | |
| 1907 | # Parameters for the new Kustomization object to point to the profile |
| 1908 | local KS_NAME="$(safe_name "${PROFILE_TYPE}-${PROFILE_NAME}")" |
| 1909 | |
| 1910 | # Finally, we update the folder with all the required changes: |
| 1911 | # - Update pre-existing Kustomizations that should depend on the new profile (besides others). |
| 1912 | # - Create a new Kustomization pointing to the profile. |
| 1913 | # - Update Kustomize's `kustomization.yaml` at the root of the cluster folder so that it no longer tries to gather the Kustomization pointing to the profile. |
| 1914 | # - Update the cluster folder accordingly. |
| 1915 | folder2list \ |
| 1916 | "${CLUSTER_FOLDER}" | |
| 1917 | remove_dependency_from_set_of_kustomizations_safely \ |
| 1918 | "${KS_NAME}" \ |
| 1919 | "${THEY_DEPEND_ON_THIS}" | \ |
| 1920 | delete_object \ |
| 1921 | "${KS_NAME}" \ |
| 1922 | "Kustomization" \ |
| 1923 | "kustomize.toolkit.fluxcd.io/v1" | \ |
| 1924 | patch_delete_from_list \ |
| 1925 | ".resources[] | select(. == \"${MANIFEST_FILENAME}\") " \ |
| 1926 | "| select(.kind == \"Kustomization\") | select(.apiVersion == \"kustomize.config.k8s.io/v1beta1\") | select(.metadata.annotations.\"config.kubernetes.io/path\" == \"kustomization.yaml\")" | \ |
| 1927 | list2folder_sync_replace \ |
| 1928 | "${CLUSTER_FOLDER}" |
| 1929 | } |
| 1930 | |
| 1931 | |
| 1932 | # Low-level function to add a KSU into a profile |
| 1933 | function create_ksu_into_profile() { |
| 1934 | local KSU_NAME="$1" |
| 1935 | local TARGET_PROFILE_FOLDER="$2" |
| 1936 | local TEMPLATES_PATH="$3" |
| 1937 | local SW_CATALOGS_REPO_DIR="$4" |
| 1938 | local TRANSFORMER="${5:-noop_transformer}" |
| 1939 | |
| 1940 | # Gathers all optional parameters for transformer funcion (if any) and puts them into an array for further use |
| 1941 | local ALL_PARAMS=( "${@}" ) |
| 1942 | local TRANSFORMER_ARGS=( "${ALL_PARAMS[@]:5}" ) |
| 1943 | |
| 1944 | # Composes the route to the local templates folder |
| 1945 | local TEMPLATES_FOLDER="${SW_CATALOGS_REPO_DIR}/${TEMPLATES_PATH}" |
| 1946 | |
| 1947 | folder2list \ |
| 1948 | "${TEMPLATES_FOLDER}" | \ |
| 1949 | "${TRANSFORMER}" \ |
| 1950 | "${TRANSFORMER_ARGS[@]}" | \ |
| 1951 | prepend_folder_path "${KSU_NAME}/" | \ |
| 1952 | list2folder_cp_over \ |
| 1953 | "${TARGET_PROFILE_FOLDER}" |
| 1954 | } |
| 1955 | |
| 1956 | |
| 1957 | # Function to render a KSU from a `ResourceList` into a profile |
| 1958 | function render_ksu_into_profile() { |
| 1959 | local KSU_NAME="$1" |
| 1960 | local PROFILE_NAME="$2" |
| 1961 | local PROFILE_TYPE="$3" |
| 1962 | local PROJECT_NAME="${4:-"${MGMT_PROJECT_NAME}"}" |
| 1963 | local FLEET_REPO_DIR="$5" |
| 1964 | local SYNC="${6:-"false"}" |
| 1965 | |
| 1966 | local TARGET_PROFILE_PATH=$( |
| 1967 | path_to_profile \ |
| 1968 | "${PROFILE_NAME}" \ |
| 1969 | "${PROFILE_TYPE}" \ |
| 1970 | "${PROJECT_NAME}" |
| 1971 | ) |
| 1972 | |
| 1973 | local TARGET_PROFILE_FOLDER="${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}" |
| 1974 | |
| 1975 | # Determines the appropriate function depending on rendering strategy |
| 1976 | # - Sync (and potentially delete files in target folder) |
| 1977 | # - Copy over (only overwrite changed files, keep the rest) |
| 1978 | RENDERER="" |
| 1979 | if [[ ${SYNC,,} == "true" ]]; |
| 1980 | then |
| 1981 | RENDERER="list2folder_sync_replace" |
| 1982 | else |
| 1983 | RENDERER="list2folder_cp_over" |
| 1984 | fi |
| 1985 | |
| 1986 | # Render with the selected strategy |
| 1987 | [[ "${DRY_RUN,,}" != "true" ]] && mkdir -p "${TARGET_PROFILE_FOLDER}/${KSU_NAME}" |
| 1988 | "${RENDERER}" \ |
| 1989 | "${TARGET_PROFILE_FOLDER}/${KSU_NAME}" |
| 1990 | ## This is improves the behaviour of the following code, |
| 1991 | ## since avoids unintented deletions in parent folder due to sync |
| 1992 | # prepend_folder_path "${KSU_NAME}/" | \ |
| 1993 | # "${RENDERER}" \ |
| 1994 | # "${TARGET_PROFILE_FOLDER}" |
| 1995 | } |
| 1996 | |
| 1997 | |
| 1998 | # High-level function to add a KSU into a profile for the case where |
| 1999 | # 1. It is originated from an OKA, and |
| 2000 | # 2. It is based on a HelmRelease. |
| 2001 | function create_hr_ksu_into_profile() { |
| 2002 | # Base KSU generation from template |
| 2003 | ## `TEMPLATES_DIR` is the result of: |
| 2004 | ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}/{{inputs.parameters.templates_path}}" |
| 2005 | local TEMPLATES_DIR="$1" |
| 2006 | local SUBSTITUTE_ENVIRONMENT="${2:-"false"}" |
| 2007 | local SUBSTITUTION_FILTER="${3:-""}" |
| 2008 | local CUSTOM_ENV_VARS="${4:-""}" |
| 2009 | # Patch HelmRelease in KSU with inline values |
| 2010 | local KUSTOMIZATION_NAME="$5" |
| 2011 | local HELMRELEASE_NAME="$6" |
| 2012 | local INLINE_VALUES="${7:-""}" |
| 2013 | # Secret reference and generation (if required) |
| 2014 | local IS_PREEXISTING_SECRET="${8:-"false"}" |
| 2015 | local TARGET_NS="$9" |
| 2016 | local VALUES_SECRET_NAME="${10}" |
| 2017 | local SECRET_KEY="${11:-"values.yaml"}" |
| 2018 | local AGE_PUBLIC_KEY="${12}" |
| 2019 | ## `SECRET_VALUES` will be obtained from the |
| 2020 | ## secret named after the input parameter `reference_secret_for_values`, |
| 2021 | ## and from the key named after the input parameter `reference_key_for_values` |
| 2022 | local LOCAL_SECRET_VALUES="${13:-"${SECRET_VALUES}"}" |
| 2023 | # ConfigMap reference and generation (if required) |
| 2024 | local IS_PREEXISTING_CM="${14:-"false"}" |
| 2025 | local VALUES_CM_NAME="${15:-""}" |
| 2026 | local CM_KEY="${16:-""}" |
| 2027 | local CM_VALUES="${17:-""}" |
| 2028 | # KSU rendering |
| 2029 | local KSU_NAME="${18}" |
| 2030 | local PROFILE_NAME="${19}" |
| 2031 | local PROFILE_TYPE="${20}" |
| 2032 | local PROJECT_NAME="${21:-"osm_admin"}" |
| 2033 | ## `FLEET_REPO_DIR` is the result of: |
| 2034 | ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}" |
| 2035 | local FLEET_REPO_DIR="${22:-"/fleet/fleet-osm/"}" |
| 2036 | local SYNC="${23:-"true"}" |
| 2037 | |
| 2038 | # Decides which steps may be skipped |
| 2039 | HAS_INLINE_VALUES=$([[ -n "${INLINE_VALUES}" ]]; echo $?) |
| 2040 | HAS_REFERENCES=$([[ ( -n "${VALUES_SECRET_NAME}" ) || ( -n "${VALUES_CM_NAME}" ) ]]; echo $?) |
| 2041 | NEEDS_NEW_SECRET=$([[ ( -n "${VALUES_SECRET_NAME}" ) && ( "${IS_PREEXISTING_SECRET,,}" == "false" ) ]]; echo $?) |
| 2042 | NEEDS_NEW_CM=$([[ ( -n "${VALUES_CM_NAME}" ) && ( "${IS_PREEXISTING_CM,,}" == "false" ) ]]; echo $?) |
| 2043 | ECHO_RESOURCELIST=$([[ "${DEBUG,,}" == "true" ]]; echo $?) |
| 2044 | |
| 2045 | # If applicable, loads additional environment variables |
| 2046 | if [[ -n "${CUSTOM_ENV_VARS}" ]]; |
| 2047 | then |
| 2048 | set -a |
| 2049 | source <(echo "${CUSTOM_ENV_VARS}") |
| 2050 | set +a |
| 2051 | fi |
| 2052 | |
| 2053 | # Runs workflow |
| 2054 | folder2list_generator \ |
| 2055 | "${TEMPLATES_DIR}" \ |
| 2056 | "${SUBSTITUTE_ENVIRONMENT}" \ |
| 2057 | "${SUBSTITUTION_FILTER}" | \ |
| 2058 | transform_if \ |
| 2059 | "${HAS_INLINE_VALUES}" \ |
| 2060 | add_values_to_helmrelease_via_ks \ |
| 2061 | "${KUSTOMIZATION_NAME}" \ |
| 2062 | "${HELMRELEASE_NAME}" \ |
| 2063 | "${INLINE_VALUES}" | \ |
| 2064 | transform_if \ |
| 2065 | "${HAS_REFERENCES}" \ |
| 2066 | add_ref_values_to_hr_via_ks \ |
| 2067 | "${KUSTOMIZATION_NAME}" \ |
| 2068 | "${HELMRELEASE_NAME}" \ |
| 2069 | "${VALUES_SECRET_NAME}" \ |
| 2070 | "${VALUES_CM_NAME}" | \ |
| 2071 | transform_if \ |
| 2072 | "${NEEDS_NEW_SECRET}" \ |
| 2073 | make_generator \ |
| 2074 | "hr-values-secret.yaml" \ |
| 2075 | kubectl_encrypt \ |
| 2076 | "${AGE_PUBLIC_KEY}" \ |
| 2077 | create \ |
| 2078 | secret \ |
| 2079 | generic \ |
| 2080 | "${VALUES_SECRET_NAME}" \ |
| 2081 | --namespace="${TARGET_NS}" \ |
| 2082 | --from-file="${SECRET_KEY}"=<(echo "${LOCAL_SECRET_VALUES}") \ |
| 2083 | -o=yaml \ |
| 2084 | --dry-run=client | \ |
| 2085 | transform_if \ |
| 2086 | "${NEEDS_NEW_CM}" \ |
| 2087 | make_generator \ |
| 2088 | "hr-values-configmap.yaml" \ |
| 2089 | kubectl \ |
| 2090 | create \ |
| 2091 | configmap \ |
| 2092 | "${VALUES_CM_NAME}" \ |
| 2093 | --namespace="${TARGET_NS}" \ |
| 2094 | --from-file="${SECRET_KEY}"=<(echo "${CM_VALUES}") \ |
| 2095 | -o=yaml \ |
| 2096 | --dry-run=client | \ |
| 2097 | transform_if \ |
| 2098 | "${ECHO_RESOURCELIST}" \ |
| 2099 | tee /dev/stderr | \ |
| 2100 | render_ksu_into_profile \ |
| 2101 | "${KSU_NAME}" \ |
| 2102 | "${PROFILE_NAME}" \ |
| 2103 | "${PROFILE_TYPE}" \ |
| 2104 | "${PROJECT_NAME}" \ |
| 2105 | "${FLEET_REPO_DIR}" \ |
| 2106 | "${SYNC}" |
| 2107 | } |
| 2108 | |
| 2109 | |
| 2110 | # High-level function to update a KSU for the case where |
| 2111 | # 1. It is originated from an OKA, and |
| 2112 | # 2. It is based on a HelmRelease. |
| 2113 | # NOTE: It is an alias of `create_hr_ksu_into_profile`, setting `sync` to true |
| 2114 | function update_hr_ksu_into_profile() { |
| 2115 | # Base KSU generation from template |
| 2116 | ## `TEMPLATES_DIR` is the result of: |
| 2117 | ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}/{{inputs.parameters.templates_path}}" |
| 2118 | local TEMPLATES_DIR="$1" |
| 2119 | local SUBSTITUTE_ENVIRONMENT="${2:-"false"}" |
| 2120 | local SUBSTITUTION_FILTER="${3:-""}" |
| 2121 | local CUSTOM_ENV_VARS="${4:-""}" |
| 2122 | # Patch HelmRelease in KSU with inline values |
| 2123 | local KUSTOMIZATION_NAME="$5" |
| 2124 | local HELMRELEASE_NAME="$6" |
| 2125 | local INLINE_VALUES="${7:-""}" |
| 2126 | # Secret reference and generation (if required) |
| 2127 | local IS_PREEXISTING_SECRET="${8:-"false"}" |
| 2128 | local TARGET_NS="$9" |
| 2129 | local VALUES_SECRET_NAME="${10}" |
| 2130 | local SECRET_KEY="${11:-"values.yaml"}" |
| 2131 | local AGE_PUBLIC_KEY="${12}" |
| 2132 | ## `SECRET_VALUES` will be obtained from the |
| 2133 | ## secret named after the input parameter `reference_secret_for_values`, |
| 2134 | ## and from the key named after the input parameter `reference_key_for_values` |
| 2135 | local LOCAL_SECRET_VALUES="${13:-"${SECRET_VALUES}"}" |
| 2136 | # ConfigMap reference and generation (if required) |
| 2137 | local IS_PREEXISTING_CM="${14:-"false"}" |
| 2138 | local VALUES_CM_NAME="${15:-""}" |
| 2139 | local CM_KEY="${16:-""}" |
| 2140 | local CM_VALUES="${17:-""}" |
| 2141 | # KSU rendering |
| 2142 | local KSU_NAME="${18}" |
| 2143 | local PROFILE_NAME="${19}" |
| 2144 | local PROFILE_TYPE="${20}" |
| 2145 | local PROJECT_NAME="${21:-"osm_admin"}" |
| 2146 | ## `FLEET_REPO_DIR` is the result of: |
| 2147 | ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}" |
| 2148 | local FLEET_REPO_DIR="${22:-"/fleet/fleet-osm/"}" |
| 2149 | # local SYNC="${23:-"true"}" |
| 2150 | |
| 2151 | |
| 2152 | # This function is just an alias of `create_hr_ksu_into_profile` |
| 2153 | # forcing synchronization over the KSU folder |
| 2154 | create_hr_ksu_into_profile \ |
| 2155 | "${TEMPLATES_DIR}" \ |
| 2156 | "${SUBSTITUTE_ENVIRONMENT}" \ |
| 2157 | "${SUBSTITUTION_FILTER}" \ |
| 2158 | "${CUSTOM_ENV_VARS}" \ |
| 2159 | "${KUSTOMIZATION_NAME}" \ |
| 2160 | "${HELMRELEASE_NAME}" \ |
| 2161 | "${INLINE_VALUES}" \ |
| 2162 | "${IS_PREEXISTING_SECRET}" \ |
| 2163 | "${TARGET_NS}" \ |
| 2164 | "${VALUES_SECRET_NAME}" \ |
| 2165 | "${SECRET_KEY}" \ |
| 2166 | "${AGE_PUBLIC_KEY}" \ |
| 2167 | "${LOCAL_SECRET_VALUES}" \ |
| 2168 | "${IS_PREEXISTING_CM}" \ |
| 2169 | "${VALUES_CM_NAME}" \ |
| 2170 | "${CM_KEY}" \ |
| 2171 | "${CM_VALUES}" \ |
| 2172 | "${KSU_NAME}" \ |
| 2173 | "${PROFILE_NAME}" \ |
| 2174 | "${PROFILE_TYPE}" \ |
| 2175 | "${PROJECT_NAME}" \ |
| 2176 | "${FLEET_REPO_DIR}" \ |
| 2177 | "true" |
| 2178 | } |
| 2179 | |
| 2180 | |
| 2181 | # High-level function to create a "generated" KSU into a profile when: |
| 2182 | # 1. There is no template (OKA) available. |
| 2183 | # 2. The SW is based on a Helm Chart that we want to deploy. |
| 2184 | function create_generated_ksu_from_helm_into_profile() { |
| 2185 | # HelmRelease generation |
| 2186 | local HELMRELEASE_NAME="$1" |
| 2187 | local CHART_NAME="$2" |
| 2188 | local CHART_VERSION="$3" |
| 2189 | local TARGET_NS="$4" |
| 2190 | local CREATE_NS="${5:-"true"}" |
| 2191 | # Repo source generation |
| 2192 | local IS_PREEXISTING_REPO="${6:-"false"}" |
| 2193 | local HELMREPO_NAME="$7" |
| 2194 | local HELMREPO_URL="${8:-""}" |
| 2195 | local HELMREPO_NS="${9:-"${TARGET_NS}"}" |
| 2196 | local HELMREPO_SECRET_REF="${10:-""}" |
| 2197 | # HelmRelease inline values (if any) |
| 2198 | local INLINE_VALUES="${11:-""}" |
| 2199 | # Secret reference and generation (if required) |
| 2200 | local IS_PREEXISTING_SECRET="${12:-"false"}" |
| 2201 | local VALUES_SECRET_NAME="${13}" |
| 2202 | local SECRET_KEY="${14:-"values.yaml"}" |
| 2203 | local AGE_PUBLIC_KEY="${15}" |
| 2204 | ## `SECRET_VALUES` will be obtained from the |
| 2205 | ## secret named after the input parameter `reference_secret_for_values`, |
| 2206 | ## and from the key named after the input parameter `reference_key_for_values` |
| 2207 | local LOCAL_SECRET_VALUES="${16:-"${SECRET_VALUES}"}" |
| 2208 | # ConfigMap reference and generation (if required) |
| 2209 | local IS_PREEXISTING_CM="${17:-"false"}" |
| 2210 | local VALUES_CM_NAME="${18:-""}" |
| 2211 | local CM_KEY="${19:-""}" |
| 2212 | local CM_VALUES="${20:-""}" |
| 2213 | # KSU rendering |
| 2214 | local KSU_NAME="${21}" |
| 2215 | local PROFILE_NAME="${22}" |
| 2216 | local PROFILE_TYPE="${23}" |
| 2217 | local PROJECT_NAME="${24:-"osm_admin"}" |
| 2218 | ## `FLEET_REPO_DIR` is the result of: |
| 2219 | ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}" |
| 2220 | local FLEET_REPO_DIR="${25:-"/fleet/fleet-osm/"}" |
| 2221 | # By default, it will not syncronize, so that we can easily accumulate more than |
| 2222 | # one Helm chart into the same KSU if desired |
| 2223 | local SYNC="${26:-"false"}" |
| 2224 | |
| 2225 | # Decides which steps may be skipped |
| 2226 | local NEEDS_NEW_NS=$([[ "${CREATE_NS,,}" == "true" ]]; echo $?) |
| 2227 | local NEEDS_NEW_REPO_SOURCE=$([[ "${IS_PREEXISTING_REPO,,}" == "false" ]]; echo $?) |
| 2228 | local NEEDS_NEW_SECRET=$([[ ( -n "${VALUES_SECRET_NAME}" ) && ( "${IS_PREEXISTING_SECRET,,}" == "false" ) ]]; echo $?) |
| 2229 | local NEEDS_NEW_CM=$([[ ( -n "${VALUES_CM_NAME}" ) && ( "${IS_PREEXISTING_CM,,}" == "false" ) ]]; echo $?) |
| 2230 | local ECHO_RESOURCELIST=$([[ "${DEBUG,,}" == "true" ]]; echo $?) |
| 2231 | |
| 2232 | # Determine extra options for HelmRelease creation and define full command |
| 2233 | OPTION_CHART_VERSION="" |
| 2234 | [[ -n "${CHART_VERSION}" ]] && OPTION_CHART_VERSION='--chart-version=${CHART_VERSION}' |
| 2235 | OPTION_INLINE_VALUES="" |
| 2236 | [[ -n "${INLINE_VALUES}" ]] && OPTION_INLINE_VALUES='--values=<( |
| 2237 | echo "${INLINE_VALUES}" |
| 2238 | )' |
| 2239 | OPTION_REFERENCE_SECRET="" |
| 2240 | [[ -n "${VALUES_SECRET_NAME}" ]] && OPTION_REFERENCE_SECRET='--values-from=Secret/${VALUES_SECRET_NAME}' |
| 2241 | OPTION_REFERENCE_CM="" |
| 2242 | [[ -n "${VALUES_CM_NAME}" ]] && OPTION_REFERENCE_CM='--values-from=ConfigMap/${VALUES_CM_NAME}' |
| 2243 | |
| 2244 | export HR_COMMAND="\ |
| 2245 | flux \ |
| 2246 | -n "${TARGET_NS}" \ |
| 2247 | create hr "${HELMRELEASE_NAME}" \ |
| 2248 | --chart="${CHART_NAME}" \ |
| 2249 | --source=HelmRepository/"${HELMREPO_NAME}.${HELMREPO_NS}" \ |
| 2250 | "${OPTION_CHART_VERSION}" \ |
| 2251 | "${OPTION_INLINE_VALUES}" \ |
| 2252 | "${OPTION_REFERENCE_SECRET}" \ |
| 2253 | "${OPTION_REFERENCE_CM}" \ |
| 2254 | --export |
| 2255 | " |
| 2256 | |
| 2257 | # Determine extra options for Helm source repo creation and define full command |
| 2258 | OPTION_REPO_SECRET="" |
| 2259 | [[ -n "${HELMREPO_SECRET_REF}" ]] && OPTION_REPO_SECRET='--secret-ref=${HELMREPO_SECRET_REF}' |
| 2260 | |
| 2261 | export REPO_COMMAND="\ |
| 2262 | flux \ |
| 2263 | -n "${HELMREPO_NS}" \ |
| 2264 | create source helm "${HELMREPO_NAME}" \ |
| 2265 | --url="${HELMREPO_URL}" \ |
| 2266 | "${OPTION_REPO_SECRET}" \ |
| 2267 | --export |
| 2268 | " |
| 2269 | |
| 2270 | # Runs workflow |
| 2271 | echo "" | \ |
| 2272 | make_generator \ |
| 2273 | "helm-release.yaml" \ |
| 2274 | eval "${HR_COMMAND}" | \ |
| 2275 | transform_if \ |
| 2276 | "${NEEDS_NEW_NS}" \ |
| 2277 | make_generator \ |
| 2278 | "ns-for-hr.yaml" \ |
| 2279 | kubectl \ |
| 2280 | create \ |
| 2281 | namespace \ |
| 2282 | "${TARGET_NS}" \ |
| 2283 | -o=yaml \ |
| 2284 | --dry-run=client | \ |
| 2285 | transform_if \ |
| 2286 | "${NEEDS_NEW_REPO_SOURCE}" \ |
| 2287 | make_generator \ |
| 2288 | "helm-repo.yaml" \ |
| 2289 | eval "${REPO_COMMAND}" | \ |
| 2290 | transform_if \ |
| 2291 | "${NEEDS_NEW_SECRET}" \ |
| 2292 | make_generator \ |
| 2293 | "hr-values-secret.yaml" \ |
| 2294 | kubectl_encrypt \ |
| 2295 | "${AGE_PUBLIC_KEY}" \ |
| 2296 | create \ |
| 2297 | secret \ |
| 2298 | generic \ |
| 2299 | "${VALUES_SECRET_NAME}" \ |
| 2300 | --namespace="${TARGET_NS}" \ |
| 2301 | --from-file="${SECRET_KEY}"=<(echo "${LOCAL_SECRET_VALUES}") \ |
| 2302 | -o=yaml \ |
| 2303 | --dry-run=client | \ |
| 2304 | transform_if \ |
| 2305 | "${NEEDS_NEW_CM}" \ |
| 2306 | make_generator \ |
| 2307 | "hr-values-configmap.yaml" \ |
| 2308 | kubectl \ |
| 2309 | create \ |
| 2310 | configmap \ |
| 2311 | "${VALUES_CM_NAME}" \ |
| 2312 | --namespace="${TARGET_NS}" \ |
| 2313 | --from-file="${SECRET_KEY}"=<(echo "${CM_VALUES}") \ |
| 2314 | -o=yaml \ |
| 2315 | --dry-run=client | \ |
| 2316 | transform_if \ |
| 2317 | "${ECHO_RESOURCELIST}" \ |
| 2318 | tee /dev/stderr | \ |
| 2319 | render_ksu_into_profile \ |
| 2320 | "${KSU_NAME}" \ |
| 2321 | "${PROFILE_NAME}" \ |
| 2322 | "${PROFILE_TYPE}" \ |
| 2323 | "${PROJECT_NAME}" \ |
| 2324 | "${FLEET_REPO_DIR}" \ |
| 2325 | "${SYNC}" |
| 2326 | } |
| 2327 | |
| 2328 | |
| 2329 | # High-level function to update a "generated" KSU: |
| 2330 | # 1. There is no template (OKA) available. |
| 2331 | # 2. The SW is based on a Helm Chart that we want to deploy. |
| 2332 | # NOTE: It is an alias of `create_generated_ksu_from_helm_into_profile`, setting `sync` to true |
| 2333 | function update_generated_ksu_from_helm_into_profile() { |
| 2334 | # HelmRelease generation |
| 2335 | local HELMRELEASE_NAME="$1" |
| 2336 | local CHART_NAME="$2" |
| 2337 | local CHART_VERSION="$3" |
| 2338 | local TARGET_NS="$4" |
| 2339 | local CREATE_NS="${5:-"true"}" |
| 2340 | # Repo source generation |
| 2341 | local IS_PREEXISTING_REPO="${6:-"false"}" |
| 2342 | local HELMREPO_NAME="$7" |
| 2343 | local HELMREPO_URL="${8:-""}" |
| 2344 | local HELMREPO_NS="${9:-"${TARGET_NS}"}" |
| 2345 | local HELMREPO_SECRET_REF="${10:-""}" |
| 2346 | # HelmRelease inline values (if any) |
| 2347 | local INLINE_VALUES="${11:-""}" |
| 2348 | # Secret reference and generation (if required) |
| 2349 | local IS_PREEXISTING_SECRET="${12:-"false"}" |
| 2350 | local VALUES_SECRET_NAME="${13}" |
| 2351 | local SECRET_KEY="${14:-"values.yaml"}" |
| 2352 | local AGE_PUBLIC_KEY="${15}" |
| 2353 | ## `SECRET_VALUES` will be obtained from the |
| 2354 | ## secret named after the input parameter `reference_secret_for_values`, |
| 2355 | ## and from the key named after the input parameter `reference_key_for_values` |
| 2356 | local LOCAL_SECRET_VALUES="${16:-"${SECRET_VALUES}"}" |
| 2357 | # ConfigMap reference and generation (if required) |
| 2358 | local IS_PREEXISTING_CM="${17:-"false"}" |
| 2359 | local VALUES_CM_NAME="${18:-""}" |
| 2360 | local CM_KEY="${19:-""}" |
| 2361 | local CM_VALUES="${20:-""}" |
| 2362 | # KSU rendering |
| 2363 | local KSU_NAME="${21}" |
| 2364 | local PROFILE_NAME="${22}" |
| 2365 | local PROFILE_TYPE="${23}" |
| 2366 | local PROJECT_NAME="${24:-"osm_admin"}" |
| 2367 | ## `FLEET_REPO_DIR` is the result of: |
| 2368 | ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}" |
| 2369 | local FLEET_REPO_DIR="${25:-"/fleet/fleet-osm/"}" |
| 2370 | # By default, it will not syncronize, so that we can easily accumulate more than |
| 2371 | # one Helm chart into the same KSU if desired |
| 2372 | # local SYNC="${26:-"false"}" |
| 2373 | |
| 2374 | # Decides which steps may be skipped |
| 2375 | local NEEDS_NEW_NS=$([[ "${CREATE_NS,,}" == "true" ]]; echo $?) |
| 2376 | local NEEDS_NEW_REPO_SOURCE=$([[ "${IS_PREEXISTING_REPO,,}" == "false" ]]; echo $?) |
| 2377 | local NEEDS_NEW_SECRET=$([[ ( -n "${VALUES_SECRET_NAME}" ) && ( "${IS_PREEXISTING_SECRET,,}" == "false" ) ]]; echo $?) |
| 2378 | local NEEDS_NEW_CM=$([[ ( -n "${VALUES_CM_NAME}" ) && ( "${IS_PREEXISTING_CM,,}" == "false" ) ]]; echo $?) |
| 2379 | local ECHO_RESOURCELIST=$([[ "${DEBUG,,}" == "true" ]]; echo $?) |
| 2380 | |
| 2381 | |
| 2382 | # This function is just an alias of `create_generated_ksu_from_helm_into_profile` |
| 2383 | # forcing synchronization over the KSU folder |
| 2384 | create_generated_ksu_from_helm_into_profile \ |
| 2385 | "${HELMRELEASE_NAME}" \ |
| 2386 | "${CHART_NAME}" \ |
| 2387 | "${CHART_VERSION}" \ |
| 2388 | "${TARGET_NS}" \ |
| 2389 | "${CREATE_NS}" \ |
| 2390 | "${IS_PREEXISTING_REPO}" \ |
| 2391 | "${HELMREPO_NAME}" \ |
| 2392 | "${HELMREPO_URL}" \ |
| 2393 | "${HELMREPO_NS}" \ |
| 2394 | "${HELMREPO_SECRET_REF}" \ |
| 2395 | "${INLINE_VALUES}" \ |
| 2396 | "${IS_PREEXISTING_SECRET}" \ |
| 2397 | "${VALUES_SECRET_NAME}" \ |
| 2398 | "${SECRET_KEY}" \ |
| 2399 | "${AGE_PUBLIC_KEY}" \ |
| 2400 | "${LOCAL_SECRET_VALUES}" \ |
| 2401 | "${IS_PREEXISTING_CM}" \ |
| 2402 | "${VALUES_CM_NAME}" \ |
| 2403 | "${CM_KEY}" \ |
| 2404 | "${CM_VALUES}" \ |
| 2405 | "${KSU_NAME}" \ |
| 2406 | "${PROFILE_NAME}" \ |
| 2407 | "${PROFILE_TYPE}" \ |
| 2408 | "${PROJECT_NAME}" \ |
| 2409 | "${FLEET_REPO_DIR}" \ |
| 2410 | "true" |
| 2411 | } |
| 2412 | |
| 2413 | |
| 2414 | # Low-level function to delete a KSU from a profile |
| 2415 | function delete_ksu_from_profile_path() { |
| 2416 | local KSU_NAME="$1" |
| 2417 | local TARGET_PROFILE_PATH="$2" |
| 2418 | local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}" |
| 2419 | |
| 2420 | # Calculate profile folder |
| 2421 | TARGET_PROFILE_FOLDER="${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}" |
| 2422 | |
| 2423 | # Delete the KSU folder |
| 2424 | rm -rf "${TARGET_PROFILE_FOLDER}/${KSU_NAME}" |
| 2425 | } |
| 2426 | |
| 2427 | |
| 2428 | # High-level function to delete a KSU from a profile |
| 2429 | function delete_ksu_from_profile() { |
| 2430 | local KSU_NAME="$1" |
| 2431 | local PROFILE_NAME="$2" |
| 2432 | local PROFILE_TYPE="$3" |
| 2433 | local PROJECT_NAME="${4:-"osm_admin"}" |
| 2434 | ## `FLEET_REPO_DIR` is the result of: |
| 2435 | ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}" |
| 2436 | local FLEET_REPO_DIR="$5" |
| 2437 | |
| 2438 | # Calculate profile folder |
| 2439 | local TARGET_PROFILE_PATH=$( |
| 2440 | path_to_profile \ |
| 2441 | "${PROFILE_NAME}" \ |
| 2442 | "${PROFILE_TYPE}" \ |
| 2443 | "${PROJECT_NAME}" |
| 2444 | ) |
| 2445 | TARGET_PROFILE_FOLDER="${FLEET_REPO_DIR}/${TARGET_PROFILE_PATH}" |
| 2446 | |
| 2447 | # Delete the KSU folder |
| 2448 | rm -rf "${TARGET_PROFILE_FOLDER}/${KSU_NAME}" |
| 2449 | } |
| 2450 | |
| 2451 | |
| 2452 | # High-level function to clone a KSU from a profile to another |
| 2453 | function clone_ksu() { |
| 2454 | local SOURCE_KSU_NAME="$1" |
| 2455 | local SOURCE_PROFILE_NAME="$2" |
| 2456 | local SOURCE_PROFILE_TYPE="$3" |
| 2457 | local SOURCE_PROJECT_NAME="${4:-"osm_admin"}" |
| 2458 | local DESTINATION_KSU_NAME="${5:-"${SOURCE_KSU_NAME}"}" |
| 2459 | local DESTINATION_PROFILE_NAME="${6:-"${SOURCE_PROFILE_NAME}"}" |
| 2460 | local DESTINATION_PROFILE_TYPE="${7:-"${SOURCE_PROFILE_TYPE}"}" |
| 2461 | local DESTINATION_PROJECT_NAME="${8:-"${SOURCE_PROJECT_NAME}"}" |
| 2462 | ## `FLEET_REPO_DIR` is the result of: |
| 2463 | ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}" |
| 2464 | local FLEET_REPO_DIR="$9" |
| 2465 | |
| 2466 | |
| 2467 | # If source and destination are identical, aborts |
| 2468 | if [[ |
| 2469 | ("${SOURCE_KSU_NAME}" == "${DESTINATION_KSU_NAME}") && \ |
| 2470 | ("${SOURCE_PROFILE_NAME}" == "${DESTINATION_PROFILE_NAME}") && \ |
| 2471 | ("${SOURCE_PROFILE_TYPE}" == "${DESTINATION_PROFILE_TYPE}") && \ |
| 2472 | ("${SOURCE_PROJECT_NAME}" == "${DESTINATION_PROJECT_NAME}") \ |
| 2473 | ]]; |
| 2474 | then |
| 2475 | return 1 |
| 2476 | fi |
| 2477 | |
| 2478 | # Calculate profile folders |
| 2479 | local SOURCE_PROFILE_PATH=$( |
| 2480 | path_to_profile \ |
| 2481 | "${SOURCE_PROFILE_NAME}" \ |
| 2482 | "${SOURCE_PROFILE_TYPE}" \ |
| 2483 | "${SOURCE_PROJECT_NAME}" |
| 2484 | ) |
| 2485 | local SOURCE_PROFILE_FOLDER="${FLEET_REPO_DIR}/${SOURCE_PROFILE_PATH}" |
| 2486 | local DESTINATION_PROFILE_PATH=$( |
| 2487 | path_to_profile \ |
| 2488 | "${DESTINATION_PROFILE_NAME}" \ |
| 2489 | "${DESTINATION_PROFILE_TYPE}" \ |
| 2490 | "${DESTINATION_PROJECT_NAME}" |
| 2491 | ) |
| 2492 | local DESTINATION_PROFILE_FOLDER="${FLEET_REPO_DIR}/${DESTINATION_PROFILE_PATH}" |
| 2493 | |
| 2494 | # Clone KSU folder |
| 2495 | cp -ar \ |
| 2496 | "${SOURCE_PROFILE_FOLDER}/${SOURCE_KSU_NAME}" \ |
| 2497 | "${DESTINATION_PROFILE_FOLDER}/${DESTINATION_KSU_NAME}" |
| 2498 | } |
| 2499 | |
| 2500 | |
| 2501 | # Create a `ProviderConfig` for a CrossPlane provider |
| 2502 | function create_crossplane_providerconfig() { |
| 2503 | local PROVIDERCONFIG_NAME="$1" |
| 2504 | # As of today, one among `azure`, `aws` or `gcp`: |
| 2505 | local PROVIDER_TYPE="$2" |
| 2506 | local CRED_SECRET_NAME="$3" |
| 2507 | local CRED_SECRET_KEY="${4:-"creds"}" |
| 2508 | local CRED_SECRET_NS="${5:-"crossplane-system"}" |
| 2509 | # If empty, it assumes the secret already exists |
| 2510 | local CRED_SECRET_CONTENT="${6:-"${CRED_SECRET_CONTENT:-""}"}" |
| 2511 | local AGE_PUBLIC_KEY_MGMT="$7" |
| 2512 | ## `FLEET_REPO_DIR` is the result of: |
| 2513 | ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}" |
| 2514 | local FLEET_REPO_DIR="${8:-"${FLEET_REPO_DIR}"}" |
| 2515 | ## `SW_CATALOGS_REPO_DIR` is the result of: |
| 2516 | ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}" |
| 2517 | local SW_CATALOGS_REPO_DIR="${9:-"${SW_CATALOGS_REPO_DIR}"}" |
| 2518 | # Only when applicable |
| 2519 | local TARGET_GCP_PROJECT="${10:-""}" |
| 2520 | # Do not touch unless strictly needed |
| 2521 | local BASE_TEMPLATES_PATH="${11:-"infra-configs/crossplane/providers"}" |
| 2522 | local OSM_PROJECT_NAME="${12:-"osm_admin"}" |
| 2523 | local MGMT_CLUSTER_NAME="${13:-"_management"}" |
| 2524 | |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 2525 | |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 2526 | # Is the provider type supported? |
| 2527 | local VALID_PROVIDERS=("aws" "azure" "gcp") |
| 2528 | PROVIDER_TYPE="${PROVIDER_TYPE,,}" |
| 2529 | [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${PROVIDER_TYPE}")) ]] && return 1 |
| 2530 | |
| 2531 | # Determines the source dir for the templates and the target folder in Fleet |
| 2532 | local TEMPLATES_DIR="${SW_CATALOGS_REPO_DIR}/${BASE_TEMPLATES_PATH}/${PROVIDER_TYPE}/templates" |
| 2533 | local TARGET_FOLDER="${FLEET_REPO_DIR}/${OSM_PROJECT_NAME}/infra-config-profiles/${MGMT_CLUSTER_NAME}/crossplane-providerconfigs/${PROVIDER_TYPE}" |
| 2534 | |
| 2535 | # Determine which optional steps may be needed |
| 2536 | local NEEDS_NEW_SECRET=$([[ -n "${CRED_SECRET_CONTENT}" ]]; echo $?) |
| 2537 | local NEEDS_PROJECT_NAME=$([[ "${PROVIDER_TYPE}" == "gcp" ]]; echo $?) |
| 2538 | |
| 2539 | # Renders the `ProviderConfig` manifest and the encrypted secret (if applicable) |
| 2540 | echo "" | \ |
| 2541 | folder2list_generator \ |
| 2542 | "${TEMPLATES_DIR}" | \ |
| 2543 | patch_replace \ |
| 2544 | ".metadata.name" \ |
| 2545 | "${PROVIDERCONFIG_NAME}" \ |
| 2546 | "| select(.kind == \"ProviderConfig\")" | \ |
| 2547 | patch_replace \ |
| 2548 | ".spec.credentials.secretRef.name" \ |
| 2549 | "${CRED_SECRET_NAME}" \ |
| 2550 | "| select(.kind == \"ProviderConfig\")" | \ |
| 2551 | patch_replace \ |
| 2552 | ".spec.credentials.secretRef.key" \ |
| 2553 | "${CRED_SECRET_KEY}" \ |
| 2554 | "| select(.kind == \"ProviderConfig\")" | \ |
| 2555 | patch_replace \ |
| 2556 | ".spec.credentials.secretRef.namespace" \ |
| 2557 | "${CRED_SECRET_NS}" \ |
| 2558 | "| select(.kind == \"ProviderConfig\")" | \ |
| 2559 | transform_if \ |
| 2560 | "${NEEDS_PROJECT_NAME}" \ |
| 2561 | patch_replace \ |
| 2562 | ".spec.projectID" \ |
| 2563 | "${TARGET_GCP_PROJECT}" \ |
| 2564 | "| select(.kind == \"ProviderConfig\")" | \ |
| 2565 | transform_if \ |
| 2566 | "${NEEDS_NEW_SECRET}" \ |
| 2567 | make_generator \ |
| 2568 | "credentials-secret.yaml" \ |
| 2569 | kubectl_encrypt \ |
| 2570 | "${AGE_PUBLIC_KEY_MGMT}" \ |
| 2571 | create \ |
| 2572 | secret \ |
| 2573 | generic \ |
| 2574 | "${CRED_SECRET_NAME}" \ |
| 2575 | --namespace="${CRED_SECRET_NS}" \ |
| 2576 | --from-file="${CRED_SECRET_KEY}"=<(echo "${CRED_SECRET_CONTENT}") \ |
| 2577 | -o=yaml \ |
| 2578 | --dry-run=client | \ |
| 2579 | prepend_folder_path \ |
| 2580 | "${PROVIDERCONFIG_NAME}/" | \ |
| 2581 | list2folder_cp_over \ |
| 2582 | "${TARGET_FOLDER}" |
| 2583 | } |
| 2584 | |
| 2585 | |
| 2586 | # Delete a `ProviderConfig` for a CrossPlane provider |
| 2587 | function delete_crossplane_providerconfig() { |
| 2588 | local PROVIDERCONFIG_NAME="$1" |
| 2589 | # As of today, one among `azure`, `aws` or `gcp`: |
| 2590 | local PROVIDER_TYPE="$2" |
| 2591 | ## `FLEET_REPO_DIR` is the result of: |
| 2592 | ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}" |
| 2593 | local FLEET_REPO_DIR="${3:-"${FLEET_REPO_DIR}"}" |
| 2594 | # Do not touch unless strictly needed |
| 2595 | local OSM_PROJECT_NAME="${4:-"osm_admin"}" |
| 2596 | local MGMT_CLUSTER_NAME="${5:-"_management"}" |
| 2597 | |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 2598 | |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 2599 | # Is the provider type supported? |
| 2600 | local VALID_PROVIDERS=("aws" "azure" "gcp") |
| 2601 | PROVIDER_TYPE="${PROVIDER_TYPE,,}" |
| 2602 | [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${PROVIDER_TYPE}")) ]] && return 1 |
| 2603 | |
| 2604 | # Determines the target folder in Fleet |
| 2605 | local PROVIDERCONFIG_FOLDER="${FLEET_REPO_DIR}/${OSM_PROJECT_NAME}/infra-config-profiles/${MGMT_CLUSTER_NAME}/crossplane-providerconfigs/${PROVIDER_TYPE}/${PROVIDERCONFIG_NAME}" |
| 2606 | |
| 2607 | # Delete the folder |
| 2608 | rm -rf "${PROVIDERCONFIG_FOLDER}" |
| 2609 | } |
| 2610 | |
| 2611 | |
| 2612 | # Update a `ProviderConfig` for a CrossPlane provider |
| 2613 | function update_crossplane_providerconfig() { |
| 2614 | local PROVIDERCONFIG_NAME="$1" |
| 2615 | # As of today, one among `azure`, `aws` or `gcp`: |
| 2616 | local PROVIDER_TYPE="$2" |
| 2617 | local CRED_SECRET_NAME="$3" |
| 2618 | local CRED_SECRET_KEY="${4:-"creds"}" |
| 2619 | local CRED_SECRET_NS="${5:-"crossplane-system"}" |
| 2620 | # If empty, it assumes the secret already exists |
| 2621 | local CRED_SECRET_CONTENT="${6:-"${CRED_SECRET_CONTENT:-""}"}" |
| 2622 | local AGE_PUBLIC_KEY_MGMT="$7" |
| 2623 | ## `FLEET_REPO_DIR` is the result of: |
| 2624 | ## "{{inputs.parameters.fleet_mount_path}}/{{inputs.parameters.cloned_fleet_folder_name}}" |
| 2625 | local FLEET_REPO_DIR="${8:-"${FLEET_REPO_DIR}"}" |
| 2626 | ## `SW_CATALOGS_REPO_DIR` is the result of: |
| 2627 | ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}" |
| 2628 | local SW_CATALOGS_REPO_DIR="${9:-"${SW_CATALOGS_REPO_DIR}"}" |
| 2629 | # Only when applicable |
| 2630 | local TARGET_GCP_PROJECT="${10:-""}" |
| 2631 | # Do not touch unless strictly needed |
| 2632 | local BASE_TEMPLATES_PATH="${11:-"infra-configs/crossplane/providers"}" |
| 2633 | local OSM_PROJECT_NAME="${12:-"osm_admin"}" |
| 2634 | local MGMT_CLUSTER_NAME="${13:-"_management"}" |
| 2635 | |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 2636 | |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 2637 | # Is the provider type supported? |
| 2638 | local VALID_PROVIDERS=("aws" "azure" "gcp") |
| 2639 | PROVIDER_TYPE="${PROVIDER_TYPE,,}" |
| 2640 | [[ ! ($(echo ${VALID_PROVIDERS[@]} | grep -w "${PROVIDER_TYPE}")) ]] && return 1 |
| 2641 | |
| 2642 | # First, delete; then, re-create |
| 2643 | delete_crossplane_providerconfig \ |
| 2644 | "${PROVIDERCONFIG_NAME}" \ |
| 2645 | "${PROVIDER_TYPE}" \ |
| 2646 | "${FLEET_REPO_DIR}" \ |
| 2647 | "${OSM_PROJECT_NAME}" \ |
| 2648 | "${MGMT_CLUSTER_NAME}" |
| 2649 | |
| 2650 | create_crossplane_providerconfig \ |
| 2651 | "${PROVIDERCONFIG_NAME}" \ |
| 2652 | "${PROVIDER_TYPE}" \ |
| 2653 | "${CRED_SECRET_NAME}" \ |
| 2654 | "${CRED_SECRET_KEY}" \ |
| 2655 | "${CRED_SECRET_NS}" \ |
| 2656 | "${CRED_SECRET_CONTENT}" \ |
| 2657 | "${AGE_PUBLIC_KEY_MGMT}" \ |
| 2658 | "${FLEET_REPO_DIR}" \ |
| 2659 | "${SW_CATALOGS_REPO_DIR}" \ |
| 2660 | "${TARGET_GCP_PROJECT}" \ |
| 2661 | "${BASE_TEMPLATES_PATH}" \ |
| 2662 | "${OSM_PROJECT_NAME}" \ |
| 2663 | "${MGMT_CLUSTER_NAME}" |
| 2664 | } |
| 2665 | |
| 2666 | |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 2667 | # Create a CloudConfig for CAPI provider |
| 2668 | function create_capi_openstack_cloudconf() { |
| 2669 | local OPENSTACK_CLOUD_NAME="${1}" |
| 2670 | local PUBLIC_KEY="${2:-"${PUBLIC_KEY_MGMT}"}" |
| 2671 | local CONFIG_DIR="${3:-"${MGMT_ADDON_CONFIG_DIR}"}" |
| 2672 | |
| 2673 | local NAMESPACE="managed-resources" |
| 2674 | |
| 2675 | local CLOUDS_YAML="${OPENSTACK_CLOUDS_YAML}" |
| 2676 | local CACERT="${OPENSTACK_CACERT}" |
| 2677 | |
| 2678 | local CLOUD_CREDENTIALS_SECRET_NAME="${OPENSTACK_CLOUD_NAME}-capo-config" |
| 2679 | local CLOUD_CREDENTIALS_CLOUDS_KEY="clouds.yaml" |
| 2680 | local CLOUD_CREDENTIALS_CACERT_KEY="cacert" |
| 2681 | local CLOUD_CREDENTIALS_FILENAME="credentials-secret.yaml" |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 2682 | |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 2683 | local CLOUD_CREDENTIALS_TOML_SECRET_NAME="${OPENSTACK_CLOUD_NAME}-capo-config-toml" |
| 2684 | local CLOUD_CREDENTIALS_TOML_FILENAME="credentials-toml-secret.yaml" |
| 2685 | |
| 2686 | local TARGET_FOLDER="${CONFIG_DIR}/capi-providerconfigs/capo/${OPENSTACK_CLOUD_NAME}-config" |
| 2687 | mkdir -p "${TARGET_FOLDER}" |
| 2688 | |
| 2689 | echo "" | \ |
| 2690 | make_generator \ |
| 2691 | "${CLOUD_CREDENTIALS_FILENAME}" \ |
| 2692 | kubectl_encrypt \ |
| 2693 | "${PUBLIC_KEY}" \ |
| 2694 | create \ |
| 2695 | secret \ |
| 2696 | generic \ |
| 2697 | "${CLOUD_CREDENTIALS_SECRET_NAME}" \ |
| 2698 | --namespace="${NAMESPACE}" \ |
| 2699 | --from-file="${CLOUD_CREDENTIALS_CLOUDS_KEY}"=<(echo "${CLOUDS_YAML}") \ |
| 2700 | --from-file="${CLOUD_CREDENTIALS_CACERT_KEY}"=<(echo "${CACERT}") \ |
| 2701 | -o=yaml \ |
| 2702 | --dry-run=client | \ |
| 2703 | make_generator \ |
| 2704 | "${CLOUD_CREDENTIALS_TOML_FILENAME}" \ |
| 2705 | kubectl_encrypt \ |
| 2706 | "${PUBLIC_KEY}" \ |
| 2707 | create \ |
| 2708 | secret \ |
| 2709 | generic \ |
| 2710 | "${CLOUD_CREDENTIALS_TOML_SECRET_NAME}" \ |
| 2711 | --namespace="${NAMESPACE}" \ |
| 2712 | --from-file="os_auth_url"=<(echo "${OS_AUTH_URL}") \ |
| 2713 | --from-file="os_region_name"=<(echo "${OS_REGION_NAME}") \ |
| 2714 | --from-file="os_username"=<(echo "${OS_USERNAME}") \ |
| 2715 | --from-file="os_password"=<(echo "${OS_PASSWORD}") \ |
| 2716 | --from-file="os_project_id"=<(echo "${OS_PROJECT_ID}") \ |
| 2717 | --from-file="os_project_domain_id"=<(echo "${OS_PROJECT_DOMAIN_ID}") \ |
| 2718 | -o=yaml \ |
| 2719 | --dry-run=client | \ |
| 2720 | list2folder_cp_over \ |
| 2721 | "${TARGET_FOLDER}" |
| 2722 | } |
| 2723 | |
| 2724 | # Update a CloudConfig for CAPI provider |
| 2725 | function update_capi_openstack_cloudconf() { |
| 2726 | local CLOUD_CONFIG_NAME="${1}" |
| 2727 | local PUBLIC_KEY="${2:-"${PUBLIC_KEY_MGMT}"}" |
| 2728 | local CONFIG_DIR="${3:-"${MGMT_ADDON_CONFIG_DIR}"}" |
| 2729 | |
| 2730 | delete_capi_openstack_cloudconf \ |
| 2731 | "${CLOUD_CONFIG_NAME}" \ |
| 2732 | "${CONFIG_DIR}" |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 2733 | |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 2734 | create_capi_openstack_cloudconf \ |
| 2735 | "${CLOUD_CONFIG_NAME}" \ |
| 2736 | "${PUBLIC_KEY}" \ |
| 2737 | "${CONFIG_DIR}" |
| 2738 | } |
| 2739 | |
| 2740 | |
| 2741 | # Delete a CloudConfig for CAPI provider |
| 2742 | function delete_capi_openstack_cloudconf() { |
| 2743 | local OPENSTACK_CLOUD_NAME="$1" |
| 2744 | local CONFIG_DIR="${2:-"${MGMT_ADDON_CONFIG_DIR}"}" |
| 2745 | |
| 2746 | local TARGET_FOLDER="${CONFIG_DIR}/capi-providerconfigs/capo/${OPENSTACK_CLOUD_NAME}-config" |
| garciadeblas | b476945 | 2025-08-06 18:08:18 +0200 | [diff] [blame] | 2747 | |
| garciadeblas | 8a28f6d | 2025-06-11 11:11:56 +0200 | [diff] [blame] | 2748 | # Delete the encrypted secrets files. |
| 2749 | rm -rf "${TARGET_FOLDER}" |
| 2750 | } |
| 2751 | |
| garciadeblas | 70461c5 | 2024-07-03 09:17:56 +0200 | [diff] [blame] | 2752 | # Helper function to return the relative path of a location in SW Catalogs for an OKA |
| 2753 | function path_to_catalog() { |
| 2754 | local OKA_TYPE="$1" |
| 2755 | local PROJECT_NAME="${2:-"osm_admin"}" |
| 2756 | |
| 2757 | # Corrects `osm_admin` project, since it uses the root folder |
| 2758 | PROJECT_NAME="${PROJECT_NAME}" |
| 2759 | [[ "${PROJECT_NAME}" == "osm_admin" ]] && PROJECT_NAME="." |
| 2760 | |
| 2761 | # Echoes the relate path from the SW-Catalogs root |
| 2762 | case "${OKA_TYPE,,}" in |
| 2763 | |
| 2764 | "controller" | "infra-controller" | "infra-controllers" | "infra_controller" | "infra_controllers") |
| 2765 | echo -n "${PROJECT_NAME}/infra-controllers" |
| 2766 | return 0 |
| 2767 | ;; |
| 2768 | |
| 2769 | "config" | "infra-config" | "infra-configs" | "infra_config" | "infra_configs") |
| 2770 | echo -n "${PROJECT_NAME}/infra-configs" |
| 2771 | return 0 |
| 2772 | ;; |
| 2773 | |
| 2774 | "managed" | "resources" | "managed-resources" | "managed_resources" | "cloud-resources" | "cloud_resources") |
| 2775 | echo -n "${PROJECT_NAME}/cloud-resources" |
| 2776 | return 0 |
| 2777 | ;; |
| 2778 | |
| 2779 | "app" |"apps" | "applications" | "cnf" | "cnfs" | "nf" | "nfs") |
| 2780 | echo -n "${PROJECT_NAME}/apps" |
| 2781 | return 0 |
| 2782 | ;; |
| 2783 | |
| 2784 | *) |
| 2785 | echo -n "------------ ERROR ------------" |
| 2786 | return 1 |
| 2787 | ;; |
| 2788 | esac |
| 2789 | } |
| 2790 | |
| 2791 | |
| 2792 | # Create OKA of a specific kind |
| 2793 | function create_oka() { |
| 2794 | local OKA_NAME="$1" |
| 2795 | local OKA_TYPE="$2" |
| 2796 | local PROJECT_NAME="${3:-"."}" |
| 2797 | ## `SW_CATALOGS_REPO_DIR` is the result of: |
| 2798 | ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}" |
| 2799 | local SW_CATALOGS_REPO_DIR="$4" |
| 2800 | local OKA_LOCATION="${5:-"."}" |
| 2801 | local TARBALL_FILE="${6:-"true"}" |
| 2802 | |
| 2803 | |
| 2804 | # Finds the corresponding catalog path from the SW-Catalogs root |
| 2805 | # and create the destination |
| 2806 | local CATALOG_PATH=$(\ |
| 2807 | path_to_catalog \ |
| 2808 | "${OKA_TYPE}" \ |
| 2809 | "${PROJECT_NAME}" |
| 2810 | ) |
| 2811 | local DESTINATION="${SW_CATALOGS_REPO_DIR}/${CATALOG_PATH}/${OKA_NAME}" |
| 2812 | mkdir -p "${DESTINATION}" |
| 2813 | |
| 2814 | # When the OKA comes as a `tar.gz` |
| 2815 | if [[ "${TARBALL_FILE,,}" == "true" ]]; |
| 2816 | then |
| 2817 | tar xvfz "${OKA_LOCATION}/${OKA_NAME}.tar.gz" -C "${DESTINATION}" |
| 2818 | else |
| 2819 | # Otherwise it must be a folder structure |
| 2820 | cp -var "${OKA_LOCATION}/${OKA_NAME}/*" "${DESTINATION}/" |
| 2821 | fi |
| 2822 | } |
| 2823 | |
| 2824 | |
| 2825 | # Delete OKA of a specific kind |
| 2826 | function delete_oka() { |
| 2827 | local OKA_NAME="$1" |
| 2828 | local OKA_TYPE="$2" |
| 2829 | local PROJECT_NAME="${3:-"."}" |
| 2830 | ## `SW_CATALOGS_REPO_DIR` is the result of: |
| 2831 | ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}" |
| 2832 | local SW_CATALOGS_REPO_DIR="$4" |
| 2833 | |
| 2834 | |
| 2835 | # Finds the corresponding catalog path from the SW-Catalogs root |
| 2836 | # and determine the destination |
| 2837 | local CATALOG_PATH=$(\ |
| 2838 | path_to_catalog \ |
| 2839 | "${OKA_TYPE}" \ |
| 2840 | "${PROJECT_NAME}" |
| 2841 | ) |
| 2842 | local DESTINATION="${SW_CATALOGS_REPO_DIR}/${CATALOG_PATH}/${OKA_NAME}" |
| 2843 | |
| 2844 | # Remove the folder |
| 2845 | rm -rf "${DESTINATION}" |
| 2846 | } |
| 2847 | |
| 2848 | |
| 2849 | # Update OKA of a specific kind |
| 2850 | function update_oka() { |
| 2851 | local OKA_NAME="$1" |
| 2852 | local OKA_TYPE="$2" |
| 2853 | local PROJECT_NAME="${3:-"."}" |
| 2854 | ## `SW_CATALOGS_REPO_DIR` is the result of: |
| 2855 | ## "{{inputs.parameters.sw_catalogs_mount_path}}/{{inputs.parameters.cloned_sw_catalogs_folder_name}}" |
| 2856 | local SW_CATALOGS_REPO_DIR="$4" |
| 2857 | local OKA_LOCATION="${5:-"."}" |
| 2858 | local TARBALL_FILE="${6:-"true"}" |
| 2859 | |
| 2860 | |
| 2861 | # Finds the corresponding catalog path from the SW-Catalogs root |
| 2862 | # and determine the destination |
| 2863 | local CATALOG_PATH=$(\ |
| 2864 | path_to_catalog \ |
| 2865 | "${OKA_TYPE}" \ |
| 2866 | "${PROJECT_NAME}" |
| 2867 | ) |
| 2868 | local DESTINATION="${SW_CATALOGS_REPO_DIR}/${CATALOG_PATH}/${OKA_NAME}" |
| 2869 | |
| 2870 | # Remove and re-create |
| 2871 | rm -rf "${DESTINATION}" |
| 2872 | create_oka \ |
| 2873 | "${OKA_NAME}" \ |
| 2874 | "${OKA_TYPE}" \ |
| 2875 | "${PROJECT_NAME}" \ |
| 2876 | "${SW_CATALOGS_REPO_DIR}" \ |
| 2877 | "${OKA_LOCATION}" \ |
| 2878 | "${TARBALL_FILE}" |
| 2879 | } |