Skip to content

Commit 423ebce

Browse files
committed
Initial implementation
Signed-off-by: Magnus Ullberg <magnus@ullberg.us>
1 parent ab59c02 commit 423ebce

22 files changed

Lines changed: 3322 additions & 5 deletions

.devcontainer/Dockerfile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ RUN OC_VERSION=latest \
2121
&& rm /tmp/openshift-client-linux.tar.gz \
2222
&& chmod +x /usr/local/bin/oc /usr/local/bin/kubectl
2323

24+
# Install OpenShift CLI (oc)
25+
RUN OC_VERSION=latest \
26+
&& curl -sSL -o /tmp/openshift-client-linux.tar.gz "https://mirror.openshift.com/pub/openshift-v4/clients/ocp/stable/openshift-client-linux.tar.gz" \
27+
&& tar -xzf /tmp/openshift-client-linux.tar.gz -C /usr/local/bin oc kubectl \
28+
&& rm /tmp/openshift-client-linux.tar.gz \
29+
&& chmod +x /usr/local/bin/oc /usr/local/bin/kubectl
30+
2431
CMD sleep infinity
2532

2633
ENTRYPOINT []

.devcontainer/post-install.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,15 @@ if ! command -v golangci-lint >/dev/null 2>&1; then
1717
rm -rf /tmp/golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64* golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz || true
1818
echo "golangci-lint installed"
1919
fi
20+
21+
# Install OpenShift CLI if not present
22+
if ! command -v oc >/dev/null 2>&1; then
23+
echo "oc not found, installing OpenShift CLI"
24+
apt-get update || true
25+
apt-get install -y --no-install-recommends ca-certificates curl || true
26+
curl -sSL -o /tmp/openshift-client-linux.tar.gz "https://mirror.openshift.com/pub/openshift-v4/clients/ocp/stable/openshift-client-linux.tar.gz" || true
27+
tar -xzf /tmp/openshift-client-linux.tar.gz -C /usr/local/bin oc kubectl || true
28+
rm /tmp/openshift-client-linux.tar.gz || true
29+
chmod +x /usr/local/bin/oc /usr/local/bin/kubectl || true
30+
echo "oc installed"
31+
fi

.github/workflows/release.yml

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ on:
1414
env:
1515
REGISTRY: ghcr.io
1616
CONTROLLER_IMAGE: ghcr.io/${{ github.repository }}
17+
WEBHOOK_IMAGE: ghcr.io/${{ github.repository_owner }}/object-lease-webhook
1718
OPERATOR_IMAGE: ghcr.io/${{ github.repository_owner }}/object-lease-operator-controller
1819
PLUGIN_IMAGE: ghcr.io/${{ github.repository_owner }}/object-lease-console-plugin
1920
BUNDLE_IMAGE: ghcr.io/${{ github.repository_owner }}/object-lease-operator-bundle
@@ -89,6 +90,50 @@ jobs:
8990
${{ env.CONTROLLER_IMAGE }}:${{ needs.prepare.outputs.version_tag }} \
9091
--tag ${{ env.CONTROLLER_IMAGE }}:latest
9192
93+
build-webhook:
94+
name: Build and Push Webhook
95+
needs: prepare
96+
runs-on: ubuntu-latest
97+
permissions:
98+
contents: write
99+
packages: write
100+
steps:
101+
- name: Checkout
102+
uses: actions/checkout@v4
103+
104+
- name: Set up Go
105+
uses: actions/setup-go@v5
106+
with:
107+
go-version-file: 'go.mod'
108+
109+
- name: Run Tests
110+
run: make test
111+
112+
- name: Set up QEMU
113+
uses: docker/setup-qemu-action@v3
114+
115+
- name: Set up Docker Buildx
116+
uses: docker/setup-buildx-action@v3
117+
118+
- name: Login to GitHub Container Registry
119+
uses: docker/login-action@v3
120+
with:
121+
registry: ${{ env.REGISTRY }}
122+
username: ${{ github.actor }}
123+
password: ${{ secrets.GITHUB_TOKEN }}
124+
125+
- name: Build and Push Webhook Image
126+
run: |
127+
make webhook-buildx \
128+
WEBHOOK_IMG=${{ env.WEBHOOK_IMAGE }}:${{ needs.prepare.outputs.version_tag }} \
129+
PLATFORMS=${{ env.PLATFORMS }}
130+
131+
- name: Tag Latest
132+
run: |
133+
docker buildx imagetools create \
134+
${{ env.WEBHOOK_IMAGE }}:${{ needs.prepare.outputs.version_tag }} \
135+
--tag ${{ env.WEBHOOK_IMAGE }}:latest
136+
92137
build-plugin:
93138
name: Build and Push Console Plugin
94139
needs: prepare
@@ -140,7 +185,7 @@ jobs:
140185
141186
build-operator:
142187
name: Build and Push Operator
143-
needs: [prepare, build-controller]
188+
needs: [prepare, build-controller, build-webhook]
144189
runs-on: ubuntu-latest
145190
permissions:
146191
contents: write
@@ -241,7 +286,7 @@ jobs:
241286
242287
create-release:
243288
name: Create GitHub Release
244-
needs: [prepare, build-controller, build-plugin, build-operator, build-bundle, build-catalog]
289+
needs: [prepare, build-controller, build-webhook, build-plugin, build-operator, build-bundle, build-catalog]
245290
runs-on: ubuntu-latest
246291
permissions:
247292
contents: write
@@ -261,6 +306,7 @@ jobs:
261306
### Container Images
262307
263308
- **Controller**: `${{ env.CONTROLLER_IMAGE }}:${{ needs.prepare.outputs.version_tag }}`
309+
- **Webhook**: `${{ env.WEBHOOK_IMAGE }}:${{ needs.prepare.outputs.version_tag }}`
264310
- **Console Plugin**: `${{ env.PLUGIN_IMAGE }}:${{ needs.prepare.outputs.version_tag }}`
265311
- **Operator**: `${{ env.OPERATOR_IMAGE }}:${{ needs.prepare.outputs.version_tag }}`
266312
- **Bundle**: `${{ env.BUNDLE_IMAGE }}:${{ needs.prepare.outputs.version_tag }}`

Dockerfile.webhook

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Build stage
2+
FROM golang:1.23 AS builder
3+
4+
WORKDIR /workspace
5+
6+
# Copy go mod files
7+
COPY go.mod go.sum ./
8+
RUN go mod download
9+
10+
# Copy source code
11+
COPY cmd/ cmd/
12+
COPY pkg/ pkg/
13+
14+
# Build the webhook binary
15+
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o webhook cmd/webhook/main.go
16+
17+
# Runtime stage
18+
FROM gcr.io/distroless/static:nonroot
19+
20+
WORKDIR /
21+
22+
COPY --from=builder /workspace/webhook .
23+
24+
USER 65532:65532
25+
26+
ENTRYPOINT ["/webhook"]

Makefile

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
# Image configurations
66
IMG ?= ghcr.io/ullbergm/object-lease-controller:v1.0.0
7+
WEBHOOK_IMG ?= ghcr.io/ullbergm/object-lease-webhook:v1.0.0
78
PLUGIN_IMG ?= ghcr.io/ullbergm/object-lease-console-plugin:v1.0.0
89

910
# Build configurations
@@ -73,6 +74,13 @@ fuzz:
7374
build: tidy fmt vet test ## Build the binary
7475
go build -o $(BUILD_DIR)/$(BINARY_NAME) ./cmd/main.go
7576

77+
.PHONY: build-webhook
78+
build-webhook: tidy fmt vet test ## Build the webhook binary
79+
go build -o $(BUILD_DIR)/lease-webhook ./cmd/webhook/main.go
80+
81+
.PHONY: build-all
82+
build-all: build build-webhook ## Build all binaries
83+
7684
.PHONY: run
7785
run: build ## Run the application locally
7886
./$(BUILD_DIR)/$(BINARY_NAME) \
@@ -85,6 +93,10 @@ run: build ## Run the application locally
8593
# -opt-in-label-value true \
8694
-zap-log-level debug
8795

96+
.PHONY: run-webhook
97+
run-webhook: build-webhook ## Run the webhook server locally (insecure mode)
98+
./$(BUILD_DIR)/lease-webhook -insecure -webhook-port 8443
99+
88100
# =============================================================================
89101
# Docker Targets - Main Controller
90102
# =============================================================================
@@ -104,6 +116,25 @@ docker-buildx: ## Build and push multi-platform Docker image for main controller
104116
- $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag $(IMG) -f Dockerfile .
105117
- $(CONTAINER_TOOL) buildx rm project-v3-builder
106118

119+
# =============================================================================
120+
# Docker Targets - Webhook
121+
# =============================================================================
122+
123+
.PHONY: webhook-build
124+
webhook-build: ## Build Docker image for webhook
125+
$(CONTAINER_TOOL) build -t $(WEBHOOK_IMG) -f Dockerfile.webhook .
126+
127+
.PHONY: webhook-push
128+
webhook-push: webhook-build ## Build and push Docker image for webhook
129+
$(CONTAINER_TOOL) push $(WEBHOOK_IMG)
130+
131+
.PHONY: webhook-buildx
132+
webhook-buildx: ## Build and push multi-platform Docker image for webhook
133+
- $(CONTAINER_TOOL) buildx create --name project-v3-builder
134+
$(CONTAINER_TOOL) buildx use project-v3-builder
135+
- $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag $(WEBHOOK_IMG) -f Dockerfile.webhook .
136+
- $(CONTAINER_TOOL) buildx rm project-v3-builder
137+
107138
# =============================================================================
108139
# Docker Targets - Console Plugin
109140
# =============================================================================
@@ -138,5 +169,6 @@ deploy-operator-and-plugin: ## Deploy operator and plugin to Kubernetes
138169
.PHONY: push-all
139170
push-all: ## Push all images and operator bundles
140171
$(MAKE) docker-push
172+
$(MAKE) webhook-push
141173
$(MAKE) plugin-push
142174
cd object-lease-operator && $(MAKE) docker-build docker-push bundle-push catalog-push && cd ..

README.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ This project implements a Kubernetes operator that allows you to specify a TTL (
1515
- Dynamically deploys a controller for each configured GVK.
1616
- Controllers are only managing one GVK each, increasing scalability.
1717
- Leader election support for high availability.
18+
- **Admission webhook** for validating TTL annotation format (optional, per-GVK configuration).
1819
- Custom cleanup scripts via Kubernetes Jobs before object deletion.
1920

2021
## Architecture
@@ -49,11 +50,14 @@ kind: LeaseController
4950
metadata:
5051
name: deployment-controller
5152
spec:
52-
group: ""
53+
group: "apps"
54+
version: "v1"
5355
kind:
5456
singular: "Deployment"
55-
plural: "Deployments"
56-
version: "v1"
57+
plural: "deployments"
58+
webhook:
59+
enabled: true # Optional: Enable admission webhook validation
60+
failurePolicy: Ignore # Optional: "Ignore" (default) or "Fail"
5761
```
5862
5963
### Object annotation
@@ -289,3 +293,14 @@ Details on enabling and namespace participation are documented by Red Hat:
289293
- [Red Hat Documentation](https://docs.openshift.com/container-platform/latest/monitoring/enabling-monitoring-for-user-defined-projects.html)
290294

291295
That is all you need. Your counters and histogram are already registered on the default registry, so Prometheus will scrape them from `/metrics` on the ServiceMonitor endpoint.
296+
297+
## Admission Webhook
298+
299+
The operator includes an optional **validating admission webhook** that validates TTL annotation format before objects are created or updated. This prevents invalid lease configurations from being applied.
300+
301+
See [docs/webhook.md](docs/webhook.md) for detailed documentation on:
302+
- Architecture and how it works
303+
- Configuration options
304+
- Certificate management
305+
- Troubleshooting
306+
- Security considerations

0 commit comments

Comments
 (0)