Skip to content

Commit

Permalink
Add script and Makefile targets for bundling images for airgap (#475)
Browse files Browse the repository at this point in the history
  • Loading branch information
squizzi authored Nov 7, 2024
1 parent e1432fe commit 70419b1
Show file tree
Hide file tree
Showing 8 changed files with 520 additions and 7 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ vendor

# mkdocs folder
mkdocs

# airgap-push script directories
hmc-airgap
25 changes: 20 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)/$*))

Expand All @@ -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)
Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions docs/dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
196 changes: 196 additions & 0 deletions scripts/airgap-push.sh
Original file line number Diff line number Diff line change
@@ -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}
Loading

0 comments on commit 70419b1

Please sign in to comment.