Skip to content

Commit 83afc2a

Browse files
authored
Merge pull request #16 from appthrust/0.5.0
0.5.0
2 parents dab4945 + 96edc95 commit 83afc2a

145 files changed

Lines changed: 9088 additions & 730 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened, ready_for_review]
6+
push:
7+
branches: [ main ]
8+
9+
jobs:
10+
build-and-test:
11+
runs-on: ubuntu-latest
12+
timeout-minutes: 30
13+
steps:
14+
- name: Checkout repository
15+
uses: actions/checkout@v4
16+
17+
- name: Set up Go
18+
uses: actions/setup-go@v4
19+
with:
20+
go-version: '1.24.4'
21+
22+
- name: Cache Go build
23+
uses: actions/cache@v4
24+
with:
25+
path: |
26+
~/.cache/go-build
27+
~/go/pkg/mod
28+
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
29+
restore-keys: |
30+
${{ runner.os }}-go-
31+
32+
- name: Lint
33+
run: |
34+
make golangci-lint
35+
make lint
36+
37+
- name: Unit tests
38+
run: |
39+
make test
40+
41+

.github/workflows/smoke-test.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ name: Smoke Test
22

33
on:
44
push:
5+
pull_request:
6+
types: [opened, synchronize, reopened, ready_for_review]
57
workflow_dispatch:
68

79
env:

.golangci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ linters:
2121
enable:
2222
- dupl
2323
- errcheck
24-
- exportloopref
24+
- copyloopvar
2525
- ginkgolinter
2626
- goconst
2727
- gocyclo

CHANGELOG.md

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
11+
## [v0.5.0] - 2025-10-26
12+
13+
### Added
14+
- Webhooks/Certificates: Align Admission/Conversion configuration with Kubebuilder best practices; resolve Service references via kustomize nameReference/namespace mapping.
15+
- Add name-merge patches to `capt-*-webhook-configuration` to enforce `clientConfig.service = capt-webhook-service/capt-system`.
16+
- Standardize CA injection into MWC/VWC and CRD conversion via cert-manager `inject-ca-from` annotations.
17+
- Add `make wait-ca` to wait until caBundle injection completes for both Admission and CRD conversion.
18+
- e2e: Add readiness waits before admission (Deployment rollout, TLS Secret, Service Endpoints, short settle time).
19+
- Samples: Update to CAPI v1beta2 (Cluster `spec.topology.classRef.name`, ClusterClass `*.templateRef`, KubeadmConfigTemplate v1beta2 minimal no-op).
20+
- API: Expose Terraform workspace name for observability via `status.workspaceTemplateStatus.workspaceName` on `CAPTControlPlane` and `CAPTCluster`.
21+
22+
### Changed
23+
- Conversion Webhook: Remove manual `/convert` registration; rely on controller-runtime auto-registration.
24+
- Controllers: Avoid mutating `spec.workspaceTemplateApplyName` when resources are managed by ClusterTopology. A deterministic name is resolved internally and the resulting workspace name is surfaced in `status.workspaceTemplateStatus.workspaceName`.
25+
- Tests: Updated unit tests to assert deterministic naming and status-based introspection rather than spec mutation side-effects.
26+
27+
### Deprecated
28+
- Field usage: Reliance on `spec.workspaceTemplateApplyName` by controllers under ClusterTopology is deprecated. The field remains for compatibility but is not written by controllers and may be removed in a future release.
29+
30+
### Fixed
31+
- Resolve `unknown authority` (missing CA) and connection failures caused by mismatched webhook Service name/namespace.
32+
33+
### Notes
34+
- Known caveat: Plan to strengthen immutability validation on `CAPTControlPlane` (forbid updates to immutable fields).
35+
- Webhooks: Strengthening immutability validation for `CAPTControlPlane` on `v1beta2` is planned as a follow-up to cover all served versions consistently.
36+
1037
## [v0.4.0] - 2025-10-24
1138

1239
### Added
13-
- ClusterClass 対応を追加 (clusterctl の ClusterClass 対応フローに適合)
40+
- Add ClusterClass support (aligned with the clusterctl ClusterClass flow)
1441

1542
### Changed
16-
- マニフェストの CAPI contract ラベルの明確化と整理 (v1beta1)
17-
- `config/*/kustomization.yaml` のイメージタグを `v0.4.0` に更新
43+
- Clarify and tidy up CAPI contract labels in manifests (v1beta1)
44+
- Update image tags in `config/*/kustomization.yaml` to `v0.4.0`
1845

1946
### Notes
20-
- 次期 `v0.5.0``v1beta2` 互換を実装予定
47+
- Heads up: v0.5.0 will implement v1beta2 compatibility
2148

2249
## [v0.2.1] - 2024-01-25
2350

INSTALL.md

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,29 @@ Before you begin, ensure you have:
2424
kubectl cluster-info
2525
```
2626

27-
### Step 2: Install cert-manager
27+
### Step 2: Install cert-manager (required for automatic CA injection)
2828

2929
```
3030
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.1/cert-manager.yaml
3131
```
32+
Wait for CRDs to be established:
33+
```
34+
kubectl wait --for=condition=Established crd/certificates.cert-manager.io --timeout=300s || true
35+
```
3236

33-
### Step 3: Install Cluster API
37+
### Step 3: Install Cluster API (management cluster v1beta2)
3438

35-
1. Install clusterctl:
39+
1. Install clusterctl (v1.11.x):
3640
```bash
37-
curl -L https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.5.1/clusterctl-linux-amd64 -o clusterctl
41+
# Match the version used by the Makefile (CLUSTERCTL_VERSION)
42+
curl -L https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.11.2/clusterctl-linux-amd64 -o clusterctl
3843
chmod +x clusterctl
3944
sudo mv clusterctl /usr/local/bin/
4045
```
4146

42-
2. Initialize Cluster API:
47+
2. Initialize Cluster API (Topology requires kubeadm bootstrap):
4348
```bash
44-
clusterctl init
49+
clusterctl init --core cluster-api --bootstrap kubeadm
4550
```
4651

4752
3. Verify the installation:
@@ -55,7 +60,7 @@ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/
5560
kubectl get crds | grep cluster.x-k8s.io
5661
```
5762

58-
### Step 3: Install CAPT
63+
### Step 4: Install CAPT (with automatic CA injection)
5964

6065
1. Download and apply the installer:
6166
```bash
@@ -69,6 +74,17 @@ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/
6974
kubectl apply -f capt.yaml
7075
```
7176

77+
3. If you build from source via kustomize (recommended one-shot bootstrap is `make setup`):
78+
```bash
79+
# Option A: one-shot bootstrap for local kind env
80+
make setup
81+
82+
# Option B: deploy only CAPT manifests with kustomize
83+
# (Issuer/Certificate are in config/certmanager; CA injection for Webhooks/CRDs
84+
# is enabled via cert-manager annotations in config/default/kustomization.yaml)
85+
make deploy
86+
```
87+
7288
2. Verify the installation:
7389
```bash
7490
# Check controller pod

Makefile

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ ifneq ($(wildcard VERSION),)
55
VERSION := $(shell sed -n 's/^VERSION *= *//p' VERSION)
66
endif
77
IMG ?= ghcr.io/appthrust/capt:v$(VERSION)
8+
# Default image used by `make setup` for local dev. Override with SETUP_IMG=<image>.
9+
SETUP_IMG ?= ghcr.io/appthrust/capt:dev
810
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
911
ENVTEST_K8S_VERSION = 1.31.0
1012

@@ -56,7 +58,7 @@ manifests: controller-gen
5658
$(CONTROLLER_GEN) crd:generateEmbeddedObjectMeta=true webhook paths="./api/controlplane/..." output:crd:artifacts:config=config/clusterapi/controlplane/bases
5759
mkdir -p config/clusterapi/infrastructure/bases
5860
$(CONTROLLER_GEN) rbac:roleName=manager-role-infrastructure paths="./internal/controller" output:stdout > config/rbac/infrastructure-role.yaml
59-
$(CONTROLLER_GEN) crd:generateEmbeddedObjectMeta=true webhook paths="./api/v1beta1/..." output:crd:artifacts:config=config/clusterapi/infrastructure/bases
61+
$(CONTROLLER_GEN) crd:generateEmbeddedObjectMeta=true webhook paths="./api/..." output:crd:artifacts:config=config/clusterapi/infrastructure/bases
6062

6163
.PHONY: clusterapi-manifests
6264
clusterapi-manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
@@ -66,7 +68,7 @@ clusterapi-manifests: controller-gen ## Generate WebhookConfiguration, ClusterRo
6668
$(CONTROLLER_GEN) crd:generateEmbeddedObjectMeta=true webhook paths="./api/controlplane/..." output:crd:artifacts:config=config/clusterapi/controlplane/bases
6769
mkdir -p config/clusterapi/infrastructure/bases
6870
$(CONTROLLER_GEN) rbac:roleName=manager-role-infrastructure paths="./internal/controller" output:stdout > config/rbac/infrastructure-role.yaml
69-
$(CONTROLLER_GEN) crd:generateEmbeddedObjectMeta=true webhook paths="./api/v1beta1/..." output:crd:artifacts:config=config/clusterapi/infrastructure/bases
71+
$(CONTROLLER_GEN) crd:generateEmbeddedObjectMeta=true webhook paths="./api/..." output:crd:artifacts:config=config/clusterapi/infrastructure/bases
7072

7173
.PHONY: clusterctl-setup
7274
clusterctl-setup: clusterapi-manifests kustomize $(KUSTOMIZE_PREREQ) ## Build components and create local config for clusterctl testing.
@@ -184,8 +186,77 @@ undeploy: $(KUSTOMIZE_PREREQ) ## Undeploy controller from the K8s cluster specif
184186

185187
##@ Setup
186188

189+
.PHONY: setup-v1beta1
190+
setup-v1beta1: setup-capi-crds setup-cert-manager setup-capi setup-crossplane setup-provider-terraform setup-webhook-certs ## Legacy v1beta1 bootstrap (pre-installs v1beta1 CRDs). Prefer `make setup`.
191+
192+
# Ideal, one-shot bootstrap that avoids pre-installing legacy v1beta1 CRDs
193+
# Flow:
194+
# 1) kind cluster (fresh)
195+
# 2) cert-manager
196+
# 3) clusterctl init (CAPI core + kubeadm)
197+
# 4) apply CAPT control-plane/infrastructure CRDs
198+
# 5) install Crossplane and provider-terraform
199+
# 6) deploy CAPT (manager + webhooks + certs)
200+
# 7) wait for CA injection (fallback to manual inject if needed)
187201
.PHONY: setup
188-
setup: setup-capi-crds setup-cert-manager setup-capi setup-crossplane setup-provider-terraform setup-webhook-certs ## Prepare local dev cluster with CAPI (clusterctl init --bootstrap kubeadm), cert-manager, Crossplane, provider-terraform, webhook certs.
202+
KIND_NAME ?= capt
203+
setup: ## One-shot bootstrap (v1beta2-compatible): kind, cert-manager, clusterctl, Crossplane, CAPT, CA
204+
$(MAKE) kind-recreate
205+
$(MAKE) setup-cert-manager
206+
$(MAKE) setup-capi
207+
$(MAKE) setup-crossplane
208+
$(MAKE) setup-provider-terraform
209+
# Build local manager image and load into kind
210+
$(MAKE) docker-build IMG=$(SETUP_IMG)
211+
- kind load docker-image $(SETUP_IMG) --name $(KIND_NAME) || { \
212+
$(CONTAINER_TOOL) save $(SETUP_IMG) | kind load image-archive /dev/stdin --name $(KIND_NAME); \
213+
}
214+
# Deploy CAPT using the locally built image
215+
$(MAKE) deploy IMG=$(SETUP_IMG)
216+
# Wait for CA injection in webhooks & CRDs
217+
$(MAKE) wait-ca
218+
219+
.PHONY: setup-ideal
220+
setup-ideal: setup ## Alias of `setup`
221+
222+
.PHONY: kind-recreate
223+
kind-recreate: ## Recreate kind cluster named $(KIND_NAME)
224+
- kind delete cluster --name $(KIND_NAME)
225+
kind create cluster --name $(KIND_NAME)
226+
227+
.PHONY: wait-ca
228+
wait-ca: ## Wait for cainjector to inject caBundle (verification only; no manual patch)
229+
@echo "Waiting for webhook TLS secret..."
230+
@i=0; until kubectl -n capt-system get secret webhook-server-cert >/dev/null 2>&1; do i=$$((i+1)); if [ $$i -gt 120 ]; then echo "timeout waiting for webhook-server-cert"; exit 1; fi; sleep 2; done
231+
@echo "Waiting for cainjector to inject caBundle into admission webhooks..."
232+
@i=0; until [ $$(kubectl get mutatingwebhookconfiguration capt-mutating-webhook-configuration -o jsonpath='{.webhooks[*].clientConfig.caBundle}' | wc -c) -gt 1 ]; do i=$$((i+1)); if [ $$i -gt 60 ]; then echo "error: cainjector did not inject caBundle in time"; exit 1; fi; sleep 2; done
233+
@echo "Waiting for CRD conversion caBundle..."
234+
@for crd in \
235+
captclusters.infrastructure.cluster.x-k8s.io \
236+
captclustertemplates.infrastructure.cluster.x-k8s.io \
237+
captmachines.infrastructure.cluster.x-k8s.io \
238+
captmachinesets.infrastructure.cluster.x-k8s.io \
239+
captmachinetemplates.infrastructure.cluster.x-k8s.io \
240+
captmachinedeployments.infrastructure.cluster.x-k8s.io \
241+
workspacetemplates.infrastructure.cluster.x-k8s.io \
242+
workspacetemplateapplies.infrastructure.cluster.x-k8s.io \
243+
captcontrolplanes.controlplane.cluster.x-k8s.io \
244+
captcontrolplanetemplates.controlplane.cluster.x-k8s.io; do \
245+
i=0; \
246+
until [ $$(kubectl get crd $$crd -o jsonpath='{.spec.conversion.webhook.clientConfig.caBundle}' | wc -c) -gt 1 ]; do \
247+
i=$$((i+1)); \
248+
if [ $$i -gt 60 ]; then echo "error: cainjector did not inject caBundle to CRD $$crd in time"; exit 1; fi; \
249+
sleep 2; \
250+
done; \
251+
done
252+
@echo "CA injection ensured."
253+
254+
.PHONY: all-in-one-noop
255+
all-in-one-noop: setup run-noop-e2e ## Bootstrap and run no-op ClusterClass e2e
256+
257+
.PHONY: run-noop-e2e
258+
run-noop-e2e: ## Run the no-op ClusterClass e2e script
259+
./test/e2e/scripts/no-op-clusterclass.sh
189260

190261
.PHONY: setup-capi-crds
191262
setup-capi-crds: ## Install only Cluster API Core CRDs (avoid conflicting tf.upbound.io CRDs).
@@ -289,7 +360,7 @@ HELM_BIN ?= $(LOCALBIN)/helm
289360
KUSTOMIZE_VERSION ?= v5.4.3
290361
CONTROLLER_TOOLS_VERSION ?= v0.16.1
291362
ENVTEST_VERSION ?= release-0.19
292-
GOLANGCI_LINT_VERSION ?= v1.59.1
363+
GOLANGCI_LINT_VERSION ?= v1.63.4
293364
CERT_MANAGER_VERSION ?= v1.16.1
294365
CROSSPLANE_VERSION ?= v1.16.0
295366

@@ -381,7 +452,7 @@ clusterctl: $(LOCALBIN) ## Download clusterctl locally (prebuilt release with Gi
381452
CLUSTERCTL_BIN ?= $(LOCALBIN)/clusterctl
382453

383454
# clusterctl prebuilt binary (ensures GitVersion is embedded)
384-
CLUSTERCTL_VERSION ?= v1.10.7
455+
CLUSTERCTL_VERSION ?= v1.11.2
385456
UNAME_M := $(shell uname -m)
386457
ifeq ($(UNAME_M),x86_64)
387458
CLUSTERCTL_ARCH := amd64

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,15 @@ sequenceDiagram
9494
CA->>CA: Apply Infrastructure
9595
```
9696

97+
### Topology Immutability and Naming
98+
99+
When using ClusterTopology, CAPT avoids mutating `spec` fields post-creation. Controllers resolve a deterministic name for `WorkspaceTemplateApply` without writing it back to `spec` and surface the actual Terraform workspace via `status.workspaceTemplateStatus.workspaceName`.
100+
101+
- Control plane WTA: `<captcontrolplane-name>-eks-controlplane-apply`
102+
- Cluster VPC WTA: `<captcluster-name>-vpc`
103+
104+
Consumers should rely on status for observability rather than controller-driven spec mutations.
105+
97106
Each component is managed independently through WorkspaceTemplates and can be templated using ClusterClass. The controllers automatically manage WorkspaceTemplateApply resources for infrastructure provisioning.
98107

99108
## Key Benefits
@@ -203,7 +212,7 @@ kubectl apply -f cluster.yaml
203212
204213
For detailed clusterctl integration guide, see [docs/clusterctl-integration.md](docs/clusterctl-integration.md).
205214

206-
**Note:** It is recommended to use `clusterctl` version `v1.5.x` or newer to ensure compatibility with the `ClusterTopology` feature gate.
215+
**Note:** It is recommended to use `clusterctl` version `v1.11.x` (management cluster v1beta2) to ensure compatibility with ClusterTopology and this repository's one-shot bootstrap (`make setup`).
207216

208217
## Quick Start Guide
209218

0 commit comments

Comments
 (0)