mirror of
https://github.com/cert-manager/webhook-example.git
synced 2025-07-02 06:45:49 +02:00
CI/CD using github actions and a custom dnsimple account (#31)
* restructure source and templates to follow helm best-practices * add basic actions for more complex workflows * workflows for pr ands tags (along with required fixes to go code) * re-add helm chart and automated chart release * fix errors in workflows * improve documentation * fix finding existing records * add missing namespace to workflow * cleanup of kubernetes pipeline * Fixed and documented tagging workflow
This commit is contained in:
parent
c1db14cfbf
commit
b5793bb12c
34 changed files with 411 additions and 192 deletions
49
.github/workflows/build-images.yaml
vendored
Normal file
49
.github/workflows/build-images.yaml
vendored
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
name: Build docker images
|
||||||
|
|
||||||
|
env:
|
||||||
|
DOCKER_BASE_NAME: 'ghcr.io/${{ github.repository_owner }}/cert-manager-webhook-dnsimple'
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
tags:
|
||||||
|
description: 'Tags to build the image for (separated by a whitespace)'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to GHCR
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
|
||||||
|
- name: Format tags
|
||||||
|
id: format-tags
|
||||||
|
# prepends DOCKER_BASE_NAME to every entry in the string ${{ inputs.tags }}
|
||||||
|
run: |
|
||||||
|
echo "TAGS=$(printf '${{ env.DOCKER_BASE_NAME }}:%s,' ${{ inputs.tags }})" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.format-tags.outputs.TAGS }}
|
30
.github/workflows/helm-release.yaml
vendored
Normal file
30
.github/workflows/helm-release.yaml
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# This workflow publishes a new chart release to github pages
|
||||||
|
# The content of the branch gh-pages is then published to https://puzzle.github.io/cert-manager-webhook-dnsimple/
|
||||||
|
name: Release a new chart version
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Configure Git
|
||||||
|
run: |
|
||||||
|
git config user.name "$GITHUB_ACTOR"
|
||||||
|
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
|
||||||
|
|
||||||
|
- name: Run chart-releaser
|
||||||
|
uses: helm/chart-releaser-action@v1.6.0
|
||||||
|
env:
|
||||||
|
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
with:
|
||||||
|
charts_dir: ./charts
|
54
.github/workflows/test-go.yaml
vendored
Normal file
54
.github/workflows/test-go.yaml
vendored
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
name: Run code tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
workflow_call:
|
||||||
|
secrets:
|
||||||
|
DNSIMPLE_API_TOKEN:
|
||||||
|
required: true
|
||||||
|
DNSIMPLE_ZONE_NAME:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version-file: src/go.mod
|
||||||
|
cache-dependency-path: src/go.sum
|
||||||
|
|
||||||
|
|
||||||
|
- name: Install kubebuilder fixtures
|
||||||
|
id: kubebuilder
|
||||||
|
run: |
|
||||||
|
go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest
|
||||||
|
echo "BIN_DIR=$(setup-envtest use -p path)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
env:
|
||||||
|
DNSIMPLE_API_TOKEN: ${{ secrets.DNSIMPLE_API_TOKEN }}
|
||||||
|
DNSIMPLE_ZONE_NAME: ${{ secrets.DNSIMPLE_ZONE_NAME }}
|
||||||
|
run: |
|
||||||
|
export TEST_ASSET_KUBE_APISERVER=${{ steps.kubebuilder.outputs.BIN_DIR }}/kube-apiserver
|
||||||
|
export TEST_ASSET_ETCD=${{ steps.kubebuilder.outputs.BIN_DIR }}/etcd
|
||||||
|
export TEST_ASSET_KUBECTL=${{ steps.kubebuilder.outputs.BIN_DIR }}/kubectl
|
||||||
|
export TEST_ZONE_NAME="${DNSIMPLE_ZONE_NAME}." # add trailing dot
|
||||||
|
echo """apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: dnsimple-token
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
token: $DNSIMPLE_API_TOKEN
|
||||||
|
""" > testdata/dnsimple-token.yaml
|
||||||
|
cd src
|
||||||
|
go test -v .
|
87
.github/workflows/test-kubernetes.yaml
vendored
Normal file
87
.github/workflows/test-kubernetes.yaml
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
name: Run webhook tests in a full environment
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
secrets:
|
||||||
|
DNSIMPLE_API_TOKEN:
|
||||||
|
required: true
|
||||||
|
DNSIMPLE_ZONE_NAME:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
|
||||||
|
- name: Start minikube
|
||||||
|
uses: medyagh/setup-minikube@master
|
||||||
|
with:
|
||||||
|
kubernetes-version: 1.29.3
|
||||||
|
|
||||||
|
|
||||||
|
- name: Install cert-manager, patch upstream dns servers, wait for readiness
|
||||||
|
run: |
|
||||||
|
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.3/cert-manager.yaml
|
||||||
|
# Patch cert-manager to use DNSimple's nameservers for faster propagation-checks
|
||||||
|
kubectl patch deployment cert-manager -n cert-manager --type='json' -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--dns01-recursive-nameservers=ns1.dnsimple.com:53"}]'
|
||||||
|
kubectl wait --for=condition=available --timeout=600s deployment/cert-manager-webhook -n cert-manager
|
||||||
|
|
||||||
|
|
||||||
|
- name: Install cert-manager-webhook-dnsimple, wait for readiness
|
||||||
|
env:
|
||||||
|
DNSIMPLE_API_TOKEN: ${{ secrets.DNSIMPLE_API_TOKEN }}
|
||||||
|
DNSIMPLE_ZONE_NAME: ${{ secrets.DNSIMPLE_ZONE_NAME}}
|
||||||
|
run: |
|
||||||
|
helm install cert-manager-webhook-dnsimple ./charts/cert-manager-webhook-dnsimple \
|
||||||
|
--namespace cert-manager \
|
||||||
|
--set dnsimple.token="$DNSIMPLE_API_TOKEN" \
|
||||||
|
--set groupName="acme.$DNSIMPLE_ZONE_NAME" \
|
||||||
|
--set image.repository=ghcr.io/${{ github.repository_owner }}/cert-manager-webhook-dnsimple \
|
||||||
|
--set clusterIssuer.staging.enabled=true \
|
||||||
|
--set clusterIssuer.email="noreply@$DNSIMPLE_ZONE_NAME" \
|
||||||
|
--set image.tag=commit-${{ github.sha }}
|
||||||
|
kubectl wait --for=condition=available --timeout=600s deployment/cert-manager-webhook-dnsimple -n cert-manager
|
||||||
|
|
||||||
|
|
||||||
|
- name: Create sample certificate that uses the webhook
|
||||||
|
env:
|
||||||
|
DNSIMPLE_ZONE_NAME: ${{ secrets.DNSIMPLE_ZONE_NAME }}
|
||||||
|
run: |
|
||||||
|
echo """apiVersion: cert-manager.io/v1
|
||||||
|
kind: Certificate
|
||||||
|
metadata:
|
||||||
|
name: dnsimple-test
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
dnsNames:
|
||||||
|
- gh-action-test.$DNSIMPLE_ZONE_NAME
|
||||||
|
issuerRef:
|
||||||
|
name: cert-manager-webhook-dnsimple-staging
|
||||||
|
kind: ClusterIssuer
|
||||||
|
secretName: dnsimple-test-tls
|
||||||
|
""" > certificate.yaml
|
||||||
|
kubectl apply -f certificate.yaml
|
||||||
|
|
||||||
|
|
||||||
|
- name: Assert that the DNS record was created
|
||||||
|
env:
|
||||||
|
DNSIMPLE_ZONE_NAME: ${{ secrets.DNSIMPLE_ZONE_NAME }}
|
||||||
|
timeout-minutes: 10
|
||||||
|
run: |
|
||||||
|
while true; do
|
||||||
|
if nslookup -type=TXT _acme-challenge.gh-action-test.$DNSIMPLE_ZONE_NAME ns1.dnsimple.com; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 30
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
- name: Check the certificate status
|
||||||
|
run: |
|
||||||
|
kubectl wait --for=condition=ready --timeout=600s certificate/dnsimple-test
|
||||||
|
# this should not be necessary since the certificate is usually ready once the DNS record is propagated
|
||||||
|
kubectl get certificate dnsimple-test -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' | grep True
|
32
.github/workflows/workflow_full-test-suite.yaml
vendored
Normal file
32
.github/workflows/workflow_full-test-suite.yaml
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
name: Run full test suite
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
code-test:
|
||||||
|
name: Run tests on code
|
||||||
|
uses: ./.github/workflows/test-go.yaml
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
|
||||||
|
build-image:
|
||||||
|
name: Build Docker image
|
||||||
|
uses: ./.github/workflows/build-images.yaml
|
||||||
|
with:
|
||||||
|
tags: >
|
||||||
|
commit-${{ github.sha }}
|
||||||
|
latest
|
||||||
|
needs: code-test
|
||||||
|
|
||||||
|
|
||||||
|
webhook-tests:
|
||||||
|
name: Run tests on webhooks
|
||||||
|
needs: build-image
|
||||||
|
uses: ./.github/workflows/test-kubernetes.yaml
|
||||||
|
secrets: inherit
|
16
.github/workflows/workflow_tagged-build.yaml
vendored
Normal file
16
.github/workflows/workflow_tagged-build.yaml
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
name: Publish a new tagged Docker image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags: # v* tags are protected in the repository settings
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker-build:
|
||||||
|
name: Build tagged Docker image
|
||||||
|
uses: ./.github/workflows/build-images.yaml
|
||||||
|
with:
|
||||||
|
tags: >
|
||||||
|
${{ github.ref_name }}
|
||||||
|
commit-${{ github.sha }}
|
||||||
|
latest
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -11,8 +11,5 @@
|
||||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
*.out
|
*.out
|
||||||
|
|
||||||
# Ignore the built binary
|
# Ignore kubebuilder test binaries
|
||||||
cert-manager-webhook-dnsimple
|
_test/
|
||||||
|
|
||||||
# Ignore test binaries
|
|
||||||
__test__/
|
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
FROM golang:1.20-alpine AS build_deps
|
FROM golang:1.22-alpine AS build_deps
|
||||||
|
|
||||||
RUN apk add --no-cache git
|
RUN apk add --no-cache git
|
||||||
|
|
||||||
WORKDIR /workspace
|
WORKDIR /workspace
|
||||||
ENV GO111MODULE=on
|
ENV GO111MODULE=on
|
||||||
|
|
||||||
COPY go.mod .
|
COPY src/go.mod .
|
||||||
COPY go.sum .
|
COPY src/go.sum .
|
||||||
|
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
|
|
||||||
FROM build_deps AS build
|
FROM build_deps AS build
|
||||||
|
|
||||||
COPY . .
|
COPY src .
|
||||||
|
|
||||||
RUN CGO_ENABLED=0 go build -o webhook -ldflags '-w -extldflags "-static"' .
|
RUN CGO_ENABLED=0 go build -o webhook -ldflags '-w -extldflags "-static"' .
|
||||||
|
|
||||||
|
|
29
Makefile
29
Makefile
|
@ -1,21 +1,15 @@
|
||||||
GO ?= $(shell which go)
|
GO ?= $(shell which go)
|
||||||
OS ?= $(shell $(GO) env GOOS)
|
OS ?= $(shell $(GO) env GOOS)
|
||||||
ARCH ?= $(shell $(GO) env GOARCH)
|
ARCH ?= $(shell $(GO) env GOARCH)
|
||||||
|
|
||||||
IMAGE_NAME := "neoskop/cert-manager-webhook-dnsimple"
|
|
||||||
IMAGE_TAG := "latest"
|
|
||||||
|
|
||||||
OUT := $(shell pwd)/_out
|
|
||||||
|
|
||||||
KUBE_VERSION=1.25.0
|
KUBE_VERSION=1.25.0
|
||||||
|
|
||||||
$(shell mkdir -p "$(OUT)")
|
# required by go tests
|
||||||
export TEST_ASSET_ETCD=_test/kubebuilder/etcd
|
export TEST_ASSET_ETCD=../_test/kubebuilder/etcd
|
||||||
export TEST_ASSET_KUBE_APISERVER=_test/kubebuilder/kube-apiserver
|
export TEST_ASSET_KUBE_APISERVER=../_test/kubebuilder/kube-apiserver
|
||||||
export TEST_ASSET_KUBECTL=_test/kubebuilder/kubectl
|
export TEST_ASSET_KUBECTL=../_test/kubebuilder/kubectl
|
||||||
|
|
||||||
test: _test/kubebuilder
|
test: _test/kubebuilder
|
||||||
$(GO) test -v .
|
cd src && $(GO) test -v .
|
||||||
|
|
||||||
_test/kubebuilder:
|
_test/kubebuilder:
|
||||||
curl -fsSL https://go.kubebuilder.io/test-tools/$(KUBE_VERSION)/$(OS)/$(ARCH) -o kubebuilder-tools.tar.gz
|
curl -fsSL https://go.kubebuilder.io/test-tools/$(KUBE_VERSION)/$(OS)/$(ARCH) -o kubebuilder-tools.tar.gz
|
||||||
|
@ -28,15 +22,4 @@ _test/kubebuilder:
|
||||||
clean: clean-kubebuilder
|
clean: clean-kubebuilder
|
||||||
|
|
||||||
clean-kubebuilder:
|
clean-kubebuilder:
|
||||||
rm -Rf _test/kubebuilder
|
rm -Rf _test
|
||||||
|
|
||||||
build:
|
|
||||||
docker build -t "$(IMAGE_NAME):$(IMAGE_TAG)" .
|
|
||||||
|
|
||||||
.PHONY: rendered-manifest.yaml
|
|
||||||
rendered-manifest.yaml:
|
|
||||||
helm template \
|
|
||||||
--name dnsimple-webhook \
|
|
||||||
--set image.repository=$(IMAGE_NAME) \
|
|
||||||
--set image.tag=$(IMAGE_TAG) \
|
|
||||||
deploy/dnsimple-webhook > "$(OUT)/rendered-manifest.yaml"
|
|
||||||
|
|
145
README.md
145
README.md
|
@ -2,95 +2,148 @@
|
||||||
|
|
||||||
A [cert-manager][2] ACME DNS01 solver webhook for [DNSimple][1].
|
A [cert-manager][2] ACME DNS01 solver webhook for [DNSimple][1].
|
||||||
|
|
||||||
|
|
||||||
## Pre-requisites
|
## Pre-requisites
|
||||||
|
|
||||||
- [cert-manager][2] >= 0.13 (The Helm chart uses the new API versions)
|
- [cert-manager][2] >= 0.13 (The Helm chart uses the new API versions)
|
||||||
- Kubernetes >= 1.17.x
|
- Kubernetes >= 1.17.x
|
||||||
- Helm 3 (otherwise adjust the example below accordingly)
|
- Helm 3 (otherwise adjust the example below accordingly)
|
||||||
|
|
||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
Take note of your DNSimple API token from the account settings in the automation tab. Run the following commands replacing the API token / account ID placeholders and email address:
|
1. Take note of your DNSimple API token from the account settings in the automation tab.
|
||||||
|
|
||||||
```bash
|
2. Add the helm repo published under the [Github pages deployment of this repository][4]:
|
||||||
$ helm repo add neoskop https://charts.neoskop.dev
|
```bash
|
||||||
$ helm install cert-manager-webhook-dnsimple \
|
$ helm repo add certmanager-webhook https://puzzle.github.io/cert-manager-webhook-dnsimple
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Install the application, replacing the API token and email placeholders:
|
||||||
|
```bash
|
||||||
|
$ helm repo add certmanager-webhook https://puzzle.github.io/cert-manager-webhook-dnsimple
|
||||||
|
$ helm install cert-manager-webhook-dnsimple \
|
||||||
|
--dry-run \ # remove once you are sure the values are correct
|
||||||
--namespace cert-manager \
|
--namespace cert-manager \
|
||||||
--dry-run \
|
|
||||||
--set dnsimple.token='<DNSIMPLE_API_TOKEN>' \
|
--set dnsimple.token='<DNSIMPLE_API_TOKEN>' \
|
||||||
--set dnsimple.accountID='<DNSIMPLE_ACCOUNT_ID>' # Only needed if using a User API token \
|
|
||||||
--set clusterIssuer.production.enabled=true \
|
--set clusterIssuer.production.enabled=true \
|
||||||
--set clusterIssuer.staging.enabled=true \
|
--set clusterIssuer.staging.enabled=true \
|
||||||
--set clusterIssuer.email=email@example.com \
|
--set clusterIssuer.email=<ISSUER_MAIL> \
|
||||||
neoskop/cert-manager-webhook-dnsimple
|
certmanager-webhook/cert-manager-webhook-dnsimple
|
||||||
```
|
```
|
||||||
|
Alternatively you can check out this repository and substitute the source of the install command with `./charts/cert-manager-webhook-dnsimple`.
|
||||||
|
|
||||||
_(Alternatively you can check out this repository and substitute neoskop/cert-manager-webhook-dnsimple with ./deploy/dnsimple)_
|
4. Afterwards you can issue a certificate:
|
||||||
|
```bash
|
||||||
Afterwards issue a certificate:
|
$ cat << EOF | kubectl apply -f -
|
||||||
|
apiVersion: cert-manager.io/v1
|
||||||
```bash
|
kind: Certificate
|
||||||
$ cat << EOF | kubectl apply -f -
|
metadata:
|
||||||
apiVersion: cert-manager.io/v1
|
|
||||||
kind: Certificate
|
|
||||||
metadata:
|
|
||||||
name: dnsimple-test
|
name: dnsimple-test
|
||||||
namespace: default
|
spec:
|
||||||
spec:
|
|
||||||
dnsNames:
|
dnsNames:
|
||||||
- test.example.com
|
- test.example.com
|
||||||
issuerRef:
|
issuerRef:
|
||||||
name: cert-manager-webhook-dnsimple-production
|
name: cert-manager-webhook-dnsimple-staging
|
||||||
kind: ClusterIssuer
|
kind: ClusterIssuer
|
||||||
secretName: dnsimple-test-tls
|
secretName: dnsimple-test-tls
|
||||||
EOF
|
EOF
|
||||||
```
|
```
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
|
## Chart options
|
||||||
The Helm chart accepts the following values:
|
The Helm chart accepts the following values:
|
||||||
|
|
||||||
| name | required | description | default value |
|
| name | required | description | default value |
|
||||||
| ---------------------------------- | -------- | ----------------------------------------------- | --------------------------------------- |
|
| ---------------------------------- | -------- | ----------------------------------------------- | --------------------------------------- |
|
||||||
| `dnsimple.token` | ✔️ | DNSimple API Token | _empty_ |
|
| `dnsimple.token` | ✔️ | DNSimple API Token | _empty_ |
|
||||||
| `dnsimple.accountID` | | DNSimple Account ID (required for User tokens) | _empty_ |
|
| `dnsimple.accountID` | | DNSimple Account ID (required when `dnsimple.token` is a user-token) | _empty_ |
|
||||||
| `clusterIssuer.email` | | LetsEncrypt Admin Email | `name@example.com` |
|
| `clusterIssuer.email` | | LetsEncrypt Admin Email | _empty_ |
|
||||||
| `clusterIssuer.production.enabled` | | Create a production `ClusterIssuer` | `false` |
|
| `clusterIssuer.production.enabled` | | Create a production `ClusterIssuer` | `false` |
|
||||||
| `clusterIssuer.staging.enabled` | | Create a staging `ClusterIssuer` | `false` |
|
| `clusterIssuer.staging.enabled` | | Create a staging `ClusterIssuer` | `false` |
|
||||||
| `image.repository` | ✔️ | Docker image for the webhook solver | `neoskop/cert-manager-webhook-dnsimple` |
|
| `image.repository` | ✔️ | Docker image for the webhook solver | `ghcr.io/puzzle/cert-manager-webhook-dnsimple` |
|
||||||
| `image.tag` | ✔️ | Docker image tag of the solver | `latest` |
|
| `image.tag` | ✔️ | Docker image tag of the solver | latest tagged docker build |
|
||||||
| `image.pullPolicy` | ✔️ | Image pull policy of the solver | `IfNotPresent` |
|
| `image.pullPolicy` | ✔️ | Image pull policy of the solver | `IfNotPresent` |
|
||||||
| `logLevel` | | Set the verbosity of the solver | _empty_ |
|
| `logLevel` | | Set the verbosity of the solver | _empty_ |
|
||||||
| `groupName` | ✔️ | Identifies the company that created the webhook | `acme.neoskop.de` |
|
| `groupName` | ✔️ | Identifies the company that created the webhook | _empty_ |
|
||||||
| `certManager.namespace` | ✔️ | The namespace cert-manager was installed to | `cert-manager` |
|
| `certManager.namespace` | ✔️ | The namespace cert-manager was installed to | `cert-manager` |
|
||||||
| `certManager.serviceAccountName` | ✔️ | The service account cert-manager runs under | `cert-manager` |
|
| `certManager.serviceAccountName` | ✔️ | The service account cert-manager runs under | `cert-manager` |
|
||||||
|
|
||||||
## Test suite
|
|
||||||
|
|
||||||
All cert-manager webhooks have to pass the DNS01 provider conformance testing suite. To run that test suite on this plug-in download the test binaries:
|
## Testing
|
||||||
|
All cert-manager webhooks have to pass the DNS01 provider conformance testing suite.
|
||||||
|
|
||||||
|
### Pull requests
|
||||||
|
Prerequisites for PRs are implemented as GitHub-actions. All tests should pass before a PR is merged:
|
||||||
|
- the `cert-manager` conformance suite is run with provided kubebuilder fixtures
|
||||||
|
- a custom test suite running on a working k8s cluster (using `minikube`) is executed as well
|
||||||
|
|
||||||
|
### Local testing
|
||||||
|
#### Test suite
|
||||||
|
You can also run tests locally, as specified in the `Makefile`:
|
||||||
|
|
||||||
|
1. Set-up `testdata/` according to its [README][3].
|
||||||
|
- `dnsimple-token.yaml` should be filled with a valid token (for either the sandbox or production environment)
|
||||||
|
- `dnsimple.env` should contain the remaining environment variables (non sensitive)
|
||||||
|
2. Execute the test suite:
|
||||||
|
```bash
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
#### In-cluster testing
|
||||||
|
1. Install cert-manager:
|
||||||
|
```bash
|
||||||
|
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.3/cert-manager.yaml
|
||||||
|
```
|
||||||
|
2. Install the webhook:
|
||||||
|
```bash
|
||||||
|
helm install cert-manager-webhook-dnsimple \
|
||||||
|
--namespace cert-manager \
|
||||||
|
--set dnsimple.token='<DNSIMPLE TOKEN>' \
|
||||||
|
--set clusterIssuer.staging.enabled=true \
|
||||||
|
./charts/cert-manager-webhook-dnsimple
|
||||||
|
```
|
||||||
|
3. Test away... You can create a sample certificate to ensure the webhook is working correctly:
|
||||||
|
```bash
|
||||||
|
kubectl apply -f - <<<EOF
|
||||||
|
apiVersion: cert-manager.io/v1
|
||||||
|
kind: Certificate
|
||||||
|
metadata:
|
||||||
|
name: dnsimple-test
|
||||||
|
spec:
|
||||||
|
dnsNames:
|
||||||
|
- test.example.com
|
||||||
|
issuerRef:
|
||||||
|
name: cert-manager-webhook-dnsimple-staging
|
||||||
|
kind: ClusterIssuer
|
||||||
|
secretName: dnsimple-test-tls
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Releases
|
||||||
|
### Docker images
|
||||||
|
Every push to `master` or on a pull-request triggers the upload of a new docker image to the GitHub Container Registry (this is configured through github actions). These images should **not considered stable** and are tagged with `commit-<hash>`. **We recommend using a specific version tag for production deployments instead.**
|
||||||
|
|
||||||
|
Tagged images are considered stable, these are the ones referenced by the default helm values.
|
||||||
|
|
||||||
|
### How to tag
|
||||||
|
Create a new tag and push it to the repository. This will trigger a new container build:
|
||||||
```bash
|
```bash
|
||||||
$ mkdir -p __main__/hack
|
git tag -a v0.1.0 -m "Release v0.1.0"
|
||||||
$ wget -O- https://storage.googleapis.com/kubebuilder-tools/kubebuilder-tools-1.14.1-linux-amd64.tar.gz | tar xz --strip-components=1 -C __main__/hack
|
git push origin v0.1.0
|
||||||
```
|
```
|
||||||
|
We recommend the following versioning scheme: `vX.Y.Z` where `X` is the major version, `Y` the minor version and `Z` the patch version.
|
||||||
|
|
||||||
Then set-up `testdata/dnsimple/config.json` and `testdata/dnsimple/dnsimple-token.yaml` according to the [README][3].
|
### Helm releases
|
||||||
|
Helm charts are only released when significant changes occur. We encourage users to update the underlying image versions on their own. A new release can be triggered manually under the _actions_ tab and running `helm-release`. This only works if a new version was specified in the `Chart.yaml`. The new release will be appended to the [Github pages deployment][4].
|
||||||
|
|
||||||
Execute the test suite replacing `TEST_ZONE_NAME` with a DNS name you have control over with your DNSimple account:
|
|
||||||
|
|
||||||
```bash
|
## Contributing
|
||||||
# Mind the trailing dot in the TEST_ZONE_NAME value
|
We welcome contributions. Please open an issue or a pull request.
|
||||||
$ TEST_ZONE_NAME=example.com. go test .
|
|
||||||
```
|
|
||||||
|
|
||||||
## Release
|
|
||||||
|
|
||||||
After you committed all of your changes, run the following command to tag a new version and build and push a new Docker image tag as well as a new Helm chart:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ ./scripts/release.sh <patch|minor|major>
|
|
||||||
```
|
|
||||||
|
|
||||||
[1]: https://dnsimple.com/
|
[1]: https://dnsimple.com/
|
||||||
[2]: https://cert-manager.io/docs/installation/kubernetes/
|
[2]: https://cert-manager.io/docs/installation/kubernetes/
|
||||||
[3]: ./testdata/dnsimple/README.md
|
[3]: ./testdata/README.md
|
||||||
|
[4]: https://puzzle.github.io/cert-manager-webhook-dnsimple
|
||||||
|
|
12
charts/cert-manager-webhook-dnsimple/Chart.yaml
Normal file
12
charts/cert-manager-webhook-dnsimple/Chart.yaml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
apiVersion: v1
|
||||||
|
appVersion: "0.1.2"
|
||||||
|
description: cert-manager webhook solver for ACME DNS01 challenge via DNSimple
|
||||||
|
name: cert-manager-webhook-dnsimple
|
||||||
|
version: 0.1.2
|
||||||
|
home: https://github.com/puzzle/cert-manager-webhook-dnsimple
|
||||||
|
sources:
|
||||||
|
- https://github.com/puzzle/cert-manager-webhook-dnsimple
|
||||||
|
maintainers:
|
||||||
|
- name: Puzzle ITC
|
||||||
|
email: container@puzzle.ch
|
||||||
|
url: https://www.puzzle.ch
|
|
@ -6,7 +6,7 @@
|
||||||
# solve the DNS01 challenge.
|
# solve the DNS01 challenge.
|
||||||
# This group name should be **unique**, hence using your own company's domain
|
# This group name should be **unique**, hence using your own company's domain
|
||||||
# here is recommended.
|
# here is recommended.
|
||||||
groupName: acme.neoskop.de
|
groupName: ""
|
||||||
certManager:
|
certManager:
|
||||||
namespace: cert-manager
|
namespace: cert-manager
|
||||||
serviceAccountName: cert-manager
|
serviceAccountName: cert-manager
|
||||||
|
@ -23,7 +23,7 @@ clusterIssuer:
|
||||||
production:
|
production:
|
||||||
enabled: false
|
enabled: false
|
||||||
image:
|
image:
|
||||||
repository: neoskop/cert-manager-webhook-dnsimple
|
repository: ghcr.io/puzzle/cert-manager-webhook-dnsimple
|
||||||
tag: 0.1.2
|
tag: 0.1.2
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
# pullSecret: "gcr"
|
# pullSecret: "gcr"
|
|
@ -1,12 +0,0 @@
|
||||||
apiVersion: v1
|
|
||||||
appVersion: "0.1.2"
|
|
||||||
description: cert-manager webhook solver for ACME DNS01 challenge via DNSimple
|
|
||||||
name: cert-manager-webhook-dnsimple
|
|
||||||
version: 0.1.2
|
|
||||||
home: https://github.com/neoskop/cert-manager-webhook-dnsimple
|
|
||||||
sources:
|
|
||||||
- https://github.com/neoskop/cert-manager-webhook-dnsimple
|
|
||||||
maintainers:
|
|
||||||
- name: Arne Diekmann
|
|
||||||
email: diekmann@neoskop.de
|
|
||||||
url: https://www.neoskop.de
|
|
|
@ -1 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
|
@ -1,84 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
check_commands() {
|
|
||||||
for command in $@; do
|
|
||||||
if ! command -v $command >/dev/null; then
|
|
||||||
echo -e "Install \033[1m$command\033[0m"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
inc_version() {
|
|
||||||
version=$1
|
|
||||||
version_array=(${version//./ })
|
|
||||||
|
|
||||||
if [ $2 = "major" ]; then
|
|
||||||
((version_array[0]++))
|
|
||||||
version_array[1]=0
|
|
||||||
version_array[2]=0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $2 = "minor" ]; then
|
|
||||||
((version_array[1]++))
|
|
||||||
version_array[2]=0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $2 = "patch" ]; then
|
|
||||||
((version_array[2]++))
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "${version_array[0]}.${version_array[1]}.${version_array[2]}"
|
|
||||||
}
|
|
||||||
|
|
||||||
check_commands git yq cr
|
|
||||||
|
|
||||||
if [[ "$#" != "1" ]] || [[ ! "$1" =~ ^(patch|minor|major)$ ]]; then
|
|
||||||
echo -e "Usage: $0 \033[1mpatch|minor|major\033[0m"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $(git status --porcelain) ]]; then
|
|
||||||
echo -e "The repository has changes. Commit first...\033[0;31mAborting!\033[0m"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
SCRIPT_DIR=$(
|
|
||||||
cd "$(dirname "$0")" >/dev/null 2>&1
|
|
||||||
pwd -P
|
|
||||||
)
|
|
||||||
|
|
||||||
git pull --rebase
|
|
||||||
current_version=$(yq e .version $SCRIPT_DIR/../deploy/dnsimple/Chart.yaml)
|
|
||||||
version=$(inc_version $current_version $1)
|
|
||||||
cd $SCRIPT_DIR/..
|
|
||||||
docker build -t neoskop/cert-manager-webhook-dnsimple:$version .
|
|
||||||
docker push neoskop/cert-manager-webhook-dnsimple:$version
|
|
||||||
cd - &>/dev/null
|
|
||||||
sed -i "s/appVersion: .*/appVersion: \"$version\"/" $SCRIPT_DIR/../deploy/dnsimple/Chart.yaml
|
|
||||||
sed -i "s/version: .*/version: $version/" $SCRIPT_DIR/../deploy/dnsimple/Chart.yaml
|
|
||||||
|
|
||||||
yq e ".version=\"$version\"" -i $SCRIPT_DIR/../deploy/dnsimple/Chart.yaml
|
|
||||||
yq e ".appVersion=\"$version\"" -i $SCRIPT_DIR/../deploy/dnsimple/Chart.yaml
|
|
||||||
yq e ".image.tag=\"$version\"" -i $SCRIPT_DIR/../deploy/dnsimple/values.yaml
|
|
||||||
git add .
|
|
||||||
git commit -m "chore: Bump version to ${version}."
|
|
||||||
git push
|
|
||||||
|
|
||||||
helm package deploy/dnsimple --destination .deploy
|
|
||||||
cr upload -o neoskop -r cert-manager-webhook-dnsimple -p .deploy
|
|
||||||
git checkout gh-pages
|
|
||||||
cr index -i ./index.yaml -p .deploy -o neoskop -r cert-manager-webhook-dnsimple -c https://neoskop.github.io/cert-manager-webhook-dnsimple/
|
|
||||||
git add index.yaml
|
|
||||||
git commit -m "chore: Bump version to ${version}."
|
|
||||||
git push
|
|
||||||
git checkout master
|
|
||||||
rm -rf .deploy/
|
|
||||||
|
|
||||||
HELM_CHARTS_DIR=../neoskop-helm-charts
|
|
||||||
[ -d $HELM_CHARTS_DIR ] || git clone git@github.com:neoskop/helm-charts.git $HELM_CHARTS_DIR
|
|
||||||
cd $HELM_CHARTS_DIR
|
|
||||||
./update-index.sh
|
|
||||||
cd - &>/dev/null
|
|
|
@ -1,6 +1,6 @@
|
||||||
module github.com/neoskop/cert-manager-webhook-dnsimple
|
module github.com/neoskop/cert-manager-webhook-dnsimple
|
||||||
|
|
||||||
go 1.20
|
go 1.22
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/cert-manager/cert-manager v1.12.1
|
github.com/cert-manager/cert-manager v1.12.1
|
|
@ -138,16 +138,14 @@ func (c *dnsimpleDNSProviderSolver) getDomainAndEntry(ch *v1alpha1.ChallengeRequ
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *dnsimpleDNSProviderSolver) getExistingRecord(cfg *dnsimpleDNSProviderConfig, client *dnsimple.Client, accountID string, zoneName string, entry string, key string) (*dnsimple.ZoneRecord, error) {
|
func (c *dnsimpleDNSProviderSolver) getExistingRecord(cfg *dnsimpleDNSProviderConfig, client *dnsimple.Client, accountID string, zoneName string, entry string, key string) (*dnsimple.ZoneRecord, error) {
|
||||||
|
|
||||||
// Look for existing TXT records.
|
// Look for existing TXT records.
|
||||||
records, err := client.Zones.ListRecords(context.Background(), accountID, zoneName, &dnsimple.ZoneRecordListOptions{Type: dnsimple.String("TXT"), Name: dnsimple.String(entry)})
|
records, err := client.Zones.ListRecords(context.Background(), accountID, zoneName, &dnsimple.ZoneRecordListOptions{Type: dnsimple.String("TXT"), Name: dnsimple.String(entry)})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to get resource records: %s", err)
|
return nil, fmt.Errorf("unable to get resource records: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, record := range records.Data {
|
for _, record := range records.Data {
|
||||||
if record.Content == key {
|
if strings.Replace(record.Content, "\"", "", 2) == key {
|
||||||
return &record, nil
|
return &record, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
zone = os.Getenv("TEST_ZONE_NAME")
|
zone = os.Getenv("TEST_ZONE_NAME")
|
||||||
|
testdata_dir = "../testdata"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRunsSuite(t *testing.T) {
|
func TestRunsSuite(t *testing.T) {
|
||||||
|
@ -19,7 +20,11 @@ func TestRunsSuite(t *testing.T) {
|
||||||
fixture := dns.NewFixture(&dnsimpleDNSProviderSolver{},
|
fixture := dns.NewFixture(&dnsimpleDNSProviderSolver{},
|
||||||
dns.SetResolvedZone(zone),
|
dns.SetResolvedZone(zone),
|
||||||
dns.SetAllowAmbientCredentials(false),
|
dns.SetAllowAmbientCredentials(false),
|
||||||
dns.SetManifestPath("testdata/dnsimple"),
|
dns.SetManifestPath(testdata_dir),
|
||||||
|
dns.SetUseAuthoritative(false),
|
||||||
|
dns.SetDNSServer("ns1.dnsimple.com:53"),
|
||||||
|
// check against dnsimple nameservers for faster propagation
|
||||||
|
dns.SetStrict(true),
|
||||||
)
|
)
|
||||||
|
|
||||||
fixture.RunConformance(t)
|
fixture.RunConformance(t)
|
Loading…
Reference in a new issue