Skip to content

Commit d1ad450

Browse files
committed
Add basic CI/CD for building and releasing
1 parent fdb5eeb commit d1ad450

File tree

10 files changed

+313
-2
lines changed

10 files changed

+313
-2
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
name: Docker
2+
3+
on:
4+
push:
5+
branches: [ "main" ]
6+
# Publish semver tags as releases.
7+
tags: [ 'v*.*.*' ]
8+
pull_request:
9+
branches: [ "main" ]
10+
11+
env:
12+
# Use docker.io for Docker Hub if empty
13+
REGISTRY: ghcr.io
14+
# github.repository as <account>/<repo>
15+
IMAGE_NAME: ${{ github.repository }}
16+
17+
18+
jobs:
19+
build:
20+
21+
runs-on: ubuntu-latest
22+
permissions:
23+
contents: read
24+
packages: write
25+
# This is used to complete the identity challenge
26+
# with sigstore/fulcio when running outside of PRs.
27+
id-token: write
28+
29+
steps:
30+
- name: Checkout repository
31+
uses: actions/checkout@v4
32+
33+
# Install the cosign tool except on PR
34+
# https://github.com/sigstore/cosign-installer
35+
- name: Install cosign
36+
if: github.event_name != 'pull_request'
37+
uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0
38+
with:
39+
cosign-release: 'v2.2.4'
40+
41+
# Set up BuildKit Docker container builder to be able to build
42+
# multi-platform images and export cache
43+
# https://github.com/docker/setup-buildx-action
44+
- name: Set up Docker Buildx
45+
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
46+
47+
# Login against a Docker registry except on PR
48+
# https://github.com/docker/login-action
49+
- name: Log into registry ${{ env.REGISTRY }}
50+
if: github.event_name != 'pull_request'
51+
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
52+
with:
53+
registry: ${{ env.REGISTRY }}
54+
username: ${{ github.actor }}
55+
password: ${{ secrets.GITHUB_TOKEN }}
56+
57+
# Extract metadata (tags, labels) for Docker
58+
# https://github.com/docker/metadata-action
59+
- name: Extract Docker metadata
60+
id: meta
61+
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0
62+
with:
63+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
64+
65+
- name: Set VERSION in env
66+
run: echo "VERSION=${GITHUB_REF_NAME/\//-}" >> $GITHUB_ENV
67+
68+
# Build and push Docker image with Buildx (don't push on PR)
69+
# https://github.com/docker/build-push-action
70+
- name: Build and push Docker image
71+
id: build-and-push
72+
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0
73+
with:
74+
context: .
75+
push: ${{ github.event_name != 'pull_request' }}
76+
platforms: linux/amd64,linux/arm64
77+
tags: ${{ steps.meta.outputs.tags }}
78+
labels: ${{ steps.meta.outputs.labels }}
79+
cache-from: type=gha
80+
cache-to: type=gha,mode=max
81+
build-args: VERSION=${{ env.VERSION }}
82+
83+
# Sign the resulting Docker image digest except on PRs.
84+
# This will only write to the public Rekor transparency log when the Docker
85+
# repository is public to avoid leaking data. If you would like to publish
86+
# transparency data even for private images, pass --force to cosign below.
87+
# https://github.com/sigstore/cosign
88+
- name: Sign the published Docker image
89+
if: ${{ github.event_name != 'pull_request' }}
90+
env:
91+
# https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable
92+
TAGS: ${{ steps.meta.outputs.tags }}
93+
DIGEST: ${{ steps.build-and-push.outputs.digest }}
94+
# This step uses the identity token to provision an ephemeral certificate
95+
# against the sigstore community Fulcio instance.
96+
run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}

.github/workflows/release.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Create Release
2+
3+
on:
4+
push:
5+
branches: [ "cicd" ]
6+
# Publish semver tags as releases.
7+
tags: [ 'v*.*.*' ]
8+
9+
env:
10+
REGISTRY: ghcr.io
11+
IMAGE_NAME: ${{ github.repository }}
12+
13+
jobs:
14+
release:
15+
permissions:
16+
contents: write # for softprops/action-gh-release to create/update GitHub release
17+
name: update release
18+
runs-on: ubuntu-latest
19+
steps:
20+
- name: checkout code
21+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # tag=v4.2.2
22+
with:
23+
fetch-depth: 0
24+
ref: ${{ github.ref_name }}
25+
- name: Set up Go
26+
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # tag=v5.5.0
27+
with:
28+
go-version-file: go.mod
29+
- name: generate release artifacts
30+
env:
31+
IMG: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
32+
run: make release
33+
- name: Release
34+
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # tag=v2.2.2
35+
with:
36+
prerelease: false
37+
generate_release_notes: true
38+
files: out/*
39+
tag_name: ${{ github.ref_name }}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,6 @@ go.work
2525
*.swp
2626
*.swo
2727
*~
28+
29+
# release directory
30+
out/*

Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
FROM golang:1.24 AS builder
33
ARG TARGETOS
44
ARG TARGETARCH
5+
ARG VERSION
56

67
WORKDIR /workspace
78
# Copy the Go Modules manifests
@@ -21,7 +22,7 @@ COPY internal/ internal/
2122
# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO
2223
# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore,
2324
# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
24-
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go
25+
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -ldflags "-w -s -X github.com/scaleway/cluster-api-provider-scaleway/internal/version.Version=${VERSION}" -o manager cmd/main.go
2526

2627
# Use distroless as minimal base image to package the manager binary
2728
# Refer to https://github.com/GoogleContainerTools/distroless for more details

Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,18 @@ deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in
163163
undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
164164
$(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f -
165165

166+
##@ Release
167+
168+
.PHONY: release
169+
release: manifests generate kustomize ## Creates release manifests in "out" directory.
170+
mkdir -p out
171+
# building metadata.yaml
172+
cp metadata.yaml out/metadata.yaml
173+
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
174+
# building infrastructure-components.yaml
175+
$(KUSTOMIZE) build config/default > out/infrastructure-components.yaml
176+
# building templates
177+
cp templates/*.yaml out/
166178
##@ Dependencies
167179

168180
## Location to install dependencies to

cmd/main.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ package main
33
import (
44
"crypto/tls"
55
"flag"
6+
"fmt"
67
"os"
78
"path/filepath"
89

910
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
1011
// to ensure that exec-entrypoint and run can make use of them.
1112
_ "k8s.io/client-go/plugin/pkg/client/auth"
1213

14+
internalVersion "github.com/scaleway/cluster-api-provider-scaleway/internal/version"
1315
"k8s.io/apimachinery/pkg/runtime"
1416
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
1517
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
@@ -49,6 +51,7 @@ func main() {
4951
var probeAddr string
5052
var secureMetrics bool
5153
var enableHTTP2 bool
54+
var version bool
5255
var tlsOpts []func(*tls.Config)
5356
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
5457
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
@@ -67,12 +70,19 @@ func main() {
6770
flag.StringVar(&metricsCertKey, "metrics-cert-key", "tls.key", "The name of the metrics server key file.")
6871
flag.BoolVar(&enableHTTP2, "enable-http2", false,
6972
"If set, HTTP/2 will be enabled for the metrics and webhook servers")
73+
flag.BoolVar(&version, "version", false,
74+
"If set, display version and exit")
7075
opts := zap.Options{
7176
Development: true,
7277
}
7378
opts.BindFlags(flag.CommandLine)
7479
flag.Parse()
7580

81+
if version {
82+
fmt.Println("cluster-api-provider-scaleway", internalVersion.Version)
83+
os.Exit(0)
84+
}
85+
7686
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
7787

7888
// if the enable-http2 flag is false (the default), http/2 should be disabled

internal/service/scaleway/client/client.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"slices"
66

7+
"github.com/scaleway/cluster-api-provider-scaleway/internal/version"
78
"github.com/scaleway/scaleway-sdk-go/api/block/v1"
89
domain "github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1"
910
"github.com/scaleway/scaleway-sdk-go/api/instance/v1"
@@ -20,6 +21,8 @@ const (
2021
createdByDescription = "Created by cluster-api-provider-scaleway"
2122
)
2223

24+
var userAgent = "cluster-api-provider-scaleway/" + version.Version
25+
2326
// Client is a wrapper over scaleway-sdk-go to access Scaleway Product APIs in
2427
// a specific region and project.
2528
type Client struct {
@@ -55,7 +58,7 @@ func New(region scw.Region, projectID string, secretData map[string][]byte) (*Cl
5558
scw.WithAuth(accessKey, secretKey),
5659
scw.WithDefaultProjectID(projectID),
5760
scw.WithDefaultRegion(region),
58-
scw.WithUserAgent("cluster-api-provider-scaleway/v0.0.0"), // TODO: set version.
61+
scw.WithUserAgent(userAgent),
5962
}
6063

6164
if apiURL := string(secretData[scw.ScwAPIURLEnv]); apiURL != "" {

internal/version/version.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package version
2+
3+
// Version is the version of cluster-api-provider-scaleway.
4+
// This variable should be set during build.
5+
var Version = "dev"

metadata.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# maps release series of major.minor to cluster-api contract version
2+
# the contract version may change between minor or major versions, but *not*
3+
# between patch versions.
4+
#
5+
# update this file only when a new major or minor version is released
6+
apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3
7+
releaseSeries:
8+
- major: 0
9+
minor: 1
10+
contract: v1beta1

templates/cluster-template.yaml

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
apiVersion: cluster.x-k8s.io/v1beta1
2+
kind: Cluster
3+
metadata:
4+
name: ${CLUSTER_NAME}
5+
spec:
6+
clusterNetwork:
7+
pods:
8+
cidrBlocks:
9+
- 10.244.0.0/16
10+
controlPlaneRef:
11+
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
12+
kind: KubeadmControlPlane
13+
name: ${CLUSTER_NAME}-control-plane
14+
infrastructureRef:
15+
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
16+
kind: ScalewayCluster
17+
name: ${CLUSTER_NAME}
18+
---
19+
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
20+
kind: KubeadmControlPlane
21+
metadata:
22+
name: ${CLUSTER_NAME}-control-plane
23+
spec:
24+
kubeadmConfigSpec:
25+
initConfiguration:
26+
nodeRegistration:
27+
kubeletExtraArgs:
28+
provider-id: "scaleway://instance/{{ ds.meta_data.zone }}/{{ ds.meta_data.id }}"
29+
cloud-provider: external
30+
name: "{{ ds.meta_data.hostname }}"
31+
joinConfiguration:
32+
nodeRegistration:
33+
kubeletExtraArgs:
34+
provider-id: "scaleway://instance/{{ ds.meta_data.zone }}/{{ ds.meta_data.id }}"
35+
cloud-provider: external
36+
name: "{{ ds.meta_data.hostname }}"
37+
machineTemplate:
38+
infrastructureRef:
39+
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
40+
kind: ScalewayMachineTemplate
41+
name: ${CLUSTER_NAME}-control-plane
42+
replicas: ${CONTROL_PLANE_MACHINE_COUNT:=1}
43+
version: ${KUBERNETES_VERSION}
44+
---
45+
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
46+
kind: ScalewayCluster
47+
metadata:
48+
name: ${CLUSTER_NAME}
49+
namespace: default
50+
spec:
51+
projectID: ${SCW_PROJECT_ID}
52+
region: ${SCW_REGION}
53+
scalewaySecretName: ${CLUSTER_NAME}
54+
failureDomains:
55+
- ${SCW_REGION}-1
56+
---
57+
apiVersion: v1
58+
kind: Secret
59+
metadata:
60+
name: ${CLUSTER_NAME}
61+
type: Opaque
62+
stringData:
63+
SCW_ACCESS_KEY: ${SCW_ACCESS_KEY}
64+
SCW_SECRET_KEY: ${SCW_SECRET_KEY}
65+
---
66+
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
67+
kind: ScalewayMachineTemplate
68+
metadata:
69+
name: ${CLUSTER_NAME}-control-plane
70+
spec:
71+
template:
72+
spec:
73+
image:
74+
name: ${CONTROL_PLANE_MACHINE_IMAGE}
75+
commercialType: ${CONTROL_PLANE_MACHINE_COMMERCIAL_TYPE:=DEV1-S}
76+
rootVolume:
77+
type: ${CONTROL_PLANE_MACHINE_ROOT_VOLUME_TYPE:=block}
78+
publicNetwork:
79+
enableIPv4: true
80+
---
81+
apiVersion: cluster.x-k8s.io/v1beta1
82+
kind: MachineDeployment
83+
metadata:
84+
name: ${CLUSTER_NAME}-md-0
85+
spec:
86+
clusterName: ${CLUSTER_NAME}
87+
replicas: ${WORKER_MACHINE_COUNT:=2}
88+
selector:
89+
matchLabels: null
90+
template:
91+
spec:
92+
bootstrap:
93+
configRef:
94+
apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
95+
kind: KubeadmConfigTemplate
96+
name: ${CLUSTER_NAME}-md-0
97+
clusterName: ${CLUSTER_NAME}
98+
failureDomain: ${SCW_REGION}-1
99+
infrastructureRef:
100+
name: ${CLUSTER_NAME}-md-0
101+
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
102+
kind: ScalewayMachineTemplate
103+
version: ${KUBERNETES_VERSION}
104+
---
105+
apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
106+
kind: KubeadmConfigTemplate
107+
metadata:
108+
name: ${CLUSTER_NAME}-md-0
109+
spec:
110+
template:
111+
spec:
112+
joinConfiguration:
113+
nodeRegistration:
114+
kubeletExtraArgs:
115+
provider-id: "scaleway://instance/{{ ds.meta_data.zone }}/{{ ds.meta_data.id }}"
116+
cloud-provider: external
117+
name: "{{ ds.meta_data.hostname }}"
118+
---
119+
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
120+
kind: ScalewayMachineTemplate
121+
metadata:
122+
name: ${CLUSTER_NAME}-md-0
123+
spec:
124+
template:
125+
spec:
126+
image:
127+
name: ${WORKER_MACHINE_IMAGE}
128+
commercialType: ${WORKER_MACHINE_COMMERCIAL_TYPE:=DEV1-S}
129+
rootVolume:
130+
type: ${WORKER_MACHINE_ROOT_VOLUME_TYPE:=block}
131+
publicNetwork:
132+
enableIPv4: true

0 commit comments

Comments
 (0)