From 338101353a1f0e7eb84a99ff27890475f15ce6df Mon Sep 17 00:00:00 2001 From: Kyle Squizzato Date: Thu, 3 Oct 2024 10:35:51 -0700 Subject: [PATCH] Add script and Makefile targets for bundling images for airgap * Pull charts referenced by providers k0s.extensions during helm-package In order to support airgap customers will need to place all of the charts a ManagedCluster may pull upon into a registry. This adds support to our 'helm-package' Makefile to ensure we package up all of the charts our templates reference so that they can be included in an airgap bundle. * Place k0s.extensions images in a seperate image tarball For the first iteration of airgap we're using k0s airgap bundle for the management cluster but the ManagedClusters we deploy will still need the extensions images that the extension charts reference. This patch modifies the bundle-images target so it creates two bundles, one that will serve as the management cluster bundle and one that will serve as the ManagedCluster bundle. * Add airgap-push script This script can be used to push images and Helm charts needed for an airgapped ManagedCluster deployment to a given image registry and chart repository. * Package the airgap-push script with the bundle Signed-off-by: Kyle Squizzato --- .gitignore | 3 + Makefile | 25 +- docs/dev.md | 12 + scripts/airgap-push.sh | 196 ++++++++++++++ scripts/bundle-images.sh | 247 ++++++++++++++++++ scripts/package-k0s-extensions-helm.sh | 40 +++ .../templates/k0smotroncontrolplane.yaml | 2 +- .../templates/k0scontrolplane.yaml | 2 +- 8 files changed, 520 insertions(+), 7 deletions(-) create mode 100755 scripts/airgap-push.sh create mode 100755 scripts/bundle-images.sh create mode 100644 scripts/package-k0s-extensions-helm.sh diff --git a/.gitignore b/.gitignore index be8549cc..55aeadb6 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,6 @@ vendor # mkdocs folder mkdocs + +# airgap-push script directories +hmc-airgap diff --git a/Makefile b/Makefile index cfeaaeed..229a4caa 100644 --- a/Makefile +++ b/Makefile @@ -129,21 +129,35 @@ lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes add-license: addlicense $(ADDLICENSE) -c "" -ignore ".github/**" -ignore "config/**" -ignore "templates/**" -ignore ".*" -y 2024 . -##@ Build +##@ Package TEMPLATES_DIR := templates PROVIDER_TEMPLATES_DIR := $(TEMPLATES_DIR)/provider CHARTS_PACKAGE_DIR ?= $(LOCALBIN)/charts +EXTENSION_CHARTS_PACKAGE_DIR ?= $(LOCALBIN)/charts/extensions +$(EXTENSION_CHARTS_PACKAGE_DIR): | $(LOCALBIN) + mkdir -p $(EXTENSION_CHARTS_PACKAGE_DIR) $(CHARTS_PACKAGE_DIR): | $(LOCALBIN) rm -rf $(CHARTS_PACKAGE_DIR) mkdir -p $(CHARTS_PACKAGE_DIR) +IMAGES_PACKAGE_DIR ?= $(LOCALBIN)/images +$(IMAGES_PACKAGE_DIR): | $(LOCALBIN) + rm -rf $(IMAGES_PACKAGE_DIR) + mkdir -p $(IMAGES_PACKAGE_DIR) TEMPLATE_FOLDERS = $(patsubst $(TEMPLATES_DIR)/%,%,$(wildcard $(TEMPLATES_DIR)/*)) .PHONY: helm-package -helm-package: $(CHARTS_PACKAGE_DIR) helm +helm-package: $(CHARTS_PACKAGE_DIR) $(EXTENSION_CHARTS_PACKAGE_DIR) helm @make $(patsubst %,package-%-tmpl,$(TEMPLATE_FOLDERS)) +bundle-images: dev-apply $(IMAGES_PACKAGE_DIR) ## Create a tarball with all images used by HMC. + @BUNDLE_TARBALL=$(IMAGES_PACKAGE_DIR)/hmc-images-$(VERSION).tgz EXTENSIONS_BUNDLE_TARBALL=$(IMAGES_PACKAGE_DIR)/hmc-extension-images-$(VERSION).tgz IMG=$(IMG) KUBECTL=$(KUBECTL) YQ=$(YQ) HELM=$(HELM) NAMESPACE=$(NAMESPACE) TEMPLATES_DIR=$(TEMPLATES_DIR) KIND_CLUSTER_NAME=$(KIND_CLUSTER_NAME) $(SHELL) "scripts/bundle-images.sh" + +airgap-package: bundle-images ## Create a tarball with all images and Helm charts used by HMC, useful for deploying in air-gapped environments. + @TEMPLATES_DIR=$(TEMPLATES_DIR) EXTENSION_CHARTS_PACKAGE_DIR=$(EXTENSION_CHARTS_PACKAGE_DIR) HELM=$(HELM) YQ=$(YQ) $(SHELL) "scripts/package-k0s-extensions-helm.sh" + cd $(LOCALBIN) && mkdir -p scripts && cp ../scripts/airgap-push.sh scripts/airgap-push.sh && tar -czf hmc-airgap-$(VERSION).tgz scripts/airgap-push.sh $(shell basename $(CHARTS_PACKAGE_DIR)) $(shell basename $(IMAGES_PACKAGE_DIR)) + package-%-tmpl: @make TEMPLATES_SUBDIR=$(TEMPLATES_DIR)/$* $(patsubst %,package-chart-%,$(shell ls $(TEMPLATES_DIR)/$*)) @@ -154,6 +168,8 @@ lint-chart-%: package-chart-%: lint-chart-% $(HELM) package --destination $(CHARTS_PACKAGE_DIR) $(TEMPLATES_SUBDIR)/$* +##@ Build + LD_FLAGS?= -s -w LD_FLAGS += -X github.com/Mirantis/hmc/internal/build.Version=$(VERSION) LD_FLAGS += -X github.com/Mirantis/hmc/internal/telemetry.segmentToken=$(SEGMENT_TOKEN) @@ -349,11 +365,11 @@ dev-creds-apply: dev-$(DEV_PROVIDER)-creds .PHONY: dev-aws-nuke dev-aws-nuke: envsubst awscli yq cloud-nuke ## Warning: Destructive! Nuke all AWS resources deployed by 'DEV_PROVIDER=aws dev-mcluster-apply' - @CLUSTER_NAME=$(CLUSTER_NAME) YQ=$(YQ) AWSCLI=$(AWSCLI) bash -c "./scripts/aws-nuke-ccm.sh elb" + @CLUSTER_NAME=$(CLUSTER_NAME) YQ=$(YQ) AWSCLI=$(AWSCLI) $(SHELL) "./scripts/aws-nuke-ccm.sh elb" @CLUSTER_NAME=$(CLUSTER_NAME) $(ENVSUBST) < config/dev/aws-cloud-nuke.yaml.tpl > config/dev/aws-cloud-nuke.yaml DISABLE_TELEMETRY=true $(CLOUDNUKE) aws --region $$AWS_REGION --force --config config/dev/aws-cloud-nuke.yaml --resource-type vpc,eip,nat-gateway,ec2,ec2-subnet,elb,elbv2,ebs,internet-gateway,network-interface,security-group @rm config/dev/aws-cloud-nuke.yaml - @CLUSTER_NAME=$(CLUSTER_NAME) YQ=$(YQ) AWSCLI=$(AWSCLI) bash -c "./scripts/aws-nuke-ccm.sh ebs" + @CLUSTER_NAME=$(CLUSTER_NAME) YQ=$(YQ) AWSCLI=$(AWSCLI) $(SHELL) "./scripts/aws-nuke-ccm.sh ebs" .PHONY: dev-azure-nuke dev-azure-nuke: envsubst azure-nuke ## Warning: Destructive! Nuke all Azure resources deployed by 'DEV_PROVIDER=azure dev-mcluster-apply' @@ -529,7 +545,6 @@ $(AWSCLI): | $(LOCALBIN) exit 1; \ fi; \ - # go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist # $1 - target path with name of binary (ideally with version) # $2 - package url which can be installed diff --git a/docs/dev.md b/docs/dev.md index 7b16c91e..f3456de0 100644 --- a/docs/dev.md +++ b/docs/dev.md @@ -228,3 +228,15 @@ objects). For example: CSI expects single Secret with configuration in `ini` format ([documented here](https://docs.vmware.com/en/VMware-vSphere-Container-Storage-Plug-in/2.0/vmware-vsphere-csp-getting-started/GUID-BFF39F1D-F70A-4360-ABC9-85BDAFBE8864.html)). Options are similar to CCM and same defaults/considerations are applicable. + +## Generating the airgap bundle +Use the `make airgap-package` target to manually generate the airgap bundle, +to ensure the correctly tagged HMC controller image is present in the bundle +prefix the `IMG` env var with the desired image, for example: + +```bash +IMG="ghcr.io/mirantis/hmc:0.0.3" make airgap-package +``` + +Not setting an `IMG` var will use the default image name/tag generated by the +Makefile. diff --git a/scripts/airgap-push.sh b/scripts/airgap-push.sh new file mode 100755 index 00000000..0f894c36 --- /dev/null +++ b/scripts/airgap-push.sh @@ -0,0 +1,196 @@ +#!/bin/bash +# Copyright 2024 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# This script can be used to help users re-tag and push images and Helm charts +# into a private registry for use when deploying HMC ManagedClusters into an +# air-gapped environment. This script is packaged as part of the airgap bundle +# for convenience. + +REPO="" +CHART_REPO="" +AIRGAP_BUNDLE="" +HELP="" +EXTENSION_TARBALL_PREFIX="hmc-extension-images" +WORK_DIR="$(pwd)/hmc-airgap" + +# Print the help message +function print_help() { + echo "Usage:" + echo " airgap-push.sh [OPTIONS]" + echo "Ensure repositories are logged into via 'helm' and 'docker' before running this script." + echo "Options:" + echo " -h, --help" + echo " Print this help message" + echo " -r, --image-repo (required)" + echo " The image repo to push the images to" + echo " -c, --chart-repo (required)" + echo " The repository to push the Helm charts to, for OCI prefix use oci://" + echo " -i, --insecure-registry" + echo " Use insecure registry for pushing Helm charts" + echo " -a, --airgap-bundle (required)" + echo " The path to the airgap bundle" +} + +function ctrl_c() { + echo "Caught CTRL-C, exiting..." + exit 1 +} + +trap ctrl_c INT + +# Parse the options +while [[ $# -gt 0 ]]; do + key="$1" + case $key in + -h|--help) + HELP="true" + shift + ;; + -r|--image-repo) + REPO="$2" + shift + shift + ;; + -c|--chart-repo) + CHART_REPO="$2" + shift + shift + ;; + -i|--insecure-registry) + INSECURE_REGISTRY="true" + shift + ;; + -a|--airgap-bundle) + AIRGAP_BUNDLE="$2" + shift + shift + ;; + *) + echo "Unknown option: $1" + print_help + exit 1 + ;; + esac +done + + +if [ ! -z "$HELP" ]; then + print_help + exit 0 +fi + +if [ -z "$REPO" ]; then + echo "The repository must be set" + print_help + exit 1 +fi + +if [ -z "$CHART_REPO" ]; then + echo "The chart repository must be set" + print_help + exit 1 +fi + +if [ -z "$AIRGAP_BUNDLE" ]; then + echo "The airgap bundle must be set" + exit 1 +else + # Validate the airgap bundle + if [ ! -f "$AIRGAP_BUNDLE" ]; then + echo "The provided airgap bundle: ${AIRGAP_BUNDLE} does not exist" + exit 1 + fi +fi + +if [ ! $(command -v jq) ]; then + echo "'jq' could not be found, install 'jq' to continue" + exit 1 +fi + +mkdir -p ${WORK_DIR} + +# Extract extension images from the airgap bundle. +echo "Extracting extension images from airgap bundle: ${AIRGAP_BUNDLE}..." +extension_tarball_name=$(tar tf ${AIRGAP_BUNDLE} | grep "${EXTENSION_TARBALL_PREFIX}") +tar -C ${WORK_DIR} -xf ${AIRGAP_BUNDLE} ${extension_tarball_name} +if [ $? -ne 0 ]; then + echo "Failed to extract extension images from the airgap bundle" + exit 1 +fi + +# Load the extension images into the Docker daemon for re-tagging and pushing. +echo "Loading extension images into Docker..." +docker load -i ${WORK_DIR}/${extension_tarball_name} +if [ $? -ne 0 ]; then + echo "Failed to load extension images into Docker" + exit 1 +fi + + +# Extract the repositories json file from the extensions bundle. +echo "Retagging and pushing extension images to ${REPO}..." +tar -C ${WORK_DIR} -xf ${WORK_DIR}/${extension_tarball_name} "repositories" +for image in $(cat ${WORK_DIR}/repositories | jq -r 'to_entries[] | .key'); do + image_name=$(echo ${image} | grep -o '[^/]*$') + + # docker images -a may return multiple images with the same name but + # different tags. We need to retag and push all of them. + for old_image in $(docker images -a | grep ${image} | awk '{print $1":"$2}'); do + tag=${old_image#*:} + new_image="${REPO}/${image_name}:${tag}" + + echo "Retagging image: ${old_image} with ${new_image}..." + + docker tag ${old_image} ${new_image} + if [ $? -ne 0 ]; then + echo "Failed to retag image: ${old_image} with ${new_image}" + exit 1 + fi + + echo "Pushing image: ${new_image}..." + + docker push ${new_image} + if [ $? -ne 0 ]; then + echo "Failed to push image: ${new_image}" + exit 1 + fi + done +done + +# Extract all of the Helm charts from the airgap bundle. +echo "Extracting Helm charts from airgap bundle: ${AIRGAP_BUNDLE}..." +tar -C ${WORK_DIR} -xf ${AIRGAP_BUNDLE} "charts" +if [ $? -ne 0 ]; then + echo "Failed to extract Helm charts from the airgap bundle" + exit 1 +fi + +# Next, use Helm to push the charts to the given chart repository. +echo "Pushing Helm charts to ${CHART_REPO}..." +if [ ! -z "$INSECURE_REGISTRY" ]; then + insecure_registry_flag="--insecure-skip-tls-verify" +fi + +for chart in $(find ${WORK_DIR}/charts -name "*.tgz"); do + helm push ${insecure_registry_flag} ${chart} ${CHART_REPO} + if [ $? -ne 0 ]; then + echo "Failed to push Helm chart: ${chart}" + exit 1 + fi +done + +# Clean up any extracted files. +echo "Cleaning up..." +rm -rf ${WORK_DIR} diff --git a/scripts/bundle-images.sh b/scripts/bundle-images.sh new file mode 100755 index 00000000..d56ff951 --- /dev/null +++ b/scripts/bundle-images.sh @@ -0,0 +1,247 @@ +#!/bin/bash +# Copyright 2024 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# bundle-images.sh bundles all of the images used across HMC into a single +# tarball. This is useful for deploying HMC into air-gapped environments. +# It is recommended to use this script in conjunction with the Makefile target +# `make bundle-images` which will perform some additional steps outside of +# this scripts functionality. +# Usage: make bundle-images +# This script should not be run directly. Use 'make bundle-images' instead. +LABEL_KEY="cluster.x-k8s.io/provider" +IMAGES_BUNDLED="$IMG" +EXTENSION_IMAGES_BUNDLED="" + +echo -e "Bundling images for HMC, this may take awhile...\n" + +trap ctrl_c INT + +function wait_for_deploy_exist() { + local deployment_label=$1 + local max_wait_secs=300 + local interval_secs=5 + local start_time + + start_time=$(date +%s) + + echo "Waiting up to ${max_wait_secs}s for provider Deployment with label: \"${deployment_label}\" to exist in namespace: \"${NAMESPACE}\"..." + + while true; do + current_time=$(date +%s) + if (( (current_time - start_time) > max_wait_secs )); then + echo "Error: Waited for Deployment with label: \"${deployment_label}\" in namespace: \"${NAMESPACE}\" to exist for ${max_wait_secs} seconds and it still does not exist." + return 1 + fi + + output=$(${KUBECTL} -n "${NAMESPACE}" get deploy -l ${deployment_label}) + + if [[ $output != "" ]]; then + echo "Deployment in namespace: \"${NAMESPACE}\" with label: \"${deployment_label}\" exists." + break + else + echo "Deployment with label: \"${deployment_label}\" in namespace: \"${NAMESPACE}\" does not exist yet. Waiting ${interval_secs} seconds..." + sleep $interval_secs + fi + done +} + +function bundle_images() { + local images=$1 + local tarball=$2 + + echo "Bundling images into ${tarball}..." + docker save -o ${tarball} ${images} + if [[ $? -ne 0 ]]; then + echo "Error: Failed to bundle images into ${tarball}" + exit 1 + fi +} + + +function ctrl_c() { + echo "Caught CTRL-C, exiting..." + exit 1 +} + +echo -e "\nVerifying provider Deployments are ready...\n" + +# Verify each provider we support has deployed so we can get the images used +# across the deployments. +for template in $(find ${TEMPLATES_DIR} -name 'provider.yaml'); +do + result=$(grep 'kind: .*Provider' ${template}) + provider_yaml=$(grep "${result}" -A2 ${template}) + provider_kind=$(echo -e "${provider_yaml}" | $YQ e '.kind' -) + provider_name=$(echo -e "${provider_yaml}" | $YQ e '.metadata.name' -) + provider_kind_tolower=$(echo ${provider_kind} | tr '[:upper:]' '[:lower:]') + + if [[ $provider_name == "" ]]; then + echo "Error: Cannot determine provider Name from ${template}" + exit 1 + fi + + if [[ $provider_kind_tolower == "" ]]; then + echo "Error: Cannot determine provider Kind from ${template}" + exit 1 + fi + + # controlplane is a special case which needs a hyphen. + if [[ $provider_kind_tolower == "controlplane" ]]; then + provider_kind_tolower="control-plane" + fi + + # coreprovider does not have a provider prefix. + if [[ $provider_kind_tolower == "coreprovider" ]]; then + label_value=$(echo ${provider_name}) + else + label_value=$(echo $(echo ${provider_kind_tolower} | sed -e 's/provider//g')-${provider_name}) + fi + + wait_for_deploy_exist "$LABEL_KEY=$label_value" + + echo "Verifying Deployment(s) with ${LABEL_KEY}=${label_value} label is condition=available..." + + ${KUBECTL} wait --for condition=available --timeout=10m deploy -l ${LABEL_KEY}=${label_value} -n ${NAMESPACE} + if [[ $? -ne 0 ]]; then + echo "Error: Timed out waiting for available Deployment with ${LABEL_KEY}=${label_value} label" + exit 1 + fi +done + + +# Now that we know everything is deployed and ready, we can get all of images by +# execing into the KIND cluster. +control_plane=$(${KUBECTL} get nodes --no-headers -o custom-columns=":metadata.name") +if [[ $? -ne 0 ]] || [[ $control_plane == "" ]]; then + echo "Error: Cannot get control plane node" + exit 1 +fi + +echo -e "\nPulling images for HMC components...\n" + +for image in $(docker exec -it ${control_plane} crictl images | sed 1,1d | awk '{print $1":"$2}' | grep -v 'kindest'); +do + if [[ $image == "" ]]; then + echo "Error: Failed to get image from KIND cluster, image string should not be empty" + exit 1 + fi + + if [[ $image == *"hmc"* ]]; then + # Don't try to pull the controller image. + continue + fi + + tag=${image#*:} + if [[ $tag == "" ]]; then + echo "Will not pull image: ${image} with tag , continuing..." + continue + fi + + docker pull $image + if [[ $? -ne 0 ]]; then + echo "Error: Failed to pull ${image}" + exit 1 + fi + + IMAGES_BUNDLED="$IMAGES_BUNDLED $image" +done + +echo -e "\nPulling images for HMC extensions...\n" + +# Next, we need to build a list of images used by k0s extensions. Walk the +# templates directory and extract the images used by the extensions. +for template in $(find ${templates_dir} -name 'k0s*.yaml'); +do + if [[ $template == *"k0smotron"* ]]; then + extensions_path=".spec.k0sConfig.spec.extensions.helm" + else + extensions_path=".spec.k0sConfigSpec.k0s.spec.extensions.helm" + fi + + repos=$(grep -vw "{{" ${template} | $YQ e "${extensions_path}.repositories[] | [.url, .name] | join(\";\")") + for repo in $repos + do + url=${repo%;*} + chartname=${repo#*;} + version=$(grep -vw "{{" ${template} | + $YQ e "${extensions_path}.charts[] | select(.chartname == \"*${chartname}*\") | .version") + name=$(grep -vw "{{" ${template} | + $YQ e "${extensions_path}.charts[] | select(.chartname == \"*${chartname}*\") | .name") + grep -vw "{{" $template | $YQ e "${extensions_path}.charts[] | select(.chartname == \"*$chartname*\") | .values" > ${name}-values.yaml + + if [[ $url == "" ]] || [[ $name == "" ]] || [[ $version == "" ]]; then + echo "Error: Failed to get URL, name, or version from ${template}" + exit 1 + fi + + # Use 'helm template' to get the images used by the extension. + if [[ $name == "kube-vip" ]]; then + # FIXME: This is a temporary workaround for kube-vip, if we use + # a custom image tag in the future we'll need to update this. + # kube-vip is a special case where our yaml values result in invalid + # Helm template output, for now render the YAML without values. + template_output=$(${HELM} template --repo ${url} --version ${version} ${name}) + else + template_output=$(${HELM} template --repo ${url} --version ${version} ${name} --values ${name}-values.yaml) + if [[ $? -ne 0 ]]; then + echo "Error: Failed to get images from Helm template for ${name}, trying to output values with debug..." + + template_output=$(${HELM} template --repo ${url} --version ${version} ${name} --values ${name}-values.yaml --debug) + if [[ $? -ne 0 ]]; then + echo "Error: Failed to get images from Helm template for ${name} with debug output" + exit 1 + fi + fi + fi + + for image in $(${HELM} template --repo ${url} --version ${version} ${name} --values ${name}-values.yaml | $YQ -N e .spec.template.spec.containers[].image); + do + docker pull ${image} + if [[ $? -ne 0 ]]; then + echo "Error: Failed to pull ${image}" + exit 1 + fi + + EXTENSION_IMAGES_BUNDLED="$EXTENSION_IMAGES_BUNDLED $image" + done + + rm $name-values.yaml + done +done + +echo -e "\nSaving images...\n" +images_bundled_uniq=$(echo "${IMAGES_BUNDLED}" | tr ' ' '\n' | sort -u) +bundle_images "$images_bundled_uniq" $BUNDLE_TARBALL + +if [[ $EXTENSION_IMAGES_BUNDLED != "" ]]; then + extension_images_bundled_uniq=$(echo "${EXTENSION_IMAGES_BUNDLED}" | tr ' ' '\n' | sort -u) + bundle_images "$extension_images_bundled_uniq" $EXTENSIONS_BUNDLE_TARBALL +fi + +echo -e "\nCleaning up all pulled images...\n" +# Cleanup the images bundled by removing them from the local image cache. +all_images="$images_bundled_uniq $extension_images_bundled_uniq" +for image in $all_images; +do + echo "Removing ${image} from local image cache..." + docker rmi ${image} + if [ $? -ne 0 ]; then + # Note that we failed here but continue trying to remove the other + # images. + echo "Error: Failed to remove ${image} from local image cache" + fi +done + +echo "Done! Images bundled into ${BUNDLE_TARBALL} and ${EXTENSIONS_BUNDLE_TARBALL}" diff --git a/scripts/package-k0s-extensions-helm.sh b/scripts/package-k0s-extensions-helm.sh new file mode 100644 index 00000000..9a6149db --- /dev/null +++ b/scripts/package-k0s-extensions-helm.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# Copyright 2024 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# This script packages Helm charts affiliated with k0s extensions for airgap +# installations. +# This script should not be run directly. Use 'make airgap-package' instead. +for template in $(find ${TEMPLATES_DIR} -name 'k0s*.yaml'); do + if [[ $template == *"k0smotron"* ]]; then + extensions_path=".spec.k0sConfig.spec.extensions.helm" + else + extensions_path=".spec.k0sConfigSpec.k0s.spec.extensions.helm" + fi + + repos=$(grep -vw "{{" ${template} | ${YQ} e "${extensions_path}.repositories[] | [.url, .name] | join(\";\")") + for repo in $repos; do + url=${repo%;*} + chartname=${repo#*;} + version=$(grep -vw "{{" ${template} | $YQ e "${extensions_path}.charts[] | select(.chartname == \"*${chartname}*\") | .version") + name=$(grep -vw "{{" ${template} | $YQ e "${extensions_path}.charts[] | select(.chartname == \"*${chartname}*\") | .name") + if [[ $url == "" ]] || [[ $name == "" ]] || [[ $version == "" ]]; then + echo "Error: Cannot construct Helm pull command from url: $url, name: $name, version: $version: one or more vars is not populated" + exit 1 + fi + if [[ ! $(find ${EXTENSION_CHARTS_PACKAGE_DIR} -name ${name}-${version}*.tgz) ]]; then + echo "Pulling Helm chart ${name} from ${url} with version ${version}" + ${HELM} pull --repo ${url} --version ${version} ${name} -d ${EXTENSION_CHARTS_PACKAGE_DIR} + fi + done +done diff --git a/templates/cluster/vsphere-hosted-cp/templates/k0smotroncontrolplane.yaml b/templates/cluster/vsphere-hosted-cp/templates/k0smotroncontrolplane.yaml index 375afe4b..cd27b2ff 100644 --- a/templates/cluster/vsphere-hosted-cp/templates/k0smotroncontrolplane.yaml +++ b/templates/cluster/vsphere-hosted-cp/templates/k0smotroncontrolplane.yaml @@ -56,7 +56,7 @@ spec: - key: CriticalAddonsOnly effect: NoExecute operator: Exists - - name: vsphere-csi + - name: vsphere-csi-driver chartname: mirantis/vsphere-csi-driver version: 0.0.2 order: 2 diff --git a/templates/cluster/vsphere-standalone-cp/templates/k0scontrolplane.yaml b/templates/cluster/vsphere-standalone-cp/templates/k0scontrolplane.yaml index 0b18d181..bf6d178e 100644 --- a/templates/cluster/vsphere-standalone-cp/templates/k0scontrolplane.yaml +++ b/templates/cluster/vsphere-standalone-cp/templates/k0scontrolplane.yaml @@ -90,7 +90,7 @@ spec: - key: CriticalAddonsOnly effect: NoExecute operator: Exists - - name: vsphere-csi + - name: vsphere-csi-driver chartname: mirantis/vsphere-csi-driver version: 0.0.2 order: 3