Skip to content

Commit fc780de

Browse files
authored
Merge pull request #6254 from Jont828/tilt-flavors
✨ Add support for deploying Cluster templates and ClusterClasses on Tilt
2 parents 6df168f + 5f03d72 commit fc780de

File tree

2 files changed

+153
-3
lines changed

2 files changed

+153
-3
lines changed

Tiltfile

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
# -*- mode: Python -*-
22

3-
load("ext://uibutton", "cmd_button", "text_input")
3+
envsubst_cmd = "./hack/tools/bin/envsubst"
4+
clusterctl_cmd = "./bin/clusterctl"
5+
kubectl_cmd = "kubectl"
6+
7+
if str(local("command -v " + kubectl_cmd + " || true", quiet = True)) == "":
8+
fail("Required command '" + kubectl_cmd + "' not found in PATH")
9+
10+
load("ext://uibutton", "cmd_button", "location", "text_input")
411

512
# set defaults
613
version_settings(True, ">=0.22.2")
@@ -108,6 +115,9 @@ COPY --from=tilt-helper /usr/bin/kubectl /usr/bin/kubectl
108115
},
109116
}
110117

118+
def ensure_clusterctl():
119+
local("make clusterctl")
120+
111121
# Reads a provider's tilt-provider.json file and merges it into the providers map.
112122
# A list of dictionaries is also supported by enclosing it in brackets []
113123
# An example file looks like this:
@@ -388,10 +398,110 @@ def prepare_all():
388398
)
389399
local(cmd, env = settings.get("kustomize_substitutions", {}))
390400

401+
# create cluster template resources from cluster-template files in the templates directory
402+
def cluster_templates():
403+
substitutions = settings.get("kustomize_substitutions", {})
404+
405+
template_dirs = settings.get("template_dirs", {
406+
"docker": ["./test/infrastructure/docker/templates"],
407+
})
408+
409+
for provider, provider_dirs in template_dirs.items():
410+
for template_dir in provider_dirs:
411+
template_list = [filename for filename in listdir(template_dir) if os.path.basename(filename).endswith("yaml")]
412+
for filename in template_list:
413+
deploy_templates(filename, provider, substitutions)
414+
415+
def deploy_templates(filename, provider, substitutions):
416+
# validate filename exists
417+
if not os.path.exists(filename):
418+
fail(filename + " not found")
419+
420+
os.environ["NAMESPACE"] = substitutions.get("NAMESPACE", "default")
421+
os.environ["KUBERNETES_VERSION"] = substitutions.get("KUBERNETES_VERSION", "v1.24.0")
422+
os.environ["CONTROL_PLANE_MACHINE_COUNT"] = substitutions.get("CONTROL_PLANE_MACHINE_COUNT", "1")
423+
os.environ["WORKER_MACHINE_COUNT"] = substitutions.get("WORKER_MACHINE_COUNT", "3")
424+
425+
basename = os.path.basename(filename)
426+
if basename.endswith(".yaml"):
427+
if basename.startswith("clusterclass-"):
428+
template_name = basename.replace("clusterclass-", "").replace(".yaml", "")
429+
deploy_clusterclass(template_name, provider, filename)
430+
elif basename.startswith("cluster-template-"):
431+
clusterclass_name = basename.replace("cluster-template-", "").replace(".yaml", "")
432+
deploy_cluster_template(clusterclass_name, provider, filename)
433+
434+
def deploy_clusterclass(clusterclass_name, provider, filename):
435+
apply_clusterclass_cmd = "cat " + filename + " | " + envsubst_cmd + " | " + kubectl_cmd + " apply -f - && echo \"ClusterClass created from\'" + filename + "\', don't forget to delete\n\""
436+
delete_clusterclass_cmd = kubectl_cmd + " delete clusterclass " + clusterclass_name + ' --ignore-not-found=true; echo "\n"'
437+
438+
local_resource(
439+
name = clusterclass_name,
440+
cmd = ["bash", "-c", apply_clusterclass_cmd],
441+
auto_init = False,
442+
trigger_mode = TRIGGER_MODE_MANUAL,
443+
labels = [provider + "-clusterclasses"],
444+
)
445+
446+
cmd_button(
447+
clusterclass_name + ":apply",
448+
argv = ["bash", "-c", apply_clusterclass_cmd],
449+
resource = clusterclass_name,
450+
icon_name = "note_add",
451+
text = "Apply ClusterClass",
452+
)
453+
454+
cmd_button(
455+
clusterclass_name + ":delete",
456+
argv = ["bash", "-c", delete_clusterclass_cmd],
457+
resource = clusterclass_name,
458+
icon_name = "delete_forever",
459+
text = "Delete ClusterClass",
460+
)
461+
462+
def deploy_cluster_template(template_name, provider, filename):
463+
apply_cluster_template_cmd = "CLUSTER_NAME=" + template_name + "-$RANDOM; " + clusterctl_cmd + " generate cluster $CLUSTER_NAME --from " + filename + " | " + kubectl_cmd + " apply -f - && echo \"Cluster '$CLUSTER_NAME' created, don't forget to delete\n\""
464+
465+
delete_clusters_cmd = 'DELETED=$(echo "$(bash -c "' + kubectl_cmd + ' get clusters --no-headers -o custom-columns=":metadata.name"")" | grep -E "^' + template_name + '-[[:digit:]]{1,5}$"); if [ -z "$DELETED" ]; then echo "Nothing to delete for cluster template ' + template_name + '"; else echo "Deleting clusters:\n$DELETED\n"; echo $DELETED | xargs -L1 ' + kubectl_cmd + ' delete cluster; fi; echo "\n"'
466+
467+
local_resource(
468+
name = template_name,
469+
cmd = ["bash", "-c", apply_cluster_template_cmd],
470+
auto_init = False,
471+
trigger_mode = TRIGGER_MODE_MANUAL,
472+
labels = [provider + "-cluster-templates"],
473+
)
474+
475+
cmd_button(
476+
template_name + ":apply",
477+
argv = ["bash", "-c", apply_cluster_template_cmd],
478+
resource = template_name,
479+
icon_name = "add_box",
480+
text = "Create `" + template_name + "` cluster",
481+
)
482+
483+
cmd_button(
484+
template_name + ":delete",
485+
argv = ["bash", "-c", delete_clusters_cmd],
486+
resource = template_name,
487+
icon_name = "delete_forever",
488+
text = "Delete `" + template_name + "` clusters",
489+
)
490+
491+
cmd_button(
492+
template_name + ":delete-all",
493+
argv = ["bash", "-c", kubectl_cmd + " delete clusters --all --wait=false"],
494+
resource = template_name,
495+
icon_name = "delete_sweep",
496+
text = "Delete all workload clusters",
497+
)
498+
391499
##############################
392500
# Actual work happens here
393501
##############################
394502

503+
ensure_clusterctl()
504+
395505
include_user_tilt_files()
396506

397507
load_provider_tiltfiles()
@@ -403,3 +513,5 @@ deploy_provider_crds()
403513
deploy_observability()
404514

405515
enable_providers()
516+
517+
cluster_templates()

docs/book/src/developer/tilt.md

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,26 @@ documentation](https://docs.tilt.dev/api.html#api.default_registry) for more det
7171
**enable_providers** (Array[]String, default=['docker']): A list of the providers to enable. See [available providers](#available-providers)
7272
for more details.
7373

74+
**template_dirs** (Map{String: Array[]String}, default={"docker": [
75+
"./test/infrastructure/docker/templates"]}): A map of providers to directories containing cluster templates. An example of the field is given below. See [Deploying a workload cluster](#deploying-a-workload-cluster) for how this is used.
76+
77+
```yaml
78+
template_dirs:
79+
docker:
80+
- ./test/infrastructure/docker/templates
81+
- <other-template-dir>
82+
azure:
83+
- <azure-template-dir>
84+
aws:
85+
- <aws-template-dir>
86+
gcp:
87+
- <gcp-template-dir>
88+
```
89+
7490
**kustomize_substitutions** (Map{String: String}, default={}): An optional map of substitutions for `${}`-style placeholders in the
75-
provider's yaml. **Note**: When running E2E tests locally using an existing cluster managed by Tilt, the following substitutions are required for successful tests:
91+
provider's yaml. These substitutions are also used when deploying cluster templates. See [Deploying a workload cluster](#deploying-a-workload-cluster).
92+
93+
**Note**: When running E2E tests locally using an existing cluster managed by Tilt, the following substitutions are required for successful tests:
7694
```yaml
7795
kustomize_substitutions:
7896
CLUSTER_TOPOLOGY: "true"
@@ -258,7 +276,27 @@ create a cluster. There are [example worker cluster
258276
configs](https://github.com/kubernetes-sigs/cluster-api/tree/main/test/infrastructure/docker/examples) available.
259277
These can be customized for your specific needs.
260278

261-
<aside class="note">
279+
### Deploying a workload cluster
280+
281+
After your kind management cluster is up and running with Tilt, you can deploy a workload clusters in the Tilt web UI based off of YAML templates from specified directories. By default, templates are read from `./test/infrastructure/docker/templates`.
282+
283+
These deployment resources are found in the Tilt web UI under the label grouping `<provider>-cluster-templates` and `<provider>-clusterclasses` for each specified provider, i.e. `docker-cluster-templates` and `docker-clusterclasses`.
284+
285+
The `<provider>-cluster-templates` category contains cluster templates, you can create a cluster by clicking "Create cluster" or the clockwise arrow icon ⟳. Note that each time a cluster template is deployed, it deploys a new workload cluster in addition to the existing ones. To delete all clusters based off of a template, click on "Delete \<cluster-template\> cluster," and click on "Delete all workload clusters" to delete all workload clusters.
286+
287+
The `<provider>-clusterclasses` category contains ClusterClass definitions and you can create them by clicking on the "Create clusterclass" or the clockwise arrow icon ⟳ and delete them by clicking on "Delete clusterclass".
288+
289+
Variables in a cluster template are substituted with values from `kustomize_substitutions` in `tilt-settings.yaml`. The default substitutions are:
290+
291+
```yaml
292+
kustomize_substitutions:
293+
NAMESPACE: default
294+
KUBERNETES_VERSION: v1.24.0
295+
CONTROL_PLANE_MACHINE_COUNT: 1
296+
WORKER_MACHINE_COUNT: 3
297+
```
298+
299+
Lastly, cluster template directories can be specified from the `template_dirs` field in `tilt-settings.yaml`. See [tilt-settings fields](#tilt-settings-fields) for an example.
262300

263301
<h1>Use of clusterctl</h1>
264302

0 commit comments

Comments
 (0)