Skip to content
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
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: CI

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
push:
branches: [ main ]

jobs:
build-and-test:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.24.4'

- name: Cache Go build
uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-

- name: Lint
run: |
make golangci-lint
make lint

- name: Unit tests
run: |
make test


2 changes: 2 additions & 0 deletions .github/workflows/smoke-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ name: Smoke Test

on:
push:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
workflow_dispatch:

env:
Expand Down
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ linters:
enable:
- dupl
- errcheck
- exportloopref
- copyloopvar
- ginkgolinter
- goconst
- gocyclo
Expand Down
35 changes: 31 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]


## [v0.5.0] - 2025-10-26

### Added
- Webhooks/Certificates: Align Admission/Conversion configuration with Kubebuilder best practices; resolve Service references via kustomize nameReference/namespace mapping.
- Add name-merge patches to `capt-*-webhook-configuration` to enforce `clientConfig.service = capt-webhook-service/capt-system`.
- Standardize CA injection into MWC/VWC and CRD conversion via cert-manager `inject-ca-from` annotations.
- Add `make wait-ca` to wait until caBundle injection completes for both Admission and CRD conversion.
- e2e: Add readiness waits before admission (Deployment rollout, TLS Secret, Service Endpoints, short settle time).
- Samples: Update to CAPI v1beta2 (Cluster `spec.topology.classRef.name`, ClusterClass `*.templateRef`, KubeadmConfigTemplate v1beta2 minimal no-op).
- API: Expose Terraform workspace name for observability via `status.workspaceTemplateStatus.workspaceName` on `CAPTControlPlane` and `CAPTCluster`.

### Changed
- Conversion Webhook: Remove manual `/convert` registration; rely on controller-runtime auto-registration.
- 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`.
- Tests: Updated unit tests to assert deterministic naming and status-based introspection rather than spec mutation side-effects.

### Deprecated
- 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.

### Fixed
- Resolve `unknown authority` (missing CA) and connection failures caused by mismatched webhook Service name/namespace.

### Notes
- Known caveat: Plan to strengthen immutability validation on `CAPTControlPlane` (forbid updates to immutable fields).
- Webhooks: Strengthening immutability validation for `CAPTControlPlane` on `v1beta2` is planned as a follow-up to cover all served versions consistently.

## [v0.4.0] - 2025-10-24

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

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

### Notes
- 次期 `v0.5.0` で `v1beta2` 互換を実装予定
- Heads up: v0.5.0 will implement v1beta2 compatibility

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

Expand Down
30 changes: 23 additions & 7 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,29 @@ Before you begin, ensure you have:
kubectl cluster-info
```

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

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

### Step 3: Install Cluster API
### Step 3: Install Cluster API (management cluster v1beta2)

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

2. Initialize Cluster API:
2. Initialize Cluster API (Topology requires kubeadm bootstrap):
```bash
clusterctl init
clusterctl init --core cluster-api --bootstrap kubeadm
```

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

### Step 3: Install CAPT
### Step 4: Install CAPT (with automatic CA injection)

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

3. If you build from source via kustomize (recommended one-shot bootstrap is `make setup`):
```bash
# Option A: one-shot bootstrap for local kind env
make setup

# Option B: deploy only CAPT manifests with kustomize
# (Issuer/Certificate are in config/certmanager; CA injection for Webhooks/CRDs
# is enabled via cert-manager annotations in config/default/kustomization.yaml)
make deploy
```

2. Verify the installation:
```bash
# Check controller pod
Expand Down
81 changes: 76 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ ifneq ($(wildcard VERSION),)
VERSION := $(shell sed -n 's/^VERSION *= *//p' VERSION)
endif
IMG ?= ghcr.io/appthrust/capt:v$(VERSION)
# Default image used by `make setup` for local dev. Override with SETUP_IMG=<image>.
SETUP_IMG ?= ghcr.io/appthrust/capt:dev
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.31.0

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

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

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

##@ Setup

.PHONY: setup-v1beta1
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`.

# Ideal, one-shot bootstrap that avoids pre-installing legacy v1beta1 CRDs
# Flow:
# 1) kind cluster (fresh)
# 2) cert-manager
# 3) clusterctl init (CAPI core + kubeadm)
# 4) apply CAPT control-plane/infrastructure CRDs
# 5) install Crossplane and provider-terraform
# 6) deploy CAPT (manager + webhooks + certs)
# 7) wait for CA injection (fallback to manual inject if needed)
.PHONY: setup
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.
KIND_NAME ?= capt
setup: ## One-shot bootstrap (v1beta2-compatible): kind, cert-manager, clusterctl, Crossplane, CAPT, CA
$(MAKE) kind-recreate
$(MAKE) setup-cert-manager
$(MAKE) setup-capi
$(MAKE) setup-crossplane
$(MAKE) setup-provider-terraform
# Build local manager image and load into kind
$(MAKE) docker-build IMG=$(SETUP_IMG)
- kind load docker-image $(SETUP_IMG) --name $(KIND_NAME) || { \
$(CONTAINER_TOOL) save $(SETUP_IMG) | kind load image-archive /dev/stdin --name $(KIND_NAME); \
}
# Deploy CAPT using the locally built image
$(MAKE) deploy IMG=$(SETUP_IMG)
# Wait for CA injection in webhooks & CRDs
$(MAKE) wait-ca

.PHONY: setup-ideal
setup-ideal: setup ## Alias of `setup`

.PHONY: kind-recreate
kind-recreate: ## Recreate kind cluster named $(KIND_NAME)
- kind delete cluster --name $(KIND_NAME)
kind create cluster --name $(KIND_NAME)

.PHONY: wait-ca
wait-ca: ## Wait for cainjector to inject caBundle (verification only; no manual patch)
@echo "Waiting for webhook TLS secret..."
@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
@echo "Waiting for cainjector to inject caBundle into admission webhooks..."
@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
@echo "Waiting for CRD conversion caBundle..."
@for crd in \
captclusters.infrastructure.cluster.x-k8s.io \
captclustertemplates.infrastructure.cluster.x-k8s.io \
captmachines.infrastructure.cluster.x-k8s.io \
captmachinesets.infrastructure.cluster.x-k8s.io \
captmachinetemplates.infrastructure.cluster.x-k8s.io \
captmachinedeployments.infrastructure.cluster.x-k8s.io \
workspacetemplates.infrastructure.cluster.x-k8s.io \
workspacetemplateapplies.infrastructure.cluster.x-k8s.io \
captcontrolplanes.controlplane.cluster.x-k8s.io \
captcontrolplanetemplates.controlplane.cluster.x-k8s.io; do \
i=0; \
until [ $$(kubectl get crd $$crd -o jsonpath='{.spec.conversion.webhook.clientConfig.caBundle}' | wc -c) -gt 1 ]; do \
i=$$((i+1)); \
if [ $$i -gt 60 ]; then echo "error: cainjector did not inject caBundle to CRD $$crd in time"; exit 1; fi; \
sleep 2; \
done; \
done
@echo "CA injection ensured."

.PHONY: all-in-one-noop
all-in-one-noop: setup run-noop-e2e ## Bootstrap and run no-op ClusterClass e2e

.PHONY: run-noop-e2e
run-noop-e2e: ## Run the no-op ClusterClass e2e script
./test/e2e/scripts/no-op-clusterclass.sh

.PHONY: setup-capi-crds
setup-capi-crds: ## Install only Cluster API Core CRDs (avoid conflicting tf.upbound.io CRDs).
Expand Down Expand Up @@ -289,7 +360,7 @@ HELM_BIN ?= $(LOCALBIN)/helm
KUSTOMIZE_VERSION ?= v5.4.3
CONTROLLER_TOOLS_VERSION ?= v0.16.1
ENVTEST_VERSION ?= release-0.19
GOLANGCI_LINT_VERSION ?= v1.59.1
GOLANGCI_LINT_VERSION ?= v1.63.4
CERT_MANAGER_VERSION ?= v1.16.1
CROSSPLANE_VERSION ?= v1.16.0

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

# clusterctl prebuilt binary (ensures GitVersion is embedded)
CLUSTERCTL_VERSION ?= v1.10.7
CLUSTERCTL_VERSION ?= v1.11.2
UNAME_M := $(shell uname -m)
ifeq ($(UNAME_M),x86_64)
CLUSTERCTL_ARCH := amd64
Expand Down
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,15 @@ sequenceDiagram
CA->>CA: Apply Infrastructure
```

### Topology Immutability and Naming

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`.

- Control plane WTA: `<captcontrolplane-name>-eks-controlplane-apply`
- Cluster VPC WTA: `<captcluster-name>-vpc`

Consumers should rely on status for observability rather than controller-driven spec mutations.

Each component is managed independently through WorkspaceTemplates and can be templated using ClusterClass. The controllers automatically manage WorkspaceTemplateApply resources for infrastructure provisioning.

## Key Benefits
Expand Down Expand Up @@ -203,7 +212,7 @@ kubectl apply -f cluster.yaml

For detailed clusterctl integration guide, see [docs/clusterctl-integration.md](docs/clusterctl-integration.md).

**Note:** It is recommended to use `clusterctl` version `v1.5.x` or newer to ensure compatibility with the `ClusterTopology` feature gate.
**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`).

## Quick Start Guide

Expand Down
Loading
Loading