diff --git a/.github/workflows/build-and-publish-container-image.yml b/.github/workflows/build-and-publish-container-image.yml index 67eb8a2..66c6f5e 100644 --- a/.github/workflows/build-and-publish-container-image.yml +++ b/.github/workflows/build-and-publish-container-image.yml @@ -1,40 +1,65 @@ name: Build and Push Container Image -run-name: "Build and Push Image: Kubectl ${{ inputs.kubectl-version }}, Helm ${{ inputs.helm-version }}, Powershell ${{ inputs.powershell-version }}" on: - workflow_dispatch: - inputs: - kubectl-version: - required: true - type: string - default: "1.30.5" - helm-version: - required: true - type: string - default: "3.16.1" - powershell-version: - required: true - type: string - default: "7.4.5" - tag-as-latest: - description: "(Main only) Tag the image as latest - kubectl 1.30.x only" - required: true - type: boolean - default: false + pull_request: + paths: + - 'versions.json' + push: + branches: + - main + paths: + - 'versions.json' + +env: + ArtifactoryImagePath: "${{ secrets.ARTIFACTORY_DOCKER_REPO_HOSTNAME }}/octopusdeploy/kubernetes-agent-tools-base" + DockerHubImagePath: "octopusdeploy/kubernetes-agent-tools-base" jobs: + versions: + runs-on: ubuntu-latest + + outputs: + toolsVersions: ${{ steps.versions.outputs.tools }} + latestVersion: ${{ steps.versions.outputs.latest }} + revisionHash: ${{ steps.versions.outputs.revisionHash}} + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: "Parse versions.json" + id: versions + run: | + toolsVersions=$(jq -c .tools versions.json) + latestVersion=$(jq -r -c .latest versions.json) + revisionHash=$(jq -r -c .revisionHash versions.json) + + echo "tools=$toolsVersions" >> $GITHUB_OUTPUT + echo "tools=$toolsVersions" + + echo "latest=$latestVersion" >> $GITHUB_OUTPUT + echo "latest=$latestVersion" + + echo "revisionHash=$revisionHash" >> $GITHUB_OUTPUT + echo "revisionHash=$revisionHash" + build: runs-on: ubuntu-latest + needs: versions + strategy: + matrix: ${{ fromJSON(needs.versions.outputs.toolsVersions) }} steps: - name: Log Inputs run: | - echo "Kubectl Version: ${{ inputs.kubectl-version }}" - echo "Helm Version: ${{ inputs.helm-version }}" + echo "Kubectl Version: ${{ matrix.kubectl }}" + echo "Helm Version: ${{ matrix.helm }}" + echo "Powershell Version: ${{ matrix.powershell }}" - uses: actions/checkout@v4 with: - fetch-depth: 0 + fetch-depth: 0 - name: Get branch names id: branch_names @@ -58,41 +83,63 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} - - name: Create Tag Version + - name: Get Kubernetes Version + id: kubernetes-version + run: | + kubectlVersion="${{ matrix.kubectl }}" + kubeVersion="${kubectlVersion%'.'*}" + echo "kubernetesVersion=$kubeVersion" >> $GITHUB_OUTPUT + echo "kubeVersion=$kubeVersion" + + - name: Create tags run: | - fullVersion="${{ inputs.kubectl-version }}" + kubernetesVersion="${{ steps.kubernetes-version.outputs.kubernetesVersion }}" + revisionHash="-${{ needs.versions.outputs.revisionHash }}" + if [[ "${{steps.branch_names.outputs.branch_name}}" != "main" ]] then preRelease="-${{steps.branch_names.outputs.branch_name}}-$(date +'%Y%m%d%H%M%S')" fi - tagVersion="${fullVersion%'.'*}$preRelease"; - echo "tagVersion=$tagVersion" >> $GITHUB_OUTPUT; - echo "tagVersion=$tagVersion"; - id: createTagVersion - + # The short tag is just `1.30` (if pre-release, contains full pre-release string) + shortTag="$kubernetesVersion$preRelease" + echo "shortTag=$shortTag" >> $GITHUB_OUTPUT; + echo "shortTag=$shortTag"; + + # Revisioned short tag contains the 6 char revision hash e.g. `1.30-Df8l2d` (plus pre-release, if pre-release) + revisionedShortTag="$kubernetesVersion$revisionHash$preRelease" + echo "revisionedShortTag=$revisionedShortTag" >> $GITHUB_OUTPUT; + echo "revisionedShortTag=$revisionedShortTag"; + + # The all versions tag contains all the versions of the main tooling, plus revision hash and pre-release + allVersionsTag="kube${{ matrix.kubectl}}-helm${{ matrix.helm}}-pwsh${{matrix.powershell}}$revisionHash$preRelease" + echo "allVersionsTag=$allVersionsTag" >> $GITHUB_OUTPUT; + echo "allVersionsTag=$allVersionsTag"; + id: create-tags + - name: Build and push for test if: ${{ github.ref != 'refs/heads/main' }} uses: docker/build-push-action@v5 with: push: true - tags: "${{ secrets.ARTIFACTORY_DOCKER_REPO_HOSTNAME }}/octopusdeploy/kubernetes-agent-tools-base:${{ steps.createTagVersion.outputs.tagVersion }}" + tags: "${{ env.ArtifactoryImagePath }}:${{ steps.create-tags.outputs.shortTag }},${{ env.ArtifactoryImagePath }}:${{ steps.create-tags.outputs.revisionedShortTag}},${{ env.ArtifactoryImagePath }}:${{ steps.create-tags.outputs.allVersionsTag}}" platforms: linux/amd64,linux/arm64 build-args: | - "KUBECTL_VERSION=${{ inputs.kubectl-version }}" - "HELM_VERSION=${{ inputs.helm-version }}" - "POWERSHELL_VERSION=${{ inputs.powershell-version }}" + "KUBECTL_VERSION=${{ matrix.kubectl }}" + "HELM_VERSION=${{ matrix.helm }}" + "POWERSHELL_VERSION=${{ matrix.powershell }}" - name: Create production docker tags if: ${{ github.ref == 'refs/heads/main' }} run: | - artifactoryTags="${{ secrets.ARTIFACTORY_DOCKER_REPO_HOSTNAME }}/octopusdeploy/kubernetes-agent-tools-base:${{ steps.createTagVersion.outputs.tagVersion }}" - dockerhubTags="octopusdeploy/kubernetes-agent-tools-base:${{ steps.createTagVersion.outputs.tagVersion }}" + artifactoryTags="$ArtifactoryImagePath:${{ steps.create-tags.outputs.shortTag }},$ArtifactoryImagePath:${{ steps.create-tags.outputs.revisionedShortTag}},$ArtifactoryImagePath:${{ steps.create-tags.outputs.allVersionsTag}}" + dockerhubTags="$DockerHubImagePath:${{ steps.create-tags.outputs.shortTag }},$DockerHubImagePath:${{ steps.create-tags.outputs.revisionedShortTag}},$DockerHubImagePath:${{ steps.create-tags.outputs.allVersionsTag}}" - if [[ "${{ inputs.tag-as-latest }}" == "true" ]] + kubernetesVersion="${{ matrix.kubectl }}" + if [[ "${{ needs.versions.outputs.latestVersion }}" == "${{ steps.kubernetes-version.outputs.kubernetesVersion }}" ]] then - artifactoryTags="$artifactoryTags,${{ secrets.ARTIFACTORY_DOCKER_REPO_HOSTNAME }}/octopusdeploy/kubernetes-agent-tools-base:latest" - dockerhubTags="$dockerhubTags,octopusdeploy/kubernetes-agent-tools-base:latest" + artifactoryTags="$artifactoryTags,$ArtifactoryImagePath:latest" + dockerhubTags="$dockerhubTags,$DockerHubImagePath:latest" fi dockerTags="$artifactoryTags,$dockerhubTags" @@ -100,7 +147,6 @@ jobs: echo "dockerTags=$dockerTags"; id: createProductionDockerTags - - name: Build and push for production if: ${{ github.ref == 'refs/heads/main' }} uses: docker/build-push-action@v5 @@ -109,6 +155,6 @@ jobs: tags: ${{ steps.createProductionDockerTags.outputs.dockerTags }} platforms: linux/amd64,linux/arm64 build-args: | - "KUBECTL_VERSION=${{ inputs.kubectl-version }}" - "HELM_VERSION=${{ inputs.helm-version }}" - "POWERSHELL_VERSION=${{ inputs.powershell-version }}" + "KUBECTL_VERSION=${{ matrix.kubectl }}" + "HELM_VERSION=${{ matrix.helm }}" + "POWERSHELL_VERSION=${{ matrix.powershell }}" diff --git a/README.md b/README.md index 2c9a53a..723c6b2 100644 --- a/README.md +++ b/README.md @@ -4,24 +4,37 @@ This repo produces a container image that is used by the Kubernetes Agent to exe Summary: The image packages `kubectl`, `helm`, `powershell` and `curl` on the base image `mcr.microsoft.com/dotnet/runtime-deps`. -# Building and Pushing a image -Currently this is mostly a manual process which involves dispatching `build-and-publish-container-image` github workflow. -The steps are as follows: -1. Navigate to the ["build and publish container image" workflow](https://github.com/OctopusDeploy/kubernetes-agent-tools-base/actions/workflows/build-and-publish-container-image.yml) -2. Click "Run workflow" -3. Configure the workflow as follows -* Branch: main or your desired branch - Only main will be pushed to dockerhub -* kubectl-version: This follows the Kubernetes versioning - values can be found on the [K8s git repo](https://github.com/kubernetes/kubernetes/tags) -* helm-version: This value will depend on the version of kubectl you have chosen, see the [helm compatibility table](https://helm.sh/docs/topics/version_skew/#supported-version-skew) to get the value. -* powershell-version: See the [Powershell github repo](https://github.com/PowerShell/PowerShell/tags) for a value or just use the default. -* tag-as-latest: If running against main and checked this will also push the image with the latest tag as well as the version tag. -4. Click "Run workflow" - -# Accessing the image -Mainline builds will be pushed to both dockerhub with the name `octopusdeploy/kubernetes-agent-tools-base:{Kubectll Minor Version}.{Kubectl Minor Version}` -Example Dockerhub: `octopusdeploy/kubernetes-agent-tools-base:1.29` - -Branch builds will only be pushed the Octopus' Artifactory instance with a prerelease version `{artifactory-hostname}/octopusdeploy/kubernetes-agent-tools-base:{Kubectll Minor Version}.{Kubectl Minor Version}-{Sanitized Branch Name}-{Date}` -Example: `{artifactory-hostname}/octopusdeploy/kubernetes-agent-tools-base:1.29-tl-push-to-dockerhub-20240424041854` +## Updating versions + +In the root of the directory there is a file, `versions.json` which contains information about what versions of Kubectl (and thus Kubernetes), Helm & Powershell are used to generate the images. +Under the `tools` object, there are 3 fields with versions arrays (`kubectl`,`helm`,`powershell`), which are used in a matrix to generate the images. + +There is also a `latest` field that represents the kubernetes version that will be tagged with the `latest` tag. + +### Tags + +There are 4 tags being published + +- `latest` - Assigned to the highest version of the Kubernetes supported by the Kubernetes agent. +- `{Kubectl Major Version}.{Kubectl Minor Version}` - For each `kubectl` version, there will be an image with the Kubernetes major & minor version. Example: `1.31`. +- `{Kubectl Major Version}.{Kubectl Minor Version}-{Random6Chars}` - For each `kubectl` version, there will be an image with the Kubernetes major & minor version and random 6 char revision hash. Example: `1.31-X5msD0`. +- `kube{Kubectl Version}-helm{Helm Version}-pwsh{Powershell Version}-{Random6Chars}` - Contains all versions of the tools plus the revision hash. Example `kube1.31.1-helm3.16.1-pwsh7.4.5-X5msD0`. + +### What is the `revisionHash`? + +The revision hash is a "cache-busting" mechanism to allow the Kubernetes agent to get an updated version of the tools container image without needing to set the `imagePullPolicy` to `Always`. Because Kubernetes will cache the image on the node(s), it's possible that the image does not get re-acquired when there is a tooling update. + +#### Generating a new `revisionHash` + +As the `revisionHash` is used in the docker tag, which are case-sensitive, the following command generates a unique 6 char hash. + +```bash +tr -dc A-Za-z0-9