From 21af02038603f3c9177ffc0de6806d4f232c18a1 Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Thu, 10 Aug 2023 13:59:43 +0400 Subject: [PATCH] feat: add external provisioner daemon helm chart (#8939) * Refactor helm to extract common templates to libcoder Signed-off-by: Spike Curtis * Remove comment from libcoder Chart.yaml Signed-off-by: Spike Curtis * Add provisioner helm chart * Fix prettier, linting, docs Signed-off-by: Spike Curtis * Log at INFO when provisionerd connects to coderd Signed-off-by: Spike Curtis * remove unnecessary exports in helm tests Signed-off-by: Spike Curtis --------- Signed-off-by: Spike Curtis --- .prettierignore | 2 +- .prettierignore.include | 2 +- Makefile | 10 +- docs/admin/configure.md | 2 +- docs/admin/scale.md | 2 +- docs/install/kubernetes.md | 2 +- helm/Makefile | 11 +- helm/{ => coder}/.helmignore | 0 helm/coder/Chart.lock | 6 + helm/{ => coder}/Chart.yaml | 7 +- helm/{ => coder}/README.md | 0 helm/coder/charts/libcoder-0.1.0.tgz | Bin 0 -> 2999 bytes helm/{ => coder}/templates/NOTES.txt | 0 helm/coder/templates/_coder.tpl | 102 ++++++++ helm/coder/templates/coder.yaml | 5 + .../templates/extra-templates.yaml | 0 helm/{ => coder}/templates/ingress.yaml | 0 helm/coder/templates/rbac.yaml | 1 + helm/{ => coder}/templates/service.yaml | 0 helm/{ => coder}/tests/chart_test.go | 24 +- .../{ => coder}/tests/testdata/command.golden | 141 ++++++----- helm/{ => coder}/tests/testdata/command.yaml | 0 .../tests/testdata/command_args.golden | 143 ++++++------ .../tests/testdata/command_args.yaml | 0 .../tests/testdata/default_values.golden | 141 ++++++----- .../tests/testdata/default_values.yaml | 0 .../tests/testdata/labels_annotations.golden | 157 ++++++------- .../tests/testdata/labels_annotations.yaml | 0 .../tests/testdata/missing_values.yaml | 0 .../tests/testdata/provisionerd_psk.golden | 194 +++++++++++++++ .../tests/testdata/provisionerd_psk.yaml | 5 + helm/{ => coder}/tests/testdata/sa.golden | 144 ++++++------ helm/{ => coder}/tests/testdata/sa.yaml | 0 helm/coder/tests/testdata/tls.golden | 210 +++++++++++++++++ helm/{ => coder}/tests/testdata/tls.yaml | 0 .../tests/testdata/workspace_proxy.golden | 197 ++++++++++++++++ .../tests/testdata/workspace_proxy.yaml | 0 helm/{ => coder}/values.yaml | 10 + helm/libcoder/Chart.yaml | 13 ++ helm/libcoder/templates/_coder.yaml | 85 +++++++ helm/{ => libcoder}/templates/_helpers.tpl | 20 +- .../templates/_rbac.yaml} | 6 +- helm/libcoder/templates/_util.yaml | 13 ++ helm/provisioner/Chart.lock | 6 + helm/provisioner/Chart.yaml | 34 +++ helm/provisioner/charts/libcoder-0.1.0.tgz | Bin 0 -> 2994 bytes helm/provisioner/templates/_coder.tpl | 85 +++++++ helm/provisioner/templates/coder.yaml | 5 + helm/provisioner/templates/rbac.yaml | 1 + helm/provisioner/tests/chart_test.go | 172 ++++++++++++++ .../provisioner/tests/testdata/command.golden | 135 +++++++++++ helm/provisioner/tests/testdata/command.yaml | 5 + .../tests/testdata/command_args.golden | 135 +++++++++++ .../tests/testdata/command_args.yaml | 6 + .../tests/testdata/default_values.golden | 135 +++++++++++ .../tests/testdata/default_values.yaml | 3 + .../tests/testdata/labels_annotations.golden | 143 ++++++++++++ .../tests/testdata/labels_annotations.yaml | 15 ++ .../tests/testdata/missing_values.yaml | 0 .../tests/testdata/provisionerd_psk.golden | 137 +++++++++++ .../tests/testdata/provisionerd_psk.yaml | 8 + helm/provisioner/tests/testdata/sa.golden | 136 +++++++++++ helm/provisioner/tests/testdata/sa.yaml | 8 + helm/provisioner/values.yaml | 204 ++++++++++++++++ helm/templates/coder.yaml | 143 ------------ helm/tests/testdata/tls.golden | 220 ------------------ helm/tests/testdata/workspace_proxy.golden | 206 ---------------- provisionerd/provisionerd.go | 2 +- scripts/helm.sh | 4 +- site/.eslintignore | 2 +- site/.prettierignore | 2 +- 71 files changed, 2621 insertions(+), 986 deletions(-) rename helm/{ => coder}/.helmignore (100%) create mode 100644 helm/coder/Chart.lock rename helm/{ => coder}/Chart.yaml (85%) rename helm/{ => coder}/README.md (100%) create mode 100644 helm/coder/charts/libcoder-0.1.0.tgz rename helm/{ => coder}/templates/NOTES.txt (100%) create mode 100644 helm/coder/templates/_coder.tpl create mode 100644 helm/coder/templates/coder.yaml rename helm/{ => coder}/templates/extra-templates.yaml (100%) rename helm/{ => coder}/templates/ingress.yaml (100%) create mode 100644 helm/coder/templates/rbac.yaml rename helm/{ => coder}/templates/service.yaml (100%) rename helm/{ => coder}/tests/chart_test.go (91%) rename helm/{ => coder}/tests/testdata/command.golden (61%) rename helm/{ => coder}/tests/testdata/command.yaml (100%) rename helm/{ => coder}/tests/testdata/command_args.golden (61%) rename helm/{ => coder}/tests/testdata/command_args.yaml (100%) rename helm/{ => coder}/tests/testdata/default_values.golden (61%) rename helm/{ => coder}/tests/testdata/default_values.yaml (100%) rename helm/{ => coder}/tests/testdata/labels_annotations.golden (64%) rename helm/{ => coder}/tests/testdata/labels_annotations.yaml (100%) rename helm/{ => coder}/tests/testdata/missing_values.yaml (100%) create mode 100644 helm/coder/tests/testdata/provisionerd_psk.golden create mode 100644 helm/coder/tests/testdata/provisionerd_psk.yaml rename helm/{ => coder}/tests/testdata/sa.golden (60%) rename helm/{ => coder}/tests/testdata/sa.yaml (100%) create mode 100644 helm/coder/tests/testdata/tls.golden rename helm/{ => coder}/tests/testdata/tls.yaml (100%) create mode 100644 helm/coder/tests/testdata/workspace_proxy.golden rename helm/{ => coder}/tests/testdata/workspace_proxy.yaml (100%) rename helm/{ => coder}/values.yaml (96%) create mode 100644 helm/libcoder/Chart.yaml create mode 100644 helm/libcoder/templates/_coder.yaml rename helm/{ => libcoder}/templates/_helpers.tpl (93%) rename helm/{templates/rbac.yaml => libcoder/templates/_rbac.yaml} (85%) create mode 100644 helm/libcoder/templates/_util.yaml create mode 100644 helm/provisioner/Chart.lock create mode 100644 helm/provisioner/Chart.yaml create mode 100644 helm/provisioner/charts/libcoder-0.1.0.tgz create mode 100644 helm/provisioner/templates/_coder.tpl create mode 100644 helm/provisioner/templates/coder.yaml create mode 100644 helm/provisioner/templates/rbac.yaml create mode 100644 helm/provisioner/tests/chart_test.go create mode 100644 helm/provisioner/tests/testdata/command.golden create mode 100644 helm/provisioner/tests/testdata/command.yaml create mode 100644 helm/provisioner/tests/testdata/command_args.golden create mode 100644 helm/provisioner/tests/testdata/command_args.yaml create mode 100644 helm/provisioner/tests/testdata/default_values.golden create mode 100644 helm/provisioner/tests/testdata/default_values.yaml create mode 100644 helm/provisioner/tests/testdata/labels_annotations.golden create mode 100644 helm/provisioner/tests/testdata/labels_annotations.yaml create mode 100644 helm/provisioner/tests/testdata/missing_values.yaml create mode 100644 helm/provisioner/tests/testdata/provisionerd_psk.golden create mode 100644 helm/provisioner/tests/testdata/provisionerd_psk.yaml create mode 100644 helm/provisioner/tests/testdata/sa.golden create mode 100644 helm/provisioner/tests/testdata/sa.yaml create mode 100644 helm/provisioner/values.yaml delete mode 100644 helm/templates/coder.yaml delete mode 100644 helm/tests/testdata/tls.golden delete mode 100644 helm/tests/testdata/workspace_proxy.golden diff --git a/.prettierignore b/.prettierignore index 9296d15d88..d68357703d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -67,7 +67,7 @@ scaletest/terraform/secrets.tfvars # .prettierignore.include: # Helm templates contain variables that are invalid YAML and can't be formatted # by Prettier. -helm/templates/*.yaml +helm/**/templates/*.yaml # Terraform state files used in tests, these are automatically generated. # Example: provisioner/terraform/testdata/instance-id/instance-id.tfstate.json diff --git a/.prettierignore.include b/.prettierignore.include index 1f60eda9c5..975c00ca21 100644 --- a/.prettierignore.include +++ b/.prettierignore.include @@ -1,6 +1,6 @@ # Helm templates contain variables that are invalid YAML and can't be formatted # by Prettier. -helm/templates/*.yaml +helm/**/templates/*.yaml # Terraform state files used in tests, these are automatically generated. # Example: provisioner/terraform/testdata/instance-id/instance-id.tfstate.json diff --git a/Makefile b/Makefile index c9089a9d4e..8bb681c9d4 100644 --- a/Makefile +++ b/Makefile @@ -553,7 +553,7 @@ coderd/apidoc/swagger.json: $(shell find ./scripts/apidocgen $(FIND_EXCLUSIONS) ./scripts/apidocgen/generate.sh pnpm run format:write:only ./docs/api ./docs/manifest.json ./coderd/apidoc/swagger.json -update-golden-files: cli/testdata/.gen-golden helm/tests/testdata/.gen-golden scripts/ci-report/testdata/.gen-golden enterprise/cli/testdata/.gen-golden +update-golden-files: cli/testdata/.gen-golden helm/coder/tests/testdata/.gen-golden helm/provisioner/tests/testdata/.gen-golden scripts/ci-report/testdata/.gen-golden enterprise/cli/testdata/.gen-golden .PHONY: update-golden-files cli/testdata/.gen-golden: $(wildcard cli/testdata/*.golden) $(wildcard cli/*.tpl) $(GO_SRC_FILES) $(wildcard cli/*_test.go) @@ -564,8 +564,12 @@ enterprise/cli/testdata/.gen-golden: $(wildcard enterprise/cli/testdata/*.golden go test ./enterprise/cli -run="TestEnterpriseCommandHelp" -update touch "$@" -helm/tests/testdata/.gen-golden: $(wildcard helm/tests/testdata/*.yaml) $(wildcard helm/tests/testdata/*.golden) $(GO_SRC_FILES) $(wildcard helm/tests/*_test.go) - go test ./helm/tests -run=TestUpdateGoldenFiles -update +helm/coder/tests/testdata/.gen-golden: $(wildcard helm/coder/tests/testdata/*.yaml) $(wildcard helm/coder/tests/testdata/*.golden) $(GO_SRC_FILES) $(wildcard helm/coder/tests/*_test.go) + go test ./helm/coder/tests -run=TestUpdateGoldenFiles -update + touch "$@" + +helm/provisioner/tests/testdata/.gen-golden: $(wildcard helm/provisioner/tests/testdata/*.yaml) $(wildcard helm/provisioner/tests/testdata/*.golden) $(GO_SRC_FILES) $(wildcard helm/provisioner/tests/*_test.go) + go test ./helm/provisioner/tests -run=TestUpdateGoldenFiles -update touch "$@" scripts/ci-report/testdata/.gen-golden: $(wildcard scripts/ci-report/testdata/*) $(wildcard scripts/ci-report/*.go) diff --git a/docs/admin/configure.md b/docs/admin/configure.md index 25f6d87583..2240ef4ed5 100644 --- a/docs/admin/configure.md +++ b/docs/admin/configure.md @@ -42,7 +42,7 @@ If you are providing TLS certificates directly to the Coder server, either 1. Use a single certificate and key for both the root and wildcard domains. 2. Configure multiple certificates and keys via - [`coder.tls.secretNames`](https://github.com/coder/coder/blob/main/helm/values.yaml) in the Helm Chart, or + [`coder.tls.secretNames`](https://github.com/coder/coder/blob/main/helm/coder/values.yaml) in the Helm Chart, or [`--tls-cert-file`](../cli/server.md#--tls-cert-file) and [`--tls-key-file`](../cli/server.md#--tls-key-file) command line options (these both take a comma separated list of files; list certificates and their respective keys in the same order). diff --git a/docs/admin/scale.md b/docs/admin/scale.md index 5b5e2369f5..998314061f 100644 --- a/docs/admin/scale.md +++ b/docs/admin/scale.md @@ -42,7 +42,7 @@ Users accessing workspaces via SSH will consume fewer resources, as SSH connecti Workspace builds are CPU-intensive, as it relies on Terraform. Various [Terraform providers](https://registry.terraform.io/browse/providers) have different resource requirements. When tested with our [kubernetes](https://github.com/coder/coder/tree/main/examples/templates/kubernetes) template, `coderd` will consume roughly 0.25 cores per concurrent workspace build. -For effective provisioning, our helm chart prefers to schedule [one coderd replica per-node](https://github.com/coder/coder/blob/main/helm/values.yaml#L188-L202). +For effective provisioning, our helm chart prefers to schedule [one coderd replica per-node](https://github.com/coder/coder/blob/main/helm/coder/values.yaml#L188-L202). We recommend: diff --git a/docs/install/kubernetes.md b/docs/install/kubernetes.md index ade4b058c2..11e8138d7c 100644 --- a/docs/install/kubernetes.md +++ b/docs/install/kubernetes.md @@ -105,7 +105,7 @@ to log in and manage templates. > You can view our > [Helm README](https://github.com/coder/coder/blob/main/helm#readme) for > details on the values that are available, or you can view the - > [values.yaml](https://github.com/coder/coder/blob/main/helm/values.yaml) + > [values.yaml](https://github.com/coder/coder/blob/main/helm/coder/values.yaml) > file directly. 1. Run the following command to install the chart in your cluster. diff --git a/helm/Makefile b/helm/Makefile index a3f689b163..4010cf42d6 100644 --- a/helm/Makefile +++ b/helm/Makefile @@ -13,6 +13,13 @@ all: lint lint: lint/helm .PHONY: lint -lint/helm: - helm lint --strict --set coder.image.tag=v0.0.1 . +lint/helm: lint/helm/coder lint/helm/provisioner .PHONY: lint/helm + +lint/helm/coder: + helm lint --strict --set coder.image.tag=v0.0.1 coder/ +.PHONY: lint/helm/coder + +lint/helm/provisioner: + helm lint --strict --set coder.image.tag=v0.0.1 provisioner/ +.PHONY: lint/helm/provisioner diff --git a/helm/.helmignore b/helm/coder/.helmignore similarity index 100% rename from helm/.helmignore rename to helm/coder/.helmignore diff --git a/helm/coder/Chart.lock b/helm/coder/Chart.lock new file mode 100644 index 0000000000..9692722e19 --- /dev/null +++ b/helm/coder/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: libcoder + repository: file://../libcoder + version: 0.1.0 +digest: sha256:5c9a99109258073b590a9f98268490ef387fde24c0c7c7ade9c1a8c7ef5e6e10 +generated: "2023-08-08T07:27:19.677972411Z" diff --git a/helm/Chart.yaml b/helm/coder/Chart.yaml similarity index 85% rename from helm/Chart.yaml rename to helm/coder/Chart.yaml index a68aa330d8..99f6b71047 100644 --- a/helm/Chart.yaml +++ b/helm/coder/Chart.yaml @@ -21,9 +21,14 @@ keywords: - coder - terraform sources: - - https://github.com/coder/coder/tree/main/helm + - https://github.com/coder/coder/tree/main/helm/coder icon: https://helm.coder.com/coder_logo_black.png maintainers: - name: Coder Technologies, Inc. email: support@coder.com url: https://coder.com/contact + +dependencies: + - name: libcoder + version: 0.1.0 + repository: file://../libcoder diff --git a/helm/README.md b/helm/coder/README.md similarity index 100% rename from helm/README.md rename to helm/coder/README.md diff --git a/helm/coder/charts/libcoder-0.1.0.tgz b/helm/coder/charts/libcoder-0.1.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..b799de7d008196ae4c4a948df1db26d2c5cd4cf5 GIT binary patch literal 2999 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 1.19.0-0" + +keywords: + - coder + - terraform +sources: + - https://github.com/coder/coder/tree/main/helm/provisioner +icon: https://helm.coder.com/coder_logo_black.png +maintainers: + - name: Coder Technologies, Inc. + email: support@coder.com + url: https://coder.com/contact + +dependencies: + - name: libcoder + version: 0.1.0 + repository: file://../libcoder diff --git a/helm/provisioner/charts/libcoder-0.1.0.tgz b/helm/provisioner/charts/libcoder-0.1.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..638d50f976a7eb18335f26a15c12769a83e9f412 GIT binary patch literal 2994 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