Skip to content

Commit

Permalink
feat: add new ansible build matrix
Browse files Browse the repository at this point in the history
  • Loading branch information
eliecharra committed Aug 2, 2024
1 parent b86b6fd commit c0bcd94
Show file tree
Hide file tree
Showing 8 changed files with 536 additions and 10 deletions.
9 changes: 9 additions & 0 deletions .github/scripts/retag-and-push.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -e

FROM="$1"
TO="$2"

echo "Pushing ${TO}"
docker tag "${FROM}" "${TO}"
docker push "${TO}"
286 changes: 286 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
name: Build docker images
on:
push:
# TODO Enable schedule
#schedule:
# At 00:00 every Sunday
#- cron: 0 0 * * 0
concurrency:
group: docker-${{ github.ref }}
cancel-in-progress: true
jobs:
matrix:
name: Compute build matrix from pypi API
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.matrix.outputs.matrix }}
steps:
- name: Check out the repo
uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version-file: './build-matrix/go.mod'
cache-dependency-path: './build-matrix/go.mod'

- name: Run matrix generator tests
working-directory: build-matrix
run: go test ./

# TODO Remove the fake matrix
- id: matrix
working-directory: build-matrix
run: |
MATRIX=$(go run ./)
echo ${MATRIX} | jq
echo 'matrix=[{"ansible":"10.2","additional_tags":["10"]},{"ansible":"10.1","additional_tags":[]}]' >> $GITHUB_OUTPUT
#echo "matrix=${MATRIX}" >> $GITHUB_OUTPUT
build:
needs: [ matrix ]
runs-on: ubuntu-latest
name: Build ansible ${{ matrix.versions.ansible }}-${{ matrix.target }}/${{ matrix.platform }}
permissions:
packages: write
contents: read
strategy:
fail-fast: false
matrix:
target:
- base
- aws
- gcp
platform:
- linux/amd64
- linux/arm64
versions: ${{ fromJson(needs.matrix.outputs.matrix) }}
steps:
- name: Check out the repo
uses: actions/checkout@v4

- name: Prepare
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Set up QEMU
if: matrix.platform == 'linux/arm64'
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build Docker images
uses: docker/build-push-action@v6
id: build
with:
pull: true
target: ${{ matrix.target }}
build-args: |
ANSIBLE_VERSION=${{ matrix.versions.ansible }}
platforms: ${{ matrix.platform }}
outputs: type=docker,dest=/tmp/${{ matrix.versions.ansible }}-${{ matrix.target }}-${{ env.PLATFORM_PAIR }}.tar
tags: ghcr.io/${{ github.repository }}:${{ matrix.versions.ansible }}-${{ matrix.target }}-${{ env.PLATFORM_PAIR }}-${{ github.sha }}

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ansible-runner-${{ github.sha }}-${{ matrix.versions.ansible }}-${{ matrix.target }}-${{ env.PLATFORM_PAIR }}
path: /tmp/${{ matrix.versions.ansible }}-${{ matrix.target }}-${{ env.PLATFORM_PAIR }}.tar
retention-days: 1
if-no-files-found: error

test:
name: Test image ${{ matrix.versions.ansible }}-${{ matrix.target }}/${{ matrix.platform }}
needs: [ matrix, build ]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target:
- base
- aws
- gcp
platform:
- linux/amd64
versions: ${{ fromJson(needs.matrix.outputs.matrix) }}
steps:
- name: Prepare
run: |
platform=${{ matrix.platform }}
export PLATFORM_PAIR=${platform//\//-}
echo "PLATFORM_PAIR=${PLATFORM_PAIR}" >> $GITHUB_ENV
echo "IMAGE=ghcr.io/${{ github.repository }}:${{ matrix.versions.ansible }}-${{ matrix.target }}-${PLATFORM_PAIR}-${{ github.sha }}" >> $GITHUB_ENV
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: ansible-runner-${{ github.sha }}-${{ matrix.versions.ansible }}-${{ matrix.target }}-${{ env.PLATFORM_PAIR }}
path: /tmp

- name: Load image
run: |
docker load --input /tmp/${{ matrix.versions.ansible }}-${{ matrix.target }}-${{ env.PLATFORM_PAIR }}.tar
docker image ls -a
- name: Test ansible version
run: docker run --rm ${{ env.IMAGE }} ansible-community --version | grep 'Ansible community version ${{ matrix.versions.ansible }}'

- name: Test aws flavor
if: matrix.target == 'aws'
run: |
docker run --rm ${{ env.IMAGE }} sh -c "python3 -c \"import boto3; print(boto3.__version__)\""
- name: Test gcp flavor
if: matrix.target == 'gcp'
run: |
docker run --rm ${{ env.IMAGE }} sh -c "python3 -c \"import google.auth; print(google.auth.__version__)\""
deploy:
name: Push image ${{ matrix.versions.ansible }}
needs: [ matrix, test ]
runs-on: ubuntu-latest
env:
AWS_REGION: "us-east-1"
strategy:
fail-fast: false
matrix:
versions: ${{ fromJson(needs.matrix.outputs.matrix) }}
if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/feat/ansible_build_matrix' }} # TODO(eliecharra): Remove condition
permissions:
id-token: write
packages: write
contents: read
steps:
- name: Download base/amd64 artifact
uses: actions/download-artifact@v4
with:
name: ansible-runner-${{ github.sha }}-${{ matrix.versions.ansible }}-base-linux-amd64
path: /tmp

- name: Download gcp/amd64 artifact
uses: actions/download-artifact@v4
with:
name: ansible-runner-${{ github.sha }}-${{ matrix.versions.ansible }}-gcp-linux-amd64
path: /tmp

- name: Download aws/amd64 artifact
uses: actions/download-artifact@v4
with:
name: ansible-runner-${{ github.sha }}-${{ matrix.versions.ansible }}-aws-linux-amd64
path: /tmp

- name: Download base/arm64 artifact
uses: actions/download-artifact@v4
with:
name: ansible-runner-${{ github.sha }}-${{ matrix.versions.ansible }}-base-linux-arm64
path: /tmp

- name: Download gcp/arm64 artifact
uses: actions/download-artifact@v4
with:
name: ansible-runner-${{ github.sha }}-${{ matrix.versions.ansible }}-gcp-linux-arm64
path: /tmp

- name: Download aws/arm64 artifact
uses: actions/download-artifact@v4
with:
name: ansible-runner-${{ github.sha }}-${{ matrix.versions.ansible }}-aws-linux-arm64
path: /tmp

- name: Load image
run: |
docker load --input /tmp/${{ matrix.versions.ansible }}-base-linux-amd64.tar
docker load --input /tmp/${{ matrix.versions.ansible }}-gcp-linux-amd64.tar
docker load --input /tmp/${{ matrix.versions.ansible }}-aws-linux-amd64.tar
docker load --input /tmp/${{ matrix.versions.ansible }}-base-linux-arm64.tar
docker load --input /tmp/${{ matrix.versions.ansible }}-gcp-linux-arm64.tar
docker load --input /tmp/${{ matrix.versions.ansible }}-aws-linux-arm64.tar
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ env.AWS_REGION }}
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
role-duration-seconds: 900

- name: Login to Amazon ECR
run: aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${REPOSITORY_PATH}
env:
REPOSITORY_PATH: ${{ secrets.PUBLIC_RUNNER_ANSIBLE_ECR_REPOSITORY_URL }}

- name: Log in to Github Container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Check out the repo
uses: actions/checkout@v4

- name: Push images
working-directory: .github/scripts
env:
ECR_IMAGE: ${{ secrets.PUBLIC_RUNNER_ANSIBLE_ECR_REPOSITORY_URL }}/${{ github.repository }}
run: |
# Push ECR amd64 tags
./retag-and-push.sh ghcr.io/${{ github.repository }}:${{ matrix.versions.ansible }}-base-linux-amd64-${{ github.sha }}\
${{ env.ECR_IMAGE }}:${{ matrix.versions.ansible }}-linux-amd64
security:
name: Security scan
needs: [ matrix, deploy ]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm64
versions: ${{ fromJson(needs.matrix.outputs.matrix) }}
steps:
- name: Prepare
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Run Trivy vulnerability scanner for base image
uses: aquasecurity/[email protected]
with:
image-ref: "ghcr.io/${{ github.repository }}:${{ matrix.versions.ansible }}-${{ env.PLATFORM_PAIR }}"
format: "template"
template: "@/contrib/sarif.tpl"
output: "base.sarif"
severity: "CRITICAL,HIGH"

- name: Run Trivy vulnerability scanner for gcp image
uses: aquasecurity/[email protected]
with:
image-ref: "ghcr.io/${{ github.repository }}:${{ matrix.versions.ansible }}-gcp-${{ env.PLATFORM_PAIR }}"
format: "template"
template: "@/contrib/sarif.tpl"
output: "gcp.sarif"
severity: "CRITICAL,HIGH"

- name: Run Trivy vulnerability scanner for aws image
uses: aquasecurity/[email protected]
with:
image-ref: "ghcr.io/${{ github.repository }}:${{ matrix.versions.ansible }}-aws-${{ env.PLATFORM_PAIR }}"
format: "template"
template: "@/contrib/sarif.tpl"
output: "aws.sarif"
severity: "CRITICAL,HIGH"

- name: Upload base image scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: "base.sarif"

- name: Upload gcp image scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: "gcp.sarif"

- name: Upload aws image scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: "aws.sarif"
24 changes: 24 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM python:3.12-alpine AS ansible
ARG ANSIBLE_VERSION=10.0
RUN apk -U upgrade --available &&\
# Required to install ansible pip package, bear in mind to remove those build deps at the end of this RUN directive
apk add --no-cache gcc musl-dev libffi-dev openssl-dev &&\
# Add here package mandatory to be able to run ansible
apk add --no-cache openssh-client ca-certificates&&\
pip install --no-cache-dir --upgrade pip &&\
pip install --no-cache-dir ansible==${ANSIBLE_VERSION}.* ansible-runner~=2.4 &&\
# Cleanup package manager cache and remove build deps
rm -rf /var/cache/apk/* &&\
pip cache purge &&\
apk del gcc musl-dev gcc musl-dev libffi-dev openssl-dev

FROM ansible AS base
USER 1983

FROM ansible AS gcp
RUN pip install --no-cache-dir requests==2.* google-auth==2.*
USER 1983

FROM ansible AS aws
RUN pip install --no-cache-dir boto3==1.*
USER 1983
40 changes: 30 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,44 @@ The image is pushed to the `public.ecr.aws/spacelift/runner-ansible` public repo

Altogether we have 3 flavors of the image:

- `public.ecr.aws/spacelift/runner-ansible` - built on top of the [Spacelift Terraform runner image](https://github.com/spacelift-io/runner-terraform), with Ansible installed.
- `public.ecr.aws/spacelift/runner-ansible-aws` - built on top of `runner-ansible`, with `boto3` installed.
- `public.ecr.aws/spacelift/runner-ansible-gcp` - built on top of `runner-ansible`, with `google-auth` installed.
- `runner-ansible:${ANSIBLE_VERSION}` - built on top of `python:3.12-alpine` base image, with `ansible` and `ansible-runner` installed.
- `runner-ansible:${ANSIBLE_VERSION}-aws` - built on top of `runner-ansible`, with `boto3` installed.
- `runner-ansible:${ANSIBLE_VERSION}-gcp` - built on top of `runner-ansible`, with `google-auth` installed.

## Branch Model
Every image is available for the following architectures:

This repository uses two main branches:
- linux/amd64
- linux/arm64

- `main` - contains the production version of the runner image.
- `future` - used to test development changes.
## Tag Model

Pushes to main deploy to the `latest` tag, whereas pushes to future deploy to the `future` tag. This
means that to use the development version you can use the `public.ecr.aws/spacelift/runner-ansible:future` image.
This repository create a tag for each minor version of ansible.

## Development
In case you don't care about locking the minor version, we also create a tag for the major version that is automatically
bumped when a new minor is released.

You can find below is a non-exhaustive list of tags. This may get outdated with time.

- `10`, `10.2`
- `10.1`
- `9`, `9.8`
- `9.7`
- `...`

All tags are rebuild every sunday at midnight to be able to get latest security fixes.

## Contributing

The only requirement for working on this repo is a [Docker](https://www.docker.com/) installation.

**ℹ️ Please do not open PR to add a new package to those base images because your workflow need it.**

We want to keep the size of those base image as small as possible 🙏 Only package that are a **strong requirement** to run ansible will be accepted in base images.

If you need a specific package, please maintain your own version using those image as base image with `FROM public.ecr.aws/spacelift/runner-ansible:10`.

We are open to add new image flavors if needed to support common ansible roles and use cases. The rule of thumb is to keep base image small.

### Testing a new Image

The easiest way to test a new image before opening a pull request is to push it to your own
Expand Down
11 changes: 11 additions & 0 deletions build-matrix/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module github.com/spacelift-io/build-matrix

go 1.22.1

require (
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.9.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit c0bcd94

Please sign in to comment.