Skip to content

Commit 0d4c226

Browse files
committed
feat: add tilt support for capz focused
1 parent 43d7f6e commit 0d4c226

File tree

5 files changed

+233
-7
lines changed

5 files changed

+233
-7
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ bazel-*
4949

5050
# Tilt files.
5151
.tiltbuild
52+
/tilt.d
53+
tilt-settings.json
5254

5355
# e2e output
5456
test/e2e/junit.e2e_suite.*.xml

Makefile

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ release-notes: $(RELEASE_NOTES)
293293
.PHONY: create-management-cluster
294294
create-management-cluster: $(ENVSUBST)
295295
## Create kind management cluster.
296-
kind create cluster --name=clusterapi
296+
$(MAKE) kind-create
297297

298298
# Install cert manager and wait for availability
299299
kubectl create -f https://github.com/jetstack/cert-manager/releases/download/v0.11.1/cert-manager.yaml
@@ -303,7 +303,7 @@ create-management-cluster: $(ENVSUBST)
303303
kubectl apply -f https://github.com/kubernetes-sigs/cluster-api/releases/download/v0.3.3/cluster-api-components.yaml
304304

305305
# Deploy CAPZ
306-
kind load docker-image $(CONTROLLER_IMG)-$(ARCH):$(TAG) --name=clusterapi
306+
kind load docker-image $(CONTROLLER_IMG)-$(ARCH):$(TAG) --name=capz
307307
kustomize build config | $(ENVSUBST) | kubectl apply -f -
308308

309309
# Wait for CAPI pods
@@ -316,7 +316,7 @@ create-management-cluster: $(ENVSUBST)
316316

317317
# required sleep for when creating management and workload cluster simultaneously
318318
sleep 10
319-
@echo 'Set kubectl context to the kind management cluster by running "kubectl config set-context kind-clusterapi"'
319+
@echo 'Set kubectl context to the kind management cluster by running "kubectl config set-context kind-capz"'
320320

321321
.PHONY: create-workload-cluster
322322
create-workload-cluster: $(ENVSUBST)
@@ -342,13 +342,26 @@ delete-workload-cluster: ## Deletes the example workload Kubernetes cluster
342342
@echo 'Your Azure resources will now be deleted, this can take up to 20 minutes'
343343
kubectl delete cluster $(CLUSTER_NAME)
344344

345+
## --------------------------------------
346+
## Tilt / Kind
347+
## --------------------------------------
348+
349+
.PHONY: kind-create
350+
kind-create: ## create capz kind cluster if needed
351+
./scripts/kind-with-registry.sh
352+
353+
.PHONY: tilt-up
354+
tilt-up: kind-create ## start tilt and build kind cluster if needed
355+
tilt up
356+
345357
.PHONY: delete-cluster
346-
delete-cluster: delete-workload-cluster ## Deletes the example kind cluster "clusterapi"
347-
kind delete cluster --name=clusterapi
358+
delete-cluster: delete-workload-cluster ## Deletes the example kind cluster "capz"
359+
kind delete cluster --name=capz
348360

349361
.PHONY: kind-reset
350-
kind-reset: ## Destroys the "clusterapi" kind cluster.
351-
kind delete cluster --name=clusterapi || true
362+
kind-reset: ## Destroys the "capz" kind cluster.
363+
kind delete cluster --name=capz || true
364+
352365

353366
## --------------------------------------
354367
## Cleanup / Verification

Tiltfile

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# -*- mode: Python -*-
2+
3+
# set defaults
4+
5+
settings = {
6+
"allowed_contexts": [
7+
"kind-capz"
8+
],
9+
"deploy_cert_manager": True,
10+
"preload_images_for_kind": True,
11+
"kind_cluster_name": "capz",
12+
"capi_version": "v0.3.3",
13+
"cert_manager_version": "v0.11.0",
14+
"feature_gates": '--feature-gates MachinePool=true'
15+
}
16+
17+
keys = ["AZURE_SUBSCRIPTION_ID_B64", "AZURE_TENANT_ID_B64", "AZURE_CLIENT_SECRET_B64", "AZURE_CLIENT_ID_B64"]
18+
19+
# global settings
20+
settings.update(read_json(
21+
"tilt-settings.json",
22+
default = {},
23+
))
24+
25+
if settings.get("trigger_mode") == "manual":
26+
trigger_mode(TRIGGER_MODE_MANUAL)
27+
28+
if "allowed_contexts" in settings:
29+
allow_k8s_contexts(settings.get("allowed_contexts"))
30+
31+
if "default_registry" in settings:
32+
default_registry(settings.get("default_registry"))
33+
34+
35+
# Prepull all the cert-manager images to your local environment and then load them directly into kind. This speeds up
36+
# setup if you're repeatedly destroying and recreating your kind cluster, as it doesn't have to pull the images over
37+
# the network each time.
38+
def deploy_cert_manager():
39+
registry = "quay.io/jetstack"
40+
version = settings.get("cert_manager_version")
41+
images = ["cert-manager-controller", "cert-manager-cainjector", "cert-manager-webhook"]
42+
43+
if settings.get("preload_images_for_kind"):
44+
for image in images:
45+
local("docker pull {}/{}:{}".format(registry, image, version))
46+
local("kind load docker-image --name {} {}/{}:{}".format(settings.get("kind_cluster_name"), registry, image, version))
47+
48+
local("kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/{}/cert-manager.yaml".format(version))
49+
50+
# wait for the service to become available
51+
local("kubectl wait --for=condition=Available --timeout=300s apiservice v1beta1.webhook.cert-manager.io")
52+
53+
54+
# deploy CAPI
55+
def deploy_capi():
56+
version = settings.get("capi_version")
57+
local("kubectl apply -f https://github.com/kubernetes-sigs/cluster-api/releases/download/{}/cluster-api-components.yaml".format(version))
58+
if settings.get("feature_gates"):
59+
args_str = str(local('kubectl get deployments capi-controller-manager -n capi-system -o jsonpath={.spec.template.spec.containers[1].args}'))
60+
if settings.get("feature_gates") not in args_str:
61+
args = args_str[1:-1].split() # "[arg1 arg2 ...]" trim off the first and last, then split
62+
args.append("\"{}\"".format(settings.get("feature_gates")))
63+
patch = [{
64+
"op": "replace",
65+
"path": "/spec/template/spec/containers/1/args",
66+
"value": args,
67+
}]
68+
local("kubectl patch deployment capi-controller-manager -n capi-system --type json -p='{}'".format(str(encode_json(patch)).replace("\n", "")))
69+
70+
71+
# Users may define their own Tilt customizations in tilt.d. This directory is excluded from git and these files will
72+
# not be checked in to version control.
73+
def include_user_tilt_files():
74+
user_tiltfiles = listdir("tilt.d")
75+
for f in user_tiltfiles:
76+
include(f)
77+
78+
79+
def append_arg_for_container_in_deployment(yaml_stream, name, namespace, contains_image_name, arg):
80+
for item in yaml_stream:
81+
if item["kind"] == "Deployment" and item.get("metadata").get("name") == name and item.get("metadata").get("namespace") == namespace:
82+
containers = item.get("spec").get("template").get("spec").get("containers")
83+
for container in containers:
84+
if contains_image_name in container.get("image"):
85+
container.get("args").append("\"{}\"".format(arg))
86+
87+
88+
def fixup_yaml_empty_arrays(yaml_str):
89+
yaml_str = yaml_str.replace("conditions: null", "conditions: []")
90+
return yaml_str.replace("storedVersions: null", "storedVersions: []")
91+
92+
93+
def validate_auth():
94+
substitutions = settings.get("kustomize_substitutions", {})
95+
missing = [k for k in keys if k not in substitutions]
96+
if missing:
97+
fail("missing kustomize_substitutions keys {} in tilt-setting.json".format(missing))
98+
99+
100+
# Build CAPZ and add feature gates
101+
def capz():
102+
# Apply the kustomized yaml for this provider
103+
yaml = str(kustomize("./config"))
104+
substitutions = settings.get("kustomize_substitutions", {})
105+
for substitution in substitutions:
106+
value = substitutions[substitution]
107+
yaml = yaml.replace("${" + substitution + "}", value)
108+
109+
# add feature gates if they are defined
110+
if settings.get("feature_gates"):
111+
yaml_dict = decode_yaml_stream(yaml)
112+
append_arg_for_container_in_deployment(yaml_dict, "capz-controller-manager", "capz-system", "cluster-api-azure-controller", settings.get("feature_gates"))
113+
yaml = str(encode_yaml_stream(yaml_dict))
114+
yaml = fixup_yaml_empty_arrays(yaml)
115+
116+
k8s_yaml(blob(yaml))
117+
docker_build( "gcr.io/k8s-staging-cluster-api-azure/cluster-api-azure-controller", ".")
118+
119+
##############################
120+
# Actual work happens here
121+
##############################
122+
123+
validate_auth()
124+
125+
include_user_tilt_files()
126+
127+
if settings.get("deploy_cert_manager"):
128+
deploy_cert_manager()
129+
130+
deploy_capi()
131+
132+
capz()

docs/development.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,45 @@ A few Makefile and scripts are offered to work with go modules:
9797

9898
### Using Tilt
9999

100+
Both of the [Tilt](https://tilt.dev) setups below will get you started developing CAPZ in a local kind cluster.
101+
The main difference is the number of components you will build from source and the scope of the changes you'd like to make.
102+
If you only want to make changes in CAPZ, then follow [CAPZ instructions](#tilt-for-dev-in-capz). This will
103+
save you from having to build all of the images for CAPI, which can take a while. If the scope of your
104+
development will span both CAPZ and CAPI, then follow the [CAPI and CAPZ instructions](#tilt-for-dev-in-both-capz-and-capi).
105+
106+
#### Tilt for dev in CAPZ
107+
108+
If you want to develop in CAPZ and get a local development cluster working quickly, this is the path for you.
109+
110+
From the root of the CAPZ repository and after configuring the environment variables, you can run the following to generate your `tilt-settings.json` file:
111+
112+
```shell
113+
cat <<EOF > tilt-settings.json
114+
{
115+
"kustomize_substitutions": {
116+
"AZURE_SUBSCRIPTION_ID_B64": "$(echo "${AZURE_SUBSCRIPTION_ID}" | tr -d '\n' | base64 | tr -d '\n')",
117+
"AZURE_TENANT_ID_B64": "$(echo "${AZURE_TENANT_ID}" | tr -d '\n' | base64 | tr -d '\n')",
118+
"AZURE_CLIENT_SECRET_B64": "$(echo "${AZURE_CLIENT_SECRET}" | tr -d '\n' | base64 | tr -d '\n')",
119+
"AZURE_CLIENT_ID_B64": "$(echo "${AZURE_CLIENT_ID}" | tr -d '\n' | base64 | tr -d '\n')"
120+
}
121+
}
122+
EOF
123+
```
124+
125+
To build a kind cluster and start tilt, just run:
126+
```shell
127+
make tilt-up
128+
```
129+
130+
To tear down the kind cluster built by the command above, just run:
131+
```shell
132+
make kind-reset
133+
```
134+
135+
#### Tilt for dev in both CAPZ and CAPI
136+
137+
If you want to develop in both CAPI and CAPZ at the same time, then this is the path for you.
138+
100139
To use [Tilt](https://tilt.dev/) for a simplified development workflow, follow the [instructions](https://cluster-api.sigs.k8s.io/developer/tilt.html) in the cluster-api repo. The instructions will walk you through cloning the Cluster API (capi) repository and configuring Tilt to use `kind` to delpoy the cluster api managment components.
101140

102141
> you may wish to checkout out the correct version of capi to match the [version used in capz](go.mod)

scripts/kind-with-registry.sh

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/env bash
2+
set -o errexit
3+
4+
# desired cluster name; default is "kind"
5+
KIND_CLUSTER_NAME="${KIND_CLUSTER_NAME:-capz}"
6+
7+
if [[ "$(kind get clusters)" =~ .*"${KIND_CLUSTER_NAME}".* ]]; then
8+
echo "cluster already exists, moving on"
9+
exit 0
10+
fi
11+
12+
# create registry container unless it already exists
13+
reg_name='kind-registry'
14+
reg_port='5000'
15+
running="$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)"
16+
if [ "${running}" != 'true' ]; then
17+
docker run \
18+
-d --restart=always -p "${reg_port}:5000" --name "${reg_name}" \
19+
registry:2
20+
fi
21+
22+
# create a cluster with the local registry enabled in containerd
23+
cat <<EOF | kind create cluster --name "${KIND_CLUSTER_NAME}" --config=-
24+
kind: Cluster
25+
apiVersion: kind.x-k8s.io/v1alpha4
26+
containerdConfigPatches:
27+
- |-
28+
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry:${reg_port}"]
29+
endpoint = ["http://registry:${reg_port}"]
30+
EOF
31+
32+
# add the registry to /etc/hosts on each node
33+
ip_fmt='{{.NetworkSettings.IPAddress}}'
34+
cmd="echo $(docker inspect -f "${ip_fmt}" "${reg_name}") registry >> /etc/hosts"
35+
for node in $(kind get nodes --name "${KIND_CLUSTER_NAME}"); do
36+
docker exec "${node}" sh -c "${cmd}"
37+
kubectl annotate node "${node}" \
38+
tilt.dev/registry=localhost:${reg_port} \
39+
tilt.dev/registry-from-cluster=registry:${reg_port}
40+
done

0 commit comments

Comments
 (0)