Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement cert reloading and add tests #3

Merged
merged 1 commit into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 44 additions & 6 deletions .github/workflows/build-docker.yaml
Original file line number Diff line number Diff line change
@@ -1,16 +1,43 @@
name: Build and Push Docker Images
name: Build, Test, and Push Docker Images

on:
workflow_dispatch: {}
push:
branches:
- main
paths:
- .github/workflows/build-docker.yaml
- Dockerfile
- "**/*.go"
- "go.mod"
- "go.sum"
- "Dockerfile"
- ".github/workflows/build-docker.yaml"
pull_request:
branches:
- main
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- "Dockerfile"
- ".github/workflows/build-docker.yaml"

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: "1.21"

- name: Run tests
run: go test -v ./...

build-and-push:
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
Expand Down Expand Up @@ -38,13 +65,24 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Generate version
id: version
run: |
if [[ $GITHUB_REF == refs/tags/* ]]; then
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
else
echo "VERSION=$(date +'%Y.%m.%d')-${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
fi

- name: Build and Push
if: github.ref != 'refs/heads/master'
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5
with:
context: .
file: Dockerfile
platforms: linux/amd64
push: true
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: |
ghcr.io/xunholy/kustomize-mutating-webhook:latest
ghcr.io/xunholy/kustomize-mutating-webhook:${{ steps.version.outputs.VERSION }}
cache-from: type=gha
cache-to: type=gha,mode=max
72 changes: 72 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,78 @@ kubectl apply -k kubernetes/static

You can verify the correct values are being collected by either using the `debug` log level which outputs the values on start-up, alternatively you may also verify by inspecting a Kustomization resource that has been mutated.

## Testing and Benchmarking

This project includes unit tests and benchmarks to ensure reliability and performance. Here's how to run them and interpret the results:

### Running Tests

To run the unit tests, use the following command in the project root:

```bash
go test -v ./...
```

This will run all tests and provide verbose output. A successful test run will show "PASS" for each test case.

### Running Benchmarks

To run the benchmarks, use:

```bash
go test -bench=. -benchmem
```

This command runs all benchmarks and includes memory allocation statistics.

### Interpreting Results

#### Test Results

After running the tests, you should see output similar to:

```log
=== RUN TestMutatingWebhook
=== RUN TestMutatingWebhook/Add_postBuild_and_substitute
[... more test output ...]
PASS
ok github.com/xunholy/fluxcd-mutating-webhook 0.015s
```

- "PASS" indicates all tests have passed successfully.
- The time at the end (0.015s in this example) shows how long the tests took to run.

#### Benchmark Results

Benchmark results will look something like this:

```log
4:47PM INF Request details Kind=Kustomization Name= Namespace= Resource= UID=
25410 41239 ns/op
PASS
ok github.com/xunholy/fluxcd-mutating-webhook 1.535s
```

Here's how to interpret these results:

- The first line shows a log output from the benchmark run.
- "25410" is the number of iterations the benchmark ran.
- "41239 ns/op" means each operation took an average of 41,239 nanoseconds (about 0.04 milliseconds).
- "PASS" indicates the benchmark completed successfully.
- "1.535s" is the total time taken for all benchmark runs.

### Importance of Testing and Benchmarking

Regular testing and benchmarking are crucial for several reasons:

1. **Reliability**: Tests ensure that the webhook behaves correctly under various scenarios.
2. **Performance Monitoring**: Benchmarks help track the webhook's performance over time, allowing us to detect and address any performance regressions.
3. **Optimization**: Benchmark results can guide optimization efforts by identifying slow operations.
4. **Confidence in Changes**: Running tests and benchmarks before and after changes helps ensure that modifications don't introduce bugs or performance issues.

We encourage contributors to run tests and benchmarks locally before submitting pull requests, and to include new tests for any added functionality.


## License

Distributed under the Apache 2.0 License. See [LICENSE](./LICENSE) for more information.
9 changes: 8 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,32 @@ module github.com/xunholy/fluxcd-mutating-webhook
go 1.21.1

require (
github.com/fsnotify/fsnotify v1.7.0
github.com/go-chi/chi/v5 v5.1.0
github.com/json-iterator/go v1.1.12
github.com/rs/zerolog v1.31.0
github.com/stretchr/testify v1.8.4
golang.org/x/time v0.3.0
k8s.io/api v0.29.0
k8s.io/apimachinery v0.29.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog/v2 v2.110.1 // indirect
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
Expand Down Expand Up @@ -73,6 +77,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
Expand Down
95 changes: 67 additions & 28 deletions kubernetes/static/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ spec:
selector:
matchLabels: &labels
app: *name
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
template:
metadata:
labels:
Expand All @@ -25,33 +30,67 @@ spec:
seccompProfile:
type: RuntimeDefault
containers:
- name: webhook
image: ghcr.io/xunholy/kustomize-mutating-webhook:latest
imagePullPolicy: Always
env:
- name: LOG_LEVEL
value: info
ports:
- containerPort: 8443
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsUser: 1000
runAsGroup: 1000
capabilities:
drop:
- ALL
volumeMounts:
- name: webhook
image: ghcr.io/xunholy/kustomize-mutating-webhook:latest
imagePullPolicy: Always
env:
- name: LOG_LEVEL
value: info
- name: RATE_LIMIT
value: "100"
ports:
- containerPort: 8443
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsUser: 1000
runAsGroup: 1000
capabilities:
drop:
- ALL
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
readinessProbe:
httpGet:
path: /ready
port: 8443
scheme: HTTPS
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 8443
scheme: HTTPS
initialDelaySeconds: 15
periodSeconds: 20
volumeMounts:
- name: webhook-certs
mountPath: /etc/webhook/certs
readOnly: true
- name: cluster-config
mountPath: /etc/config
readOnly: true
volumes:
- name: webhook-certs
mountPath: /etc/webhook/certs
readOnly: true
secret:
secretName: kustomize-mutating-webhook-tls
- name: cluster-config
mountPath: /etc/config
readOnly: true
volumes:
- name: webhook-certs
secret:
secretName: kustomize-mutating-webhook-tls
- name: cluster-config
configMap:
name: cluster-config
configMap:
name: cluster-config
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: kustomize-mutating-webhook-pdb
namespace: flux-system
spec:
minAvailable: 2
selector:
matchLabels:
app: kustomize-mutating-webhook
55 changes: 29 additions & 26 deletions kubernetes/static/mutating-webhook-configuration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,33 @@ metadata:
namespace: flux-system
annotations:
cert-manager.io/inject-ca-from: flux-system/kustomize-mutating-webhook
labels:
app: kustomize-mutating-webhook
webhooks:
- name: kustomize-mutating-webhook.xunholy.com
admissionReviewVersions: ["v1"]
failurePolicy: Fail
matchPolicy: Equivalent
# TODO: Determine if flux-system should be ignored
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: NotIn
values: ["flux-system"]
objectSelector: {}
reinvocationPolicy: Never
clientConfig:
service:
name: kustomize-mutating-webhook
namespace: flux-system
path: /mutate
port: 8443
rules:
- apiGroups: ["kustomize.toolkit.fluxcd.io"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["kustomizations"]
scope: '*'
sideEffects: None
timeoutSeconds: 10
- name: kustomize-mutating-webhook.xunholy.com
admissionReviewVersions: ["v1"]
# TODO: Set Ignore if issues persist and kustomization has already been patched
failurePolicy: Fail
matchPolicy: Equivalent
# NOTE: Expected behaviour is that flux-system contains required secrets which can be directly added and not required to be patched.
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: NotIn
values: ["flux-system"]
objectSelector: {}
reinvocationPolicy: Never
clientConfig:
service:
name: kustomize-mutating-webhook
namespace: flux-system
path: /mutate
port: 8443
rules:
- apiGroups: ["kustomize.toolkit.fluxcd.io"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["kustomizations"]
scope: "*"
sideEffects: None
timeoutSeconds: 30
Loading
Loading