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 1, 2024
1 parent 98be0ab commit 6ce747f
Show file tree
Hide file tree
Showing 7 changed files with 610 additions and 10 deletions.
381 changes: 381 additions & 0 deletions .github/workflows/docker.yml

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM python:3.12-alpine as base
ARG ANSIBLE_VERSION=10.0
RUN apt update && DEBIAN_FRONTEND=noninteractive apt upgrade -y &&\
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* &&\
pip install --no-cache-dir ansible==${ANSIBLE_VERSION}.* ansible-runner~=2.4

FROM base as gcp
RUN pip install --no-cache-dir requests==2.32.3 google-auth==2.32.0

FROM base as aws
RUN pip install --no-cache-dir boto3==1.34.151
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` 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
)
11 changes: 11 additions & 0 deletions build-matrix/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
116 changes: 116 additions & 0 deletions build-matrix/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package main

import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"sort"

"github.com/Masterminds/semver/v3"
)

const (
// Define the oldest major version we care about, we do not want to build image starting ansible 1.0
minSupportedMajor = 7
)

type ReleaseResponse struct {
Releases map[string]any `json:"releases"`
}

type matrixVersion struct {
Ansible string `json:"ansible"`
AdditionalTags []string `json:"additional_tags"`
}
type Matrix []matrixVersion

// This small script reads ansible versions from pypi and returns an aggregated list of deduplicated minor version.
// This is used to compute the build matrix in github to build all minor versions in parallel.
// This script will also find the latest minor version for every major to be able to tag the docker image dynamically.
// For example if we have the following version returned:
// - 10.1.1
// - 10.3.1
// - 10.4.3
// - 10.4.3.
// The script will return the following versions:
// - 10.1
// - 10.3
// - 10.4, additional_tags: 10
// Check main_test.go for a quick overview of the expected behavior.
func main() {
resp, err := http.Get("https://pypi.org/pypi/ansible/json")
if err != nil {
log.Fatal(err)
}

matrixOutput := GenerateBuildMatrix(resp.Body, minSupportedMajor)

output, err := json.Marshal(matrixOutput)
if err != nil {
log.Fatal(err)
}
fmt.Print(string(output))
}

func GenerateBuildMatrix(reader io.Reader, minSupportedMajor uint64) Matrix {
releases := ReleaseResponse{}
if err := json.NewDecoder(reader).Decode(&releases); err != nil {
log.Fatal(err)
}

var versions []*semver.Version

for v := range releases.Releases {
version, err := semver.NewVersion(v)
if err != nil {
log.Printf("Unable to parse version %s\n", v)
continue
}
versions = append(versions, version)
}

sort.Slice(versions, func(i, j int) bool {
return versions[j].LessThan(versions[i])
})

versionGroupedByMajor := make(map[int][]*semver.Version)
// Just used for stable ordering
var majorVersions []int

for _, version := range versions {
if version.Major() < minSupportedMajor {
break
}
major := int(version.Major())
if _, exists := versionGroupedByMajor[major]; !exists {
majorVersions = append(majorVersions, major)
}
versionGroupedByMajor[major] = append(versionGroupedByMajor[major], version)
}

sort.Sort(sort.Reverse(sort.IntSlice(majorVersions)))

minorVersionDeduplication := map[string]any{}
matrix := Matrix{}
for _, majorVersion := range majorVersions {
for i, version := range versionGroupedByMajor[majorVersion] {
key := fmt.Sprintf("%d.%d", version.Major(), version.Minor())
if _, exists := minorVersionDeduplication[key]; exists {
continue
}
additionalTags := make([]string, 0)
if i == 0 {
additionalTags = append(additionalTags, fmt.Sprintf("%d", version.Major()))
}
minorVersionDeduplication[key] = struct{}{}
matrix = append(matrix, matrixVersion{
Ansible: key,
AdditionalTags: additionalTags,
})
}
}

return matrix
}
49 changes: 49 additions & 0 deletions build-matrix/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package main

import (
"bytes"
"encoding/json"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestGenerateBuildMatrix(t *testing.T) {
fakePythonVersions := ReleaseResponse{
Releases: map[string]any{
"1.1.0": struct{}{},
"1.1.1": struct{}{},
"2.10.0": struct{}{},
"2.11.0": struct{}{},
"2.11.2": struct{}{},
"3.1.0": struct{}{},
"3.1.1": struct{}{},
"3.2.0": struct{}{},
},
}

fakeJsonResponse, err := json.Marshal(fakePythonVersions)
require.NoError(t, err)

matrix := GenerateBuildMatrix(bytes.NewReader(fakeJsonResponse), 2)
expectedMatrix := Matrix{
{
Ansible: "3.2",
AdditionalTags: []string{"3"},
},
{
Ansible: "3.1",
AdditionalTags: []string{},
},
{
Ansible: "2.11",
AdditionalTags: []string{"2"},
},
{
Ansible: "2.10",
AdditionalTags: []string{},
},
}
assert.Equal(t, expectedMatrix, matrix)
}

0 comments on commit 6ce747f

Please sign in to comment.