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 ()
0 commit comments