Reinstate utils image with clusterctl also installed

This commit is contained in:
Matt Pryor 2021-08-23 17:25:57 +01:00
parent 1d7503034c
commit f46e1fa09f
17 changed files with 206 additions and 63 deletions

View File

@ -0,0 +1,88 @@
name: Publish artifacts
# Run the tasks on every push
on: push
jobs:
build_push_utils_image:
name: Build and push utils image
runs-on: ubuntu-latest
steps:
- name: Check out the repository
uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
with:
platforms: all
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Set up Docker layer caching
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-api-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-api-
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Calculate metadata for image
id: image-meta
uses: docker/metadata-action@v3
with:
images: ghcr.io/stackhpc/k8s-utils
# Produce the branch name or tag and the SHA as tags
tags: |
type=ref,event=branch
type=ref,event=tag
type=sha,prefix=
- name: Build and push image
uses: docker/build-push-action@v2
with:
context: ./utils
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.image-meta.outputs.tags }}
labels: ${{ steps.image-meta.outputs.labels }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
# Temp fix
# https://github.com/docker/build-push-action/issues/252
# https://github.com/moby/buildkit/issues/1896
# https://github.com/docker/buildx/pull/535
- name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
publish_charts:
name: Publish Helm charts to GitHub pages
runs-on: ubuntu-latest
# We need the image to have been built (if required) and tagged before publishing charts
needs: build_push_utils_image
steps:
- name: Check out the repository
uses: actions/checkout@v2
with:
# This is important for "git rev-list --count" to work correctly when
# determining the number of commits since the last tag
fetch-depth: 0
# Use an up-to-date version of Python for the script
- uses: actions/setup-python@v2
with:
python-version: 3.9
# This action packages and pushes the chart
- name: Publish charts
run: ./scripts/publish-charts.py
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,25 +0,0 @@
name: Publish Helm charts
# Run the tasks on every push
on: push
jobs:
publish_charts:
name: Publish Helm charts to GitHub pages
runs-on: ubuntu-latest
steps:
- name: Check out the repository
uses: actions/checkout@v2
with:
# This is important for "git rev-list --count" to work correctly when
# determining the number of commits since the last tag
fetch-depth: 0
# Use an up-to-date version of Python for the script
- uses: actions/setup-python@v2
with:
python-version: 3.9
# This action packages and pushes the chart
- name: Publish charts
run: ./scripts/publish-charts.py
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -60,12 +60,16 @@ SEMVER_REGEX = r"^v?(?P<major>[0-9]+).(?P<minor>[0-9]+).(?P<patch>[0-9]+)(-(?P<p
def get_version():
"""
Returns a (version, is_tag) tuple where version is a SemVer-compliant version based on Git information
for the current working directory and is_tag is true iff the current commit is a tagged commit.
Returns a (version, app_version, is_tag) tuple where version is a SemVer-compliant version based on
Git information for the current working directory, app_version is the short-sha as used to tag the
utils image and is_tag is true iff the current commit is a tagged commit.
The version is based on the distance from the last tag and includes the name of the branch that the
commit is on. It is is constructed such that the versions for a particular branch will order correctly.
"""
# The app version is always the short SHA
app_version = cmd(["git", "rev-parse", "--short", "HEAD"])
# Assembling the version is more complicated
try:
# Start by trying to find the most recent tag
last_tag = cmd(["git", "describe", "--tags", "--abbrev=0"])
@ -106,39 +110,16 @@ def get_version():
version += f"-{prerelease_vn}"
# The current commit is a tagged commit if the number of commits since the last tag is zero
return version, commits == 0
return version, app_version, commits == 0
def get_charts():
def is_changed(path, changed_paths):
"""
Returns the list of chart directories for the repository.
"""
if 'CHART_DIRECTORY' in os.environ:
chart_directory = pathlib.Path(os.environ['CHART_DIRECTORY']).resolve()
else:
chart_directory = pathlib.Path(__file__).resolve().parent.parent
return [path.parent for path in pathlib.Path(chart_directory).glob('**/Chart.yaml')]
def get_changed_charts(charts):
"""
Returns the list of chart directories that were changed by the last commit.
"""
# First, get the files that were changed by the last commit
commit = cmd(["git", "rev-parse", "HEAD"])
commit_files = cmd(["git", "show", "--pretty=", "--name-only", commit])
changed = [pathlib.Path(filename).resolve() for filename in commit_files.splitlines()]
# Cross-reference the charts against the changed files
return [chart for chart in charts if is_changed(chart, changed)]
def is_changed(path, changed_files):
"""
Returns true if the given path is in the changed files.
Returns true if the given path is in the changed paths.
"""
return any(
changed_file.is_relative_to(pathlib.Path(path).resolve())
for changed_file in changed_files
for changed_file in changed_paths
)
@ -189,22 +170,49 @@ def main():
"""
Entrypoint for the script.
"""
# Get the version to use for deployed charts
version, is_tag = get_version()
print(f"[INFO] Charts will be published with version '{version}'")
# If the commit is a tagged commit, we will publish all the charts, regardless of
# whether they have changed
charts = get_charts()
if is_tag:
print("[INFO] Detected tagged commit - publishing all charts")
# Get the directories we will be working with
repo_root = pathlib.Path(__file__).resolve().parent.parent
if 'CHART_DIRECTORY' in os.environ:
chart_directory = pathlib.Path(os.environ['CHART_DIRECTORY']).resolve()
else:
chart_directory = repo_root / "charts"
if 'IMAGE_DIRECTORY' in os.environ:
image_directory = pathlib.Path(os.environ['IMAGE_DIRECTORY']).resolve()
else:
image_directory = repo_root / "utils"
# Get the version to use for deployed charts
version, app_version, is_tag = get_version()
print(f"[INFO] Charts will be published with version '{version}'")
# Get the paths that were changed by the curent commit
commit = cmd(["git", "rev-parse", "HEAD"])
commit_files = cmd(["git", "show", "--pretty=", "--name-only", commit])
changed_paths = [pathlib.Path(filename).resolve() for filename in commit_files.splitlines()]
# Get the charts in the repository
charts = [path.parent for path in chart_directory.glob('**/Chart.yaml')]
# Decide which charts we are actually going to publish
if is_tag:
# If the commit is a tag, publish all the charts regardless of changes
# so that they get the version bump
print("[INFO] Detected tagged commit - publishing all charts")
elif is_changed(image_directory, changed_paths):
# If the image was changed, publish all the charts regardless of changes
# so that they pick up the new image for any deployment jobs
print("[INFO] Image for deploy jobs has changed - publishing all charts")
else:
# In all other cases, just publish the charts that changed
print("[INFO] Publishing changed charts only")
charts = get_changed_charts(charts)
charts = [chart for chart in charts if is_changed(chart, changed_paths)]
if charts:
print(f"[INFO] Publishing {len(charts)} charts")
else:
print("[INFO] No charts to publish - exiting")
return
# Publish the charts and re-generate the repository index
publish_branch = os.environ.get('PUBLISH_BRANCH', 'gh-pages')
print(f"[INFO] Charts will be published to branch '{publish_branch}'")
with tempfile.TemporaryDirectory() as publish_directory:
@ -217,6 +225,8 @@ def main():
"--dependency-update",
"--version",
version,
"--app-version",
app_version,
"--destination",
publish_directory,
chart_directory

60
utils/Dockerfile Normal file
View File

@ -0,0 +1,60 @@
#####
## Build specification for a container image containing tools that are useful
## for deploying applications
##
## It is designed to be run as a job inside the target cluster
#####
FROM alpine
# Install some useful tools
RUN apk add --no-cache curl jq
# Because of the version skew policy, which only guarantees compatibility with a skew
# of one minor version, we must install a bunch of kubectl versions
ARG KUBECTL_VERSIONS="v1.22.0 v1.21.4 v1.20.10 v1.19.14 v1.18.20 v1.17.17"
RUN set -ex; \
OS_ARCH="$(uname -m)"; \
case "$OS_ARCH" in \
x86_64) kubectl_arch=amd64 ;; \
aarch64) kubectl_arch=arm64 ;; \
*) false ;; \
esac; \
for kubectl_version in $KUBECTL_VERSIONS; do \
kubectl_minor_version=${kubectl_version%.*}; \
kubectl_latest_version=${kubectl_latest_version:-$kubectl_minor_version}; \
kubectl_url=https://dl.k8s.io/release/$kubectl_version/bin/linux/$kubectl_arch/kubectl; \
kubectl_exe=/usr/bin/kubectl-$kubectl_minor_version; \
curl -fsSL $kubectl_url -o $kubectl_exe; \
chmod +x $kubectl_exe; \
$kubectl_exe version --client; \
done; \
ln -s /usr/bin/kubectl-$kubectl_latest_version /usr/bin/kubectl-latest
# This script uses the latest version to discover the version of the Kubernetes
# cluster before picking the correct version to use
COPY ./kubectl-shim /usr/bin/kubectl
ARG HELM_VERSION=v3.6.3
RUN set -ex; \
OS_ARCH="$(uname -m)"; \
case "$OS_ARCH" in \
x86_64) helm_arch=amd64 ;; \
aarch64) helm_arch=arm64 ;; \
*) false ;; \
esac; \
curl -fsSL https://get.helm.sh/helm-$HELM_VERSION-linux-$helm_arch.tar.gz | \
tar -xz --strip-components 1 -C /usr/bin linux-$helm_arch/helm; \
helm version
ARG CLUSTERCTL_VERSION=v0.4.1
RUN set -ex; \
OS_ARCH="$(uname -m)"; \
case "$OS_ARCH" in \
x86_64) clusterctl_arch=amd64 ;; \
aarch64) clusterctl_arch=arm64 ;; \
*) false ;; \
esac; \
curl -fsSL https://github.com/kubernetes-sigs/cluster-api/releases/download/$CLUSTERCTL_VERSION/clusterctl-linux-$clusterctl_arch -o /usr/bin/clusterctl; \
chmod +x /usr/bin/clusterctl; \
clusterctl version

10
utils/kubectl-shim Normal file
View File

@ -0,0 +1,10 @@
#!/bin/sh
set -eo pipefail
#####
# Simple script that selects the correct kubectl version for the target server
#####
server_version="$(kubectl-latest version -o json | jq '"v" + .serverVersion.major + "." + .serverVersion.minor')"
exec kubectl-$server_version "$@"