Skip to content

Commit a05c8c4

Browse files
authored
Merge pull request #165 from k8s-proxmox/feature/tilt
support tilt
2 parents 7080b07 + 25a8e0e commit a05c8c4

File tree

4 files changed

+316
-20
lines changed

4 files changed

+316
-20
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,7 @@ ginkgo-log.txt
3434
internal/test/e2e/clusters
3535
internal/test/e2e/kind
3636
internal/test/e2e/repository
37-
internal/test/e2e/data/infrastructure-proxmox/*/cluster-template.yaml
37+
internal/test/e2e/data/infrastructure-proxmox/*/cluster-template.yaml
38+
39+
# tilt
40+
.tiltbuild

Makefile

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,29 @@ ENVTEST_K8S_VERSION = 1.26.1
1111
LOCALBIN ?= $(shell pwd)/bin
1212
export PATH := $(abspath $(LOCALBIN)):$(PATH)
1313

14+
## Location to install dependencies to
15+
$(LOCALBIN):
16+
mkdir -p $(LOCALBIN)
17+
18+
## Tool Binaries
19+
KUSTOMIZE ?= $(LOCALBIN)/kustomize
20+
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
21+
ENVTEST ?= $(LOCALBIN)/setup-envtest
22+
ENVSUBST ?= $(LOCALBIN)/envsubst
23+
KUBECTL ?= $(LOCALBIN)/kubectl
24+
GOLANGCI_LINT ?= $(LOCALBIN)/golangci-lint
25+
GOIMPORTS ?= $(LOCALBIN)/goimports
26+
TILT ?= $(LOCALBIN)/tilt
27+
KIND ?= $(LOCALBIN)/kind
28+
29+
## Tool Versions
30+
KUSTOMIZE_VERSION ?= v5.0.0
31+
CONTROLLER_TOOLS_VERSION ?= v0.11.3
32+
ENVSUBST_VER ?= v1.4.2
33+
KUBECTL_VER := v1.25.10
34+
TILT_VER := 0.33.6
35+
KIND_VER := v0.20.0
36+
1437
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
1538
ifeq (,$(shell go env GOBIN))
1639
GOBIN=$(shell go env GOPATH)/bin
@@ -69,6 +92,29 @@ vet: ## Run go vet against code.
6992
lint: golangci-lint ## Run golangci-lint
7093
$(GOLANGCI_LINT) run
7194

95+
KIND_CLUSTER_NAME := cappx
96+
.PHONY: kind-cluster
97+
kind-cluster: kind
98+
@if kind get nodes --name=$(KIND_CLUSTER_NAME) | grep -q cappx; then \
99+
echo "kind cluster $(KIND_CLUSTER_NAME) already exists"; \
100+
else \
101+
kind create cluster --name=$(KIND_CLUSTER_NAME); \
102+
fi
103+
104+
.PHONY: kind-delete-cluster
105+
kind-delete-cluster: kind
106+
@if kind get nodes --name=$(KIND_CLUSTER_NAME) | grep -q cappx; then \
107+
kind delete cluster --name=$(KIND_CLUSTER_NAME); \
108+
fi
109+
110+
.PHONY: tilt-up
111+
tilt-up: tilt kind-cluster envsubst kustomize
112+
$(TILT) up
113+
114+
.PHONY: tilt-down
115+
tilt-down: tilt envsubst kustomize
116+
$(TILT) down
117+
72118
CLUSTER_NAME := cappx-test
73119

74120
.PHONY: create-workload-cluster
@@ -222,25 +268,6 @@ release-templates: $(RELEASE_DIR)
222268

223269
##@ Build Dependencies
224270

225-
## Location to install dependencies to
226-
$(LOCALBIN):
227-
mkdir -p $(LOCALBIN)
228-
229-
## Tool Binaries
230-
KUSTOMIZE ?= $(LOCALBIN)/kustomize
231-
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
232-
ENVTEST ?= $(LOCALBIN)/setup-envtest
233-
ENVSUBST ?= $(LOCALBIN)/envsubst
234-
KUBECTL ?= $(LOCALBIN)/kubectl
235-
GOLANGCI_LINT ?= $(LOCALBIN)/golangci-lint
236-
GOIMPORTS ?= $(LOCALBIN)/goimports
237-
238-
## Tool Versions
239-
KUSTOMIZE_VERSION ?= v5.0.0
240-
CONTROLLER_TOOLS_VERSION ?= v0.11.3
241-
ENVSUBST_VER ?= v1.4.2
242-
KUBECTL_VER := v1.25.10
243-
244271
KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh"
245272
.PHONY: kustomize
246273
kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading.
@@ -287,3 +314,21 @@ $(GOLANGCI_LINT): $(LOCALBIN)
287314
goimports: $(GOIMPORTS)
288315
$(GOIMPORTS): $(LOCALBIN)
289316
GOBIN=$(LOCALBIN) go install golang.org/x/tools/cmd/goimports@latest
317+
318+
TILT_DOWNLOAD_URL ?= https://github.com/tilt-dev/tilt/releases/download/v$(TILT_VER)/tilt.$(TILT_VER).linux.x86_64.tar.gz
319+
.PHONY: tilt
320+
tilt: $(TILT)
321+
$(TILT): $(LOCALBIN)
322+
@if test -x $(LOCALBIN)/tilt && ! $(LOCALBIN)/tilt version | grep -q $(TILT_VER); then \
323+
rm -rf $(LOCALBIN)/tilt; \
324+
fi
325+
test -s $(LOCALBIN)/tilt || (curl -fsSL $(TILT_DOWNLOAD_URL) | tar -xzv tilt && mv tilt $(LOCALBIN)/tilt)
326+
327+
KIND_DOWNLOAD_URL ?= https://kind.sigs.k8s.io/dl/$(KIND_VER)/kind-linux-$(GOARCH)
328+
.PHONY: kind
329+
kind: $(KIND)
330+
$(KIND): $(LOCALBIN)
331+
@if test -x $(LOCALBIN)/kind && ! $(LOCALBIN)/kind version | grep -q $(KIND_VER); then \
332+
rm -rf $(LOCALBIN)/kind; \
333+
fi
334+
test -s $(LOCALBIN)/kind || (curl -Lo $(LOCALBIN)/kind $(KIND_DOWNLOAD_URL) && chmod +x $(LOCALBIN)/kind)

Tiltfile

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
# -*- mode: Python -*-
2+
3+
envsubst_cmd = "./bin/envsubst"
4+
5+
load("ext://uibutton", "cmd_button", "location", "text_input")
6+
7+
# set defaults
8+
settings = {
9+
"capi_version": "v1.5.3",
10+
"allowed_contexts": [
11+
"kind-cappx",
12+
"default"
13+
],
14+
"extra_args": {
15+
"core": [
16+
"--feature-gates=MachinePool=false,ClusterResourceSet=true,ClusterTopology=false,RuntimeSDK=false,MachineSetPreflightChecks=false"
17+
]
18+
}
19+
}
20+
21+
always_enable_providers = ["proxmox"]
22+
providers = {}
23+
extra_args = settings.get("extra_args", {})
24+
25+
# global settings
26+
settings.update(read_json(
27+
"tilt-settings.json",
28+
default = {},
29+
))
30+
31+
if "allowed_contexts" in settings:
32+
allow_k8s_contexts(settings.get("allowed_contexts"))
33+
34+
# deploy core, bootstrap and controlplane provider
35+
def deploy_capi():
36+
version = settings.get("capi_version")
37+
capi_uri = "https://github.com/kubernetes-sigs/cluster-api/releases/download/{}/cluster-api-components.yaml".format(version)
38+
cmd = "curl -sSL {} | {} | kubectl apply -f -".format(capi_uri, envsubst_cmd)
39+
local(cmd, quiet=True)
40+
if settings.get("extra_args"):
41+
extra_args = settings.get("extra_args")
42+
if extra_args.get("core"):
43+
core_extra_args = extra_args.get("core")
44+
if core_extra_args:
45+
for namespace in ["capi-system"]:
46+
patch_args_with_extra_args(namespace, "capi-controller-manager", 0, core_extra_args)
47+
patch_capi_manager_role_with_exp_infra_rbac()
48+
if extra_args.get("kubeadm-bootstrap"):
49+
kb_extra_args = extra_args.get("kubeadm-bootstrap")
50+
if kb_extra_args:
51+
patch_args_with_extra_args("capi-kubeadm-bootstrap-system", "capi-kubeadm-bootstrap-controller-manager", 1, kb_extra_args)
52+
53+
def patch_args_with_extra_args(namespace, name, n_container, extra_args):
54+
args_str = str(local('kubectl get deployments {} -n {} -o jsonpath={{.spec.template.spec.containers[{}].args}}'.format(name, namespace, n_container)))
55+
args_to_add = [arg for arg in extra_args if arg not in args_str]
56+
if args_to_add:
57+
args = args_str[1:-1].split()
58+
args.extend(args_to_add)
59+
patch = [{
60+
"op": "replace",
61+
"path": "/spec/template/spec/containers/{}/args".format(n_container),
62+
"value": args,
63+
}]
64+
local("kubectl patch deployment {} -n {} --type json -p='{}'".format(name, namespace, str(encode_json(patch)).replace("\n", "")))
65+
66+
# patch the CAPI manager role to also provide access to experimental infrastructure
67+
def patch_capi_manager_role_with_exp_infra_rbac():
68+
api_groups_str = str(local('kubectl get clusterrole capi-manager-role -o jsonpath={.rules[1].apiGroups}'))
69+
exp_infra_group = "exp.infrastructure.cluster.x-k8s.io"
70+
if exp_infra_group not in api_groups_str:
71+
groups = api_groups_str[1:-1].split() # "[arg1 arg2 ...]" trim off the first and last, then split
72+
groups.append(exp_infra_group)
73+
patch = [{
74+
"op": "replace",
75+
"path": "/rules/1/apiGroups",
76+
"value": groups,
77+
}]
78+
local("kubectl patch clusterrole capi-manager-role --type json -p='{}'".format(str(encode_json(patch)).replace("\n", "")))
79+
80+
def load_provider_tiltfiles(provider_repos):
81+
for repo in provider_repos:
82+
file = repo + "/tilt-provider.json"
83+
provider_details = read_json(file, default = {})
84+
if type(provider_details) != type([]):
85+
provider_details = [provider_details]
86+
for item in provider_details:
87+
provider_name = item["name"]
88+
provider_config = item["config"]
89+
if "context" in provider_config:
90+
provider_config["context"] = repo + "/" + provider_config["context"]
91+
else:
92+
provider_config["context"] = repo
93+
if "kustomize_config" not in provider_config:
94+
provider_config["kustomize_config"] = True
95+
if "go_main" not in provider_config:
96+
provider_config["go_main"] = "main.go"
97+
providers[provider_name] = provider_config
98+
99+
tilt_helper_dockerfile_header = """
100+
# Tilt image
101+
FROM golang:1.20 as tilt-helper
102+
# Support live reloading with Tilt
103+
RUN wget --output-document /restart.sh --quiet https://raw.githubusercontent.com/windmilleng/rerun-process-wrapper/master/restart.sh && \
104+
wget --output-document /start.sh --quiet https://raw.githubusercontent.com/windmilleng/rerun-process-wrapper/master/start.sh && \
105+
chmod +x /start.sh && chmod +x /restart.sh
106+
"""
107+
108+
tilt_dockerfile_header = """
109+
FROM gcr.io/distroless/base:debug as tilt
110+
WORKDIR /
111+
COPY --from=tilt-helper /start.sh .
112+
COPY --from=tilt-helper /restart.sh .
113+
COPY manager .
114+
"""
115+
116+
# Configures a provider by doing the following:
117+
#
118+
# 1. Enables a local_resource go build of the provider's manager binary
119+
# 2. Configures a docker build for the provider, with live updating of the manager binary
120+
# 3. Runs kustomize for the provider's config/ and applies it
121+
def enable_provider(name):
122+
p = providers.get(name)
123+
124+
name = p.get("name", name)
125+
context = p.get("context")
126+
go_main = p.get("go_main")
127+
label = p.get("label", name)
128+
129+
# Prefix each live reload dependency with context. For example, for if the context is
130+
# test/infra/docker and main.go is listed as a dep, the result is test/infra/docker/main.go. This adjustment is
131+
# needed so Tilt can watch the correct paths for changes.
132+
live_reload_deps = []
133+
for d in p.get("live_reload_deps", []):
134+
live_reload_deps.append(context + "/" + d)
135+
136+
# Set up a local_resource build of the provider's manager binary. The provider is expected to have a main.go in
137+
# manager_build_path or the main.go must be provided via go_main option. The binary is written to .tiltbuild/manager.
138+
local_resource(
139+
label.lower() + "_binary",
140+
cmd = "cd " + context + ';mkdir -p .tiltbuild;CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags \'-extldflags "-static"\' -o .tiltbuild/manager ' + go_main,
141+
deps = live_reload_deps,
142+
labels = [label, "ALL.binaries"],
143+
)
144+
145+
additional_docker_helper_commands = p.get("additional_docker_helper_commands", "")
146+
additional_docker_build_commands = p.get("additional_docker_build_commands", "")
147+
148+
dockerfile_contents = "\n".join([
149+
tilt_helper_dockerfile_header,
150+
additional_docker_helper_commands,
151+
tilt_dockerfile_header,
152+
additional_docker_build_commands,
153+
])
154+
155+
# Set up an image build for the provider. The live update configuration syncs the output from the local_resource
156+
# build into the container.
157+
entrypoint = ["sh", "/start.sh", "/manager"]
158+
provider_args = extra_args.get(name)
159+
if provider_args:
160+
entrypoint.extend(provider_args)
161+
162+
docker_build(
163+
ref = p.get("image"),
164+
context = context + "/.tiltbuild/",
165+
dockerfile_contents = dockerfile_contents,
166+
target = "tilt",
167+
entrypoint = entrypoint,
168+
only = "manager",
169+
live_update = [
170+
sync(context + "/.tiltbuild/manager", "/manager"),
171+
run("sh /restart.sh"),
172+
],
173+
)
174+
175+
if p.get("kustomize_config"):
176+
# Copy all the substitutions from the user's tilt-settings.json into the environment. Otherwise, the substitutions
177+
# are not available and their placeholders will be replaced with the empty string when we call kustomize +
178+
# envsubst below.
179+
substitutions = settings.get("kustomize_substitutions", {})
180+
os.environ.update(substitutions)
181+
182+
# Apply the kustomized yaml for this provider
183+
184+
yaml = str(kustomize(context + "/config/default"))
185+
k8s_yaml(blob(yaml))
186+
get_controller_name(name)
187+
else:
188+
get_controller_name(name)
189+
190+
# Enable core cluster-api plus everything listed in 'enable_providers' in tilt-settings.json
191+
def enable_providers():
192+
# local("make kustomize")
193+
194+
provider_repos = settings.get("provider_repos", [])
195+
union_provider_repos = [ k for k in provider_repos + ["."] ]
196+
load_provider_tiltfiles(union_provider_repos)
197+
198+
user_enable_providers = settings.get("enable_providers", [])
199+
union_enable_providers = {k: "" for k in user_enable_providers + always_enable_providers}.keys()
200+
for name in union_enable_providers:
201+
enable_provider(name)
202+
203+
def get_controller_name(name):
204+
p = providers.get(name)
205+
name = p.get("name", name)
206+
label = p.get("label", name)
207+
manager_name = p.get("manager_name")
208+
if manager_name:
209+
k8s_resource(
210+
workload = manager_name,
211+
new_name = label.lower() + "_controller",
212+
labels = [label, "ALL.controllers"],
213+
)
214+
215+
def kustomize(folder):
216+
yaml = local('./bin/kustomize build {}'.format(folder), quiet=True)
217+
return yaml
218+
219+
##############################
220+
# Actual work happens here
221+
##############################
222+
223+
load("ext://cert_manager", "deploy_cert_manager")
224+
225+
if settings.get("deploy_cert_manager"):
226+
deploy_cert_manager(version=settings.get("cert_manager_version"))
227+
228+
deploy_capi()
229+
230+
enable_providers()

tilt-provider.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "proxmox",
3+
"config": {
4+
"image": "ghcr.io/sp-yduck/cluster-api-provider-proxmox",
5+
"live_reload_deps": [
6+
"api",
7+
"cloud",
8+
"cmd/main.go",
9+
"config",
10+
"controllers",
11+
"go.mod",
12+
"go.sum"
13+
],
14+
"label": "CAPPX",
15+
"manager_name": "cappx-controller-manager",
16+
"go_main": "cmd/main.go"
17+
}
18+
}

0 commit comments

Comments
 (0)