Skip to content

Commit 7bba449

Browse files
feat(server-side-apply/v1-alpha): Add plugin to scaffold APIs with Server-Side Apply
Adds optional server-side-apply plugin that scaffolds APIs with controllers using Server-Side Apply patterns. Plugin enables per-API choice between traditional Update and Server-Side Apply approaches. Genereted-by: Cursor/Claude
1 parent 1cba2d9 commit 7bba449

181 files changed

Lines changed: 12237 additions & 15 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/lint-sample.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ jobs:
2828
"testdata/project-v4-multigroup",
2929
"docs/book/src/cronjob-tutorial/testdata/project",
3030
"docs/book/src/getting-started/testdata/project",
31+
"docs/book/src/getting-started-ssa/testdata/project",
3132
"docs/book/src/multiversion-tutorial/testdata/project"
3233
]
3334
if: (github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository)

.github/workflows/test-book.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ on:
44
push:
55
paths:
66
- 'docs/book/src/getting-started/testdata/project/**'
7+
- 'docs/book/src/getting-started-ssa/testdata/project/**'
78
- 'docs/book/src/cronjob-tutorial/testdata/project/**'
89
- 'docs/book/src/multiversion-tutorial/testdata/project/**'
9-
- '.github/workflows/test-e2e-book.yml'
10+
- '.github/workflows/test-book.yml'
1011
pull_request:
1112
paths:
1213
- 'docs/book/src/getting-started/testdata/project/**'
14+
- 'docs/book/src/getting-started-ssa/testdata/project/**'
1315
- 'docs/book/src/cronjob-tutorial/testdata/project/**'
1416
- 'docs/book/src/multiversion-tutorial/testdata/project/**'
15-
- '.github/workflows/test-e2e-book.yml'
17+
- '.github/workflows/test-book.yml'
1618

1719
permissions: {}
1820

@@ -26,6 +28,7 @@ jobs:
2628
matrix:
2729
folder: [
2830
"docs/book/src/getting-started/testdata/project",
31+
"docs/book/src/getting-started-ssa/testdata/project",
2932
"docs/book/src/cronjob-tutorial/testdata/project",
3033
"docs/book/src/multiversion-tutorial/testdata/project"
3134
]

.github/workflows/test-helm-book.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ on:
55
paths:
66
- "docs/book/src/cronjob-tutorial/testdata/project/**"
77
- "docs/book/src/getting-started/testdata/project/**"
8+
- "docs/book/src/getting-started-ssa/testdata/project/**"
89
- "docs/book/src/multiversion-tutorial/testdata/project/**"
910
- ".github/workflows/test-helm-book.yml"
1011
pull_request:
1112
paths:
12-
- "docs/book/src/cronjob-tutorial/testdata/project/** "
13+
- "docs/book/src/cronjob-tutorial/testdata/project/**"
1314
- "docs/book/src/getting-started/testdata/project/**"
15+
- "docs/book/src/getting-started-ssa/testdata/project/**"
1416
- "docs/book/src/multiversion-tutorial/testdata/project/**"
1517
- ".github/workflows/test-helm-book.yml"
1618

@@ -26,6 +28,7 @@ jobs:
2628
matrix:
2729
folder: [
2830
"docs/book/src/getting-started/testdata/project",
31+
"docs/book/src/getting-started-ssa/testdata/project",
2932
"docs/book/src/cronjob-tutorial/testdata/project",
3033
"docs/book/src/multiversion-tutorial/testdata/project"
3134
]

docs/book/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@
122122
- [helm/v1-alpha](./plugins/available/helm-v1-alpha.md)
123123
- [helm/v2-alpha](./plugins/available/helm-v2-alpha.md)
124124
- [kustomize/v2](./plugins/available/kustomize-v2.md)
125+
- [ssa/v1-alpha](./plugins/available/ssa-v1-alpha.md)
125126
- [Extending](./plugins/extending.md)
126127
- [CLI and Plugins](./plugins/extending/extending_cli_features_and_plugins.md)
127128
- [External Plugins](./plugins/extending/external-plugins.md)

docs/book/src/cronjob-tutorial/testdata/project/AGENTS.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,18 @@ kubebuilder create api --group example.com --version v1alpha1 --kind Memcached \
9090

9191
Scaffolds good-practice code: reconciliation logic, status conditions, finalizers, RBAC. Use as a reference implementation.
9292

93+
### Server-Side Apply Plugin (scaffold controllers using Server-Side Apply)
94+
95+
Generate a controller that uses Server-Side Apply for declarative field management:
96+
97+
```bash
98+
# Example: API with Server-Side Apply
99+
kubebuilder create api --group <group> --version <version> --kind <Kind> \
100+
--plugins=ssa/v1-alpha
101+
```
102+
103+
Scaffolds a controller using Server-Side Apply patterns for conflict-free multi-actor field management.
104+
93105

94106
### Create Webhooks
95107
```bash

docs/book/src/faq.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,7 @@ However, note that this problem is fixed and will not occur if you deploy the pr
105105
106106
When attempting to run `make install` to apply the CRD manifests, the error `Too long: must have at most 262144 bytes may be encountered.` This error arises due to a size limit enforced by the Kubernetes API. Note that the `make install` target will apply the CRD manifest under `config/crd` using `kubectl apply -f -`. Therefore, when the apply command is used, the API annotates the object with the `last-applied-configuration` which contains the entire previous configuration. If this configuration is too large, it will exceed the allowed byte size. ([More info][k8s-obj-creation])
107107
108-
In ideal approach might use client-side apply might seem like the perfect solution since with the entire object configuration doesn't have to be stored as an annotation (last-applied-configuration) on the server. However, it's worth noting that as of now, it isn't supported by controller-gen or kubebuilder. For more on this, refer to: [Controller-tool-discussion][controller-tool-pr].
109-
110-
Therefore, you have a few options to workround this scenario such as:
108+
You have a few options to work around this scenario such as:
111109
112110
**By removing the descriptions from CRDs:**
113111
@@ -127,6 +125,14 @@ Your CRDs are generated using [controller-gen][controller-gen]. By using the opt
127125
128126
You can review the design of your APIs and see if it has not more specs than should be by hurting single responsibility principle for example. So that you might to re-design them.
129127
128+
**By using Server-Side Apply for CRD installation:**
129+
130+
Since this issue happens when CRDs are installed with client-side apply, another option is to install or update the CRDs with Server-Side Apply instead, for example by using `kubectl apply --server-side -f ...`. This avoids storing the full manifest in the `kubectl.kubernetes.io/last-applied-configuration` annotation and can prevent the size-limit failure.
131+
132+
If you are updating existing CRDs, `kubectl replace -f ...` may also help, depending on your workflow.
133+
134+
Note that the [Server-Side Apply plugin][server-side-apply-plugin] is related to how controllers manage resources at runtime. It can be useful when a controller shares ownership of fields with users or other controllers, but it does not change how `make install` applies CRDs.
135+
130136
## How can I validate and parse fields in CRDs effectively?
131137
132138
To enhance user experience, it is recommended to use [OpenAPI v3 schema](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#schemaObject) validation when writing your CRDs. However, this approach can sometimes require an additional parsing step.
@@ -168,4 +174,4 @@ type StructName struct {
168174
[permission-issue]: https://github.com/kubernetes/kubernetes/issues/82573
169175
[permission-PR]: https://github.com/kubernetes/kubernetes/pull/89193
170176
[controller-gen]: ./reference/controller-gen.html
171-
[controller-tool-pr]: https://github.com/kubernetes-sigs/controller-tools/pull/536
177+
[server-side-apply-plugin]: ./plugins/available/ssa-v1-alpha.md
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# This file configures golangci-lint with module plugins.
2+
# When you run 'make lint', it will automatically build a custom golangci-lint binary
3+
# with all the plugins listed below.
4+
#
5+
# See: https://golangci-lint.run/plugins/module-plugins/
6+
version: v2.8.0
7+
plugins:
8+
# logcheck validates structured logging calls and parameters (e.g., balanced key-value pairs)
9+
- module: "sigs.k8s.io/logtools"
10+
import: "sigs.k8s.io/logtools/logcheck/gclplugin"
11+
version: latest
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"name": "Kubebuilder DevContainer",
3+
"image": "golang:1.25",
4+
"features": {
5+
"ghcr.io/devcontainers/features/docker-in-docker:2": {
6+
"moby": false,
7+
"dockerDefaultAddressPool": "base=172.30.0.0/16,size=24"
8+
},
9+
"ghcr.io/devcontainers/features/git:1": {},
10+
"ghcr.io/devcontainers/features/common-utils:2": {
11+
"upgradePackages": true
12+
}
13+
},
14+
15+
"runArgs": ["--privileged", "--init"],
16+
17+
"customizations": {
18+
"vscode": {
19+
"settings": {
20+
"terminal.integrated.shell.linux": "/bin/bash"
21+
},
22+
"extensions": [
23+
"ms-kubernetes-tools.vscode-kubernetes-tools",
24+
"ms-azuretools.vscode-docker"
25+
]
26+
}
27+
},
28+
29+
"remoteEnv": {
30+
"GO111MODULE": "on"
31+
},
32+
33+
"onCreateCommand": "bash .devcontainer/post-install.sh"
34+
}
35+
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
echo "===================================="
5+
echo "Kubebuilder DevContainer Setup"
6+
echo "===================================="
7+
8+
# Verify running as root (required for installing to /usr/local/bin and /etc)
9+
if [ "$(id -u)" -ne 0 ]; then
10+
echo "ERROR: This script must be run as root"
11+
exit 1
12+
fi
13+
14+
echo ""
15+
echo "Detecting system architecture..."
16+
# Detect architecture using uname
17+
MACHINE=$(uname -m)
18+
case "${MACHINE}" in
19+
x86_64)
20+
ARCH="amd64"
21+
;;
22+
aarch64|arm64)
23+
ARCH="arm64"
24+
;;
25+
*)
26+
echo "WARNING: Unsupported architecture ${MACHINE}, defaulting to amd64"
27+
ARCH="amd64"
28+
;;
29+
esac
30+
echo "Architecture: ${ARCH}"
31+
32+
echo ""
33+
echo "------------------------------------"
34+
echo "Setting up bash completion..."
35+
echo "------------------------------------"
36+
37+
BASH_COMPLETIONS_DIR="/usr/share/bash-completion/completions"
38+
39+
# Enable bash-completion in root's .bashrc (devcontainer runs as root)
40+
if ! grep -q "source /usr/share/bash-completion/bash_completion" ~/.bashrc 2>/dev/null; then
41+
echo 'source /usr/share/bash-completion/bash_completion' >> ~/.bashrc
42+
echo "Added bash-completion to .bashrc"
43+
fi
44+
45+
echo ""
46+
echo "------------------------------------"
47+
echo "Installing development tools..."
48+
echo "------------------------------------"
49+
50+
# Install kind
51+
if ! command -v kind &> /dev/null; then
52+
echo "Installing kind..."
53+
curl -Lo /usr/local/bin/kind "https://kind.sigs.k8s.io/dl/latest/kind-linux-${ARCH}"
54+
chmod +x /usr/local/bin/kind
55+
echo "kind installed successfully"
56+
fi
57+
58+
# Generate kind bash completion
59+
if command -v kind &> /dev/null; then
60+
if kind completion bash > "${BASH_COMPLETIONS_DIR}/kind" 2>/dev/null; then
61+
echo "kind completion installed"
62+
else
63+
echo "WARNING: Failed to generate kind completion"
64+
fi
65+
fi
66+
67+
# Install kubebuilder
68+
if ! command -v kubebuilder &> /dev/null; then
69+
echo "Installing kubebuilder..."
70+
curl -Lo /usr/local/bin/kubebuilder "https://go.kubebuilder.io/dl/latest/linux/${ARCH}"
71+
chmod +x /usr/local/bin/kubebuilder
72+
echo "kubebuilder installed successfully"
73+
fi
74+
75+
# Generate kubebuilder bash completion
76+
if command -v kubebuilder &> /dev/null; then
77+
if kubebuilder completion bash > "${BASH_COMPLETIONS_DIR}/kubebuilder" 2>/dev/null; then
78+
echo "kubebuilder completion installed"
79+
else
80+
echo "WARNING: Failed to generate kubebuilder completion"
81+
fi
82+
fi
83+
84+
# Install kubectl
85+
if ! command -v kubectl &> /dev/null; then
86+
echo "Installing kubectl..."
87+
KUBECTL_VERSION=$(curl -Ls https://dl.k8s.io/release/stable.txt)
88+
curl -Lo /usr/local/bin/kubectl "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/${ARCH}/kubectl"
89+
chmod +x /usr/local/bin/kubectl
90+
echo "kubectl installed successfully"
91+
fi
92+
93+
# Generate kubectl bash completion
94+
if command -v kubectl &> /dev/null; then
95+
if kubectl completion bash > "${BASH_COMPLETIONS_DIR}/kubectl" 2>/dev/null; then
96+
echo "kubectl completion installed"
97+
else
98+
echo "WARNING: Failed to generate kubectl completion"
99+
fi
100+
fi
101+
102+
# Generate Docker bash completion
103+
if command -v docker &> /dev/null; then
104+
if docker completion bash > "${BASH_COMPLETIONS_DIR}/docker" 2>/dev/null; then
105+
echo "docker completion installed"
106+
else
107+
echo "WARNING: Failed to generate docker completion"
108+
fi
109+
fi
110+
111+
echo ""
112+
echo "------------------------------------"
113+
echo "Configuring Docker environment..."
114+
echo "------------------------------------"
115+
116+
# Wait for Docker to be ready
117+
echo "Waiting for Docker to be ready..."
118+
for i in {1..30}; do
119+
if docker info >/dev/null 2>&1; then
120+
echo "Docker is ready"
121+
break
122+
fi
123+
if [ "$i" -eq 30 ]; then
124+
echo "WARNING: Docker not ready after 30s"
125+
fi
126+
sleep 1
127+
done
128+
129+
# Create kind network (ignore if already exists)
130+
if ! docker network inspect kind >/dev/null 2>&1; then
131+
if docker network create kind >/dev/null 2>&1; then
132+
echo "Created kind network"
133+
else
134+
echo "WARNING: Failed to create kind network (may already exist)"
135+
fi
136+
fi
137+
138+
echo ""
139+
echo "------------------------------------"
140+
echo "Verifying installations..."
141+
echo "------------------------------------"
142+
kind version
143+
kubebuilder version
144+
kubectl version --client
145+
docker --version
146+
go version
147+
148+
echo ""
149+
echo "===================================="
150+
echo "DevContainer ready!"
151+
echo "===================================="
152+
echo "All development tools installed successfully."
153+
echo "You can now start building Kubernetes operators."
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file
2+
# Ignore everything by default and re-include only needed files
3+
**
4+
5+
# Re-include Go source files (but not *_test.go)
6+
!**/*.go
7+
**/*_test.go
8+
9+
# Re-include Go module files
10+
!go.mod
11+
!go.sum

0 commit comments

Comments
 (0)