diff --git a/cluster/code-server/Chart.yaml b/cluster/code-server/Chart.yaml new file mode 100644 index 0000000..f8e7726 --- /dev/null +++ b/cluster/code-server/Chart.yaml @@ -0,0 +1,35 @@ +# yaml-language-server: $schema=https://json.schemastore.org/chart.json +apiVersion: v2 +type: application +name: code-server +description: A Helm chart for Kubernetes +version: 2.0.0 +# renovate: image=ghcr.io/coder/code-server +appVersion: "4.93.1" + +home: https://charts.pascaliske.dev/charts/code-server/ +sources: + - https://github.com/pascaliske/helm-charts + - https://github.com/coder/code-server +keywords: + - vscode + - code-server + - browser-ide + - ide +maintainers: + - name: pascaliske + email: info@pascaliske.dev + url: https://pascaliske.dev + +dependencies: + - name: base + version: 1.2.0 + repository: https://charts.pascaliske.dev + +annotations: + # possible kinds: added, changed, deprecated, removed, fixed, security + artifacthub.io/changes: | + - kind: removed + description: 'Remove support for CRDs from "traefik.containo.us".' + - kind: added + description: 'Add support for CRDs from "traefik.io".' diff --git a/cluster/code-server/README.md b/cluster/code-server/README.md new file mode 100644 index 0000000..5e0b995 --- /dev/null +++ b/cluster/code-server/README.md @@ -0,0 +1,121 @@ +# [`code-server`](https://charts.pascaliske.dev/charts/code-server/) + +> A Helm chart for Kubernetes + +[![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ](https://charts.pascaliske.dev/charts/code-server/)[![Version: 2.0.0](https://img.shields.io/badge/Version-2.0.0-informational?style=flat-square) ](https://charts.pascaliske.dev/charts/code-server/)[![AppVersion: 4.18.0](https://img.shields.io/badge/AppVersion-4.18.0-informational?style=flat-square) ](https://charts.pascaliske.dev/charts/code-server/) + +* +* + +## Requirements + +- [`helm`](https://helm.sh) - Refer to their [docs](https://helm.sh/docs) to get started. + +## Usage + +To use this chart add the repo as follows: + +```sh +helm repo add pascaliske https://charts.pascaliske.dev +``` + +If you had already added this repo earlier, run `helm repo update` to retrieve the latest versions of the packages. + +To install this chart simply run the following command: + +```sh +helm install code-server pascaliske/code-server +``` + +To uninstall this chart simply run the following command: + +```sh +helm delete code-server +``` + +## Values + +The following values can be used to adjust the helm chart. + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | Pod-level affinity. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling). | +| certificate.annotations | object | `{}` | Additional annotations for the certificate object. | +| certificate.create | bool | `false` | Create an Certificate object for the exposed chart. | +| certificate.dnsNames | list | `[]` | List of subject alternative names for the certificate. | +| certificate.issuerRef.kind | string | `"ClusterIssuer"` | Type of the referenced certificate issuer. Can be "Issuer" or "ClusterIssuer". | +| certificate.issuerRef.name | string | `""` | Name of the referenced certificate issuer. | +| certificate.labels | object | `{}` | Additional labels for the certificate object. | +| certificate.secretName | string | `""` | Name of the secret in which the certificate will be stored. Defaults to the first item in dnsNames. | +| configMap.annotations | object | `{}` | Additional annotations for the config map object. | +| configMap.config | object | `{}` | Map containing the [configuration of code-server](https://coder.com/docs/code-server/latest/guide). | +| configMap.create | bool | `true` | Create a new config map object. | +| configMap.existingConfigMap | string | `""` | Use an existing config map object. | +| configMap.labels | object | `{}` | Additional labels for the config map object. | +| configMap.mountPath | string | `"/etc/code-server"` | Mount path of the config map object. | +| controller.annotations | object | `{}` | Additional annotations for the controller object. | +| controller.enabled | bool | `true` | Create a workload for this chart. | +| controller.kind | string | `"Deployment"` | Type of the workload object. | +| controller.labels | object | `{}` | Additional labels for the controller object. | +| controller.replicas | int | `1` | The number of replicas. | +| cronJob.annotations | object | `{}` | Additional annotations for the cronjob object. | +| cronJob.enabled | bool | `true` | Create a cron job to auto update repositories. | +| cronJob.failedJobsHistoryLimit | int | `1` | The number of failed finished jobs to retain. | +| cronJob.labels | object | `{}` | Additional labels for the cronjob object. | +| cronJob.schedule | string | `"0 */4 * * *"` | Update schedule for the cron job. | +| cronJob.successfulJobsHistoryLimit | int | `3` | The number of successful finished jobs to retain. | +| cronJob.suspend | bool | `false` | Enable/disable the cron job schedule quickly. | +| dnsConfig | object | `{}` | Pod-level dns config. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#hostname-and-name-resolution). | +| env[0] | object | `{"name":"TZ","value":"UTC"}` | Timezone for the container. | +| extraArgs | list | `[]` | List of extra arguments for the container. | +| fullnameOverride | string | `""` | | +| image.pullPolicy | string | `"IfNotPresent"` | The pull policy for the controller. | +| image.repository | string | `"ghcr.io/coder/code-server"` | The repository to pull the image from. | +| image.tag | string | `.Chart.AppVersion` | The docker tag, if left empty chart's appVersion will be used. | +| ingressRoute.annotations | object | `{}` | Additional annotations for the ingress route object. | +| ingressRoute.create | bool | `false` | Create an IngressRoute object for exposing this chart. | +| ingressRoute.entryPoints | list | `[]` | List of [entry points](https://doc.traefik.io/traefik/routing/routers/#entrypoints) on which the ingress route will be available. | +| ingressRoute.labels | object | `{}` | Additional labels for the ingress route object. | +| ingressRoute.middlewares | list | `[]` | List of [middleware objects](https://doc.traefik.io/traefik/routing/providers/kubernetes-crd/#kind-middleware) for the ingress route. | +| ingressRoute.rule | string | `""` | [Matching rule](https://doc.traefik.io/traefik/routing/routers/#rule) for the underlying router. | +| ingressRoute.tlsSecretName | string | `""` | Use an existing secret containing the TLS certificate. | +| nameOverride | string | `""` | | +| persistentVolumeClaim.accessMode | string | `"ReadWriteOnce"` | Access mode of the persistent volume claim object. | +| persistentVolumeClaim.annotations | object | `{}` | Additional annotations for the persistent volume claim object. | +| persistentVolumeClaim.create | bool | `true` | Create a new persistent volume claim object. | +| persistentVolumeClaim.ensurePermissions | bool | `true` | Manage permissions automatically. | +| persistentVolumeClaim.existingPersistentVolumeClaim | string | `""` | Use an existing persistent volume claim object. | +| persistentVolumeClaim.labels | object | `{}` | Additional labels for the persistent volume claim object. | +| persistentVolumeClaim.mountPath | string | `"/home/coder"` | Mount path of the persistent volume claim object. | +| persistentVolumeClaim.projectsPath | string | `"/home/coder/projects"` | Project folder inside the persistent volume claim object. | +| persistentVolumeClaim.size | string | `"1Gi"` | Storage request size for the persistent volume claim object. | +| persistentVolumeClaim.storageClassName | string | `""` | Storage class name for the persistent volume claim object. | +| persistentVolumeClaim.volumeMode | string | `"Filesystem"` | Volume mode of the persistent volume claim object. | +| ports.http.enabled | bool | `true` | Enable the port inside the `controller` and `Service` objects. | +| ports.http.nodePort | string | `nil` | The external port used if `.service.type` == `NodePort`. | +| ports.http.port | int | `8080` | The port used as internal port and cluster-wide port if `.service.type` == `ClusterIP`. | +| ports.http.protocol | string | `"TCP"` | The protocol used for the service. | +| repositories | list | `[]` | List of repositories to be cloned / updated automatically. | +| resources | object | `{}` | Compute resources used by the container. More info [here](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). | +| secret.annotations | object | `{}` | Additional annotations for the secret object. | +| secret.create | bool | `true` | Create a new secret containing the password. | +| secret.existingSecret | string | `""` | Use an existing secret to store the password. | +| secret.labels | object | `{}` | Additional labels for the secret object. | +| secret.password | string | `""` | Password used when not using an existing secret. | +| securityContext | object | `{"fsGroup":1000,"runAsGroup":1000,"runAsNonRoot":true,"runAsUser":1000}` | Pod-level security attributes. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context). | +| service.annotations | object | `{}` | Additional annotations for the service object. | +| service.enabled | bool | `true` | Create a service for exposing this chart. | +| service.labels | object | `{}` | Additional labels for the service object. | +| service.type | string | `"ClusterIP"` | The service type used. | +| serviceAccount.name | string | `""` | Specify the service account used for the controller. | +| tolerations | list | `[]` | Pod-level tolerations. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling). | + +## Maintainers + +| Name | Email | Url | +| ---- | ------ | --- | +| pascaliske | | | + +## License + +[MIT](../LICENSE.md) – © 2023 [Pascal Iske](https://pascaliske.dev) diff --git a/cluster/code-server/README.md.tmpl b/cluster/code-server/README.md.tmpl new file mode 100644 index 0000000..09e9e7f --- /dev/null +++ b/cluster/code-server/README.md.tmpl @@ -0,0 +1,47 @@ +# [`{{ template "chart.name" . }}`]({{ template "chart.homepage" . }}) + +{{ template "chart.deprecationWarning" . }} + +> {{ template "chart.description" . }} + +[{{ template "chart.typeBadge" . }}]({{ template "chart.homepage" . }})[{{ template "chart.versionBadge" . }}]({{ template "chart.homepage" . }})[{{ template "chart.appVersionBadge" . }}]({{ template "chart.homepage" . }}) + +{{ template "chart.sourcesList" . }} + +## Requirements + +- [`helm`](https://helm.sh) - Refer to their [docs](https://helm.sh/docs) to get started. + +## Usage + +To use this chart add the repo as follows: + +```sh +helm repo add pascaliske https://charts.pascaliske.dev +``` + +If you had already added this repo earlier, run `helm repo update` to retrieve the latest versions of the packages. + +To install this chart simply run the following command: + +```sh +helm install {{ template "chart.name" . }} pascaliske/{{ template "chart.name" . }} +``` + +To uninstall this chart simply run the following command: + +```sh +helm delete {{ template "chart.name" . }} +``` + +{{ template "chart.valuesHeader" . }} + +The following values can be used to adjust the helm chart. + +{{ template "chart.valuesTable" . }} + +{{ template "chart.maintainersSection" . }} + +## License + +[MIT](../LICENSE.md) – © {{ now | date "2006" }} [Pascal Iske](https://pascaliske.dev) diff --git a/cluster/code-server/ci/ct-values.yaml b/cluster/code-server/ci/ct-values.yaml new file mode 100644 index 0000000..24ac36d --- /dev/null +++ b/cluster/code-server/ci/ct-values.yaml @@ -0,0 +1,2 @@ +secret: + password: pass123 diff --git a/cluster/code-server/templates/NOTES.txt b/cluster/code-server/templates/NOTES.txt new file mode 100644 index 0000000..97327c6 --- /dev/null +++ b/cluster/code-server/templates/NOTES.txt @@ -0,0 +1,16 @@ +1. Get the application URL by running these commands: +{{- if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "code-server.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "code-server.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "code-server.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.ports.http.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "code-server.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/cluster/code-server/templates/_helpers.tpl b/cluster/code-server/templates/_helpers.tpl new file mode 100644 index 0000000..4111c2f --- /dev/null +++ b/cluster/code-server/templates/_helpers.tpl @@ -0,0 +1,102 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "code-server.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "code-server.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "code-server.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "code-server.labels" -}} +helm.sh/chart: {{ include "code-server.chart" . }} +{{ include "code-server.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "code-server.selectorLabels" -}} +app.kubernetes.io/name: {{ include "code-server.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "code-server.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "code-server.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Certificate name +*/}} +{{- define "code-server.certificate.name" -}} +{{- if not (empty .Values.certificate.dnsNames) }} +{{- first .Values.certificate.dnsNames }} +{{- else }} +{{- include "code-server.fullname" . }} +{{- end }} +{{- end }} + +{{/* +Certificate secret name +*/}} +{{- define "code-server.certificate.secretName" -}} +{{- if not (empty .Values.certificate.secretName) }} +{{- .Values.certificate.secretName }} +{{- else }} +{{- include "code-server.certificate.name" . }} +{{- end }} +{{- end }} + +{{/* +Certificate issuer reference name +*/}} +{{- define "code-server.certificate.issuerRefName" -}} +{{- required "Mandatory field \".certificate.issuerRef.name\" is empty!" .Values.certificate.issuerRef.name -}} +{{- end }} + +{{/* +IngressRoute TLS secret name +*/}} +{{- define "code-server.ingressRoute.tlsSecretName" -}} +{{- if not (empty .Values.ingressRoute.tlsSecretName) }} +{{- .Values.ingressRoute.tlsSecretName }} +{{- else if .Values.certificate.create }} +{{- include "code-server.certificate.name" . }} +{{- end }} +{{- end }} diff --git a/cluster/code-server/templates/certificate.yaml b/cluster/code-server/templates/certificate.yaml new file mode 100644 index 0000000..d0c547b --- /dev/null +++ b/cluster/code-server/templates/certificate.yaml @@ -0,0 +1,26 @@ +{{- if and .Values.certificate.create .Values.service.enabled -}} +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: {{ include "code-server.certificate.name" . }} + labels: + {{- include "code-server.labels" . | nindent 4 }} + {{- with .Values.certificate.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.certificate.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + secretName: {{ include "code-server.certificate.secretName" . }} + {{- with .Values.certificate.dnsNames }} + dnsNames: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.certificate.issuerRef }} + issuerRef: + kind: {{ default "ClusterIssuer" .kind }} + name: {{ include "code-server.certificate.issuerRefName" $ }} + {{- end }} +{{- end }} diff --git a/cluster/code-server/templates/configmap.yaml b/cluster/code-server/templates/configmap.yaml new file mode 100644 index 0000000..c7c1bed --- /dev/null +++ b/cluster/code-server/templates/configmap.yaml @@ -0,0 +1,27 @@ +{{- if and .Values.configMap.create (empty .Values.configMap.existingConfigMap) -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ printf "%s-config" (include "code-server.fullname" .) }} + labels: + {{- include "code-server.labels" . | nindent 4 }} + {{- with .Values.configMap.labels }} + {{ toYaml . | indent 4 }} + {{- end }} + {{- with .Values.configMap.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +data: + config.yml: | + {{- if or $.Values.secret.create $.Values.secret.existingSecret }} + auth: password + {{- else }} + auth: none + {{- end }} + {{- range $key, $val := .Values.configMap.config }} + {{- if ne $key "auth" }} + {{ $key }}: {{ $val }} + {{- end }} + {{- end }} +{{- end }} diff --git a/cluster/code-server/templates/controller.yaml b/cluster/code-server/templates/controller.yaml new file mode 100644 index 0000000..4f39082 --- /dev/null +++ b/cluster/code-server/templates/controller.yaml @@ -0,0 +1,183 @@ +{{- if .Values.controller.enabled -}} +{{- if empty .Values.persistentVolumeClaim.projectsPath -}} + {{- fail "Empty projects path detected in \".Values.persistentVolumeClaim.projectsPath\"!" -}} +{{- end -}} +apiVersion: apps/v1 +kind: {{ include "base.controller.kind" . }} +metadata: + name: {{ include "code-server.fullname" . }} + labels: + {{- include "code-server.labels" . | nindent 4 }} + {{- with .Values.controller.labels }} + {{ toYaml . | indent 4 }} + {{- end }} + {{- with .Values.controller.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.controller.replicas }} + replicas: {{ .Values.controller.replicas }} + {{- end }} + {{- if eq (include "base.controller.kind" . ) "StatefulSet" }} + serviceName: {{ include "code-server.fullname" . }}-headless + {{- end }} + selector: + matchLabels: + {{- include "code-server.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.controller.annotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "code-server.selectorLabels" . | nindent 8 }} + spec: + serviceAccountName: {{ include "code-server.serviceAccountName" . }} + initContainers: + {{- if .Values.persistentVolumeClaim.ensurePermissions }} + - name: {{ printf "%s-permissions" (include "code-server.fullname" .) }} + image: busybox:latest + imagePullPolicy: IfNotPresent + command: ['sh', '-c', 'chown -R {{ .Values.securityContext.runAsUser }}:{{ .Values.securityContext.runAsGroup }} {{ .Values.persistentVolumeClaim.mountPath }}'] + {{- if eq (include "base.persistence.enabled" . ) "true" }} + volumeMounts: + - name: {{ include "base.persistence.suffix" (include "code-server.fullname" . ) }} + mountPath: {{ .Values.persistentVolumeClaim.mountPath }} + {{- end }} + securityContext: + runAsNonRoot: false + runAsGroup: 0 + runAsUser: 0 + {{- end }} + {{- if not (empty .Values.repositories) }} + - name: {{ printf "%s-repositories" (include "code-server.fullname" $) }} + image: alpine/git:latest + imagePullPolicy: IfNotPresent + workingDir: {{ .Values.persistentVolumeClaim.projectsPath }} + command: ['/bin/sh'] + args: ['/scripts/repo-updater.sh'] + volumeMounts: + - name: script-volume + mountPath: /scripts + {{- if eq (include "base.persistence.enabled" . ) "true" }} + - name: {{ include "base.persistence.suffix" (include "code-server.fullname" . ) }} + mountPath: {{ .Values.persistentVolumeClaim.mountPath }} + {{- end }} + {{- end }} + containers: + - name: {{ template "code-server.name" . }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + {{- range $key, $val := .Values.ports }} + {{- if $val.enabled }} + - name: {{ $key | quote }} + containerPort: {{ $val.port }} + protocol: {{ default "TCP" $val.protocol | quote }} + {{- end }} + {{- end }} + env: + {{- range $i, $val := .Values.env }} + - name: {{ $val.name | quote }} + value: {{ $val.value | quote }} + {{- end }} + {{- if .Values.ports.http.enabled }} + - name: PORT + value: {{ .Values.ports.http.port | quote }} + {{- end }} + {{- if or .Values.secret.create (not (empty .Values.secret.existingSecret)) }} + - name: PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.secret.existingSecret }} + name: {{ .Values.secret.existingSecret }} + {{- else }} + name: {{ printf "%s-password" (include "code-server.fullname" .) }} + {{- end }} + key: password + {{- end }} + {{- if .Values.configMap.mountPath }} + - name: CODE_SERVER_CONFIG + value: {{ (printf "%s/config.yml" .Values.configMap.mountPath) | quote }} + {{- end }} + {{- if .Values.persistentVolumeClaim.mountPath }} + - name: XDG_DATA_HOME + value: {{ .Values.persistentVolumeClaim.mountPath | quote }} + {{- end }} + args: {{ if not .Values.extraArgs -}}[]{{- end }} + {{- range $i, $val := .Values.extraArgs }} + - {{ $val }} + {{- end }} + workingDir: {{ .Values.persistentVolumeClaim.projectsPath }} + volumeMounts: + - name: config-volume + mountPath: {{ .Values.configMap.mountPath }} + {{- if eq (include "base.persistence.enabled" . ) "true" }} + - name: {{ include "base.persistence.suffix" (include "code-server.fullname" . ) }} + mountPath: {{ .Values.persistentVolumeClaim.mountPath }} + {{- end }} + livenessProbe: + httpGet: + path: / + port: http + readinessProbe: + httpGet: + path: / + port: http + resources: {{ if not .Values.resources -}}{}{{- end }} + {{- if .Values.resources }} + {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + volumes: + {{- if not (empty .Values.repositories) }} + - name: script-volume + configMap: + name: {{ printf "%s-scripts" (include "code-server.fullname" . ) }} + defaultMode: 0777 + {{- end }} + - name: config-volume + configMap: + {{- if and .Values.configMap.create (empty .Values.configMap.existingConfigMap) }} + name: {{ printf "%s-config" (include "code-server.fullname" . ) }} + {{- else }} + name: {{ .Values.configMap.existingConfigMap }} + {{- end }} + {{- if eq (include "base.persistence.enabled" . ) "true" }} + {{- if eq (include "base.persistence.type" . ) "volumes" }} + - name: {{ include "base.persistence.suffix" (include "code-server.fullname" . ) }} + persistentVolumeClaim: + {{- if eq (include "base.persistence.created" . ) "true" }} + claimName: {{ include "base.persistence.suffix" (include "code-server.fullname" . ) }} + {{- else }} + claimName: {{ .Values.persistentVolumeClaim.existingPersistentVolumeClaim }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.securityContext }} + securityContext: + {{- toYaml .Values.securityContext | nindent 8 }} + {{- end }} + {{- if .Values.dnsConfig }} + dnsConfig: + {{- toYaml .Values.dnsConfig | nindent 12 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if eq (include "base.persistence.enabled" . ) "true" }} + {{- if eq (include "base.persistence.type" . ) "volumeClaimTemplates" }} + volumeClaimTemplates: + - metadata: + name: {{ include "base.persistence.suffix" (include "code-server.fullname" . ) }} + spec: + {{- include "base.persistence.spec" . | nindent 8 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/cluster/code-server/templates/cronjob.yaml b/cluster/code-server/templates/cronjob.yaml new file mode 100644 index 0000000..f189710 --- /dev/null +++ b/cluster/code-server/templates/cronjob.yaml @@ -0,0 +1,75 @@ +{{- if and .Values.cronJob.enabled (not (empty .Values.repositories)) -}} +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{ printf "%s-autoupdate" (include "code-server.fullname" .) }} + labels: + {{- include "code-server.labels" . | nindent 4 }} + {{- with .Values.cronJob.labels }} + {{ toYaml . | indent 4 }} + {{- end }} + {{- with .Values.cronJob.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + schedule: {{ .Values.cronJob.schedule }} + suspend: {{ .Values.cronJob.suspend }} + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: {{ .Values.cronJob.successfulJobsHistoryLimit }} + failedJobsHistoryLimit: {{ .Values.cronJob.failedJobsHistoryLimit }} + jobTemplate: + {{- with .Values.cronJob.annotations }} + metadata: + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + template: + spec: + restartPolicy: Never + containers: + - name: {{ printf "%s-repositories" (include "code-server.fullname" $) }} + image: alpine/git:latest + imagePullPolicy: IfNotPresent + workingDir: {{ .Values.persistentVolumeClaim.projectsPath }} + command: ['/bin/sh'] + args: ['/scripts/repo-updater.sh'] + volumeMounts: + - name: script-volume + mountPath: /scripts + - name: {{ include "base.persistence.suffix" (include "code-server.fullname" . ) }} + mountPath: {{ .Values.persistentVolumeClaim.mountPath }} + volumes: + {{- if not (empty .Values.repositories) }} + - name: script-volume + configMap: + name: {{ printf "%s-scripts" (include "code-server.fullname" . ) }} + defaultMode: 0777 + {{- end }} + {{- if eq (include "base.persistence.enabled" . ) "true" }} + - name: {{ include "base.persistence.suffix" (include "code-server.fullname" . ) }} + persistentVolumeClaim: + {{- if eq (include "base.persistence.created" . ) "true" }} + claimName: {{ include "base.persistence.suffix" (include "code-server.fullname" . ) }} + {{- else }} + claimName: {{ .Values.persistentVolumeClaim.existingPersistentVolumeClaim }} + {{- end }} + {{- end }} + {{- if .Values.securityContext }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + {{- end }} + {{- if .Values.dnsConfig }} + dnsConfig: + {{- toYaml .Values.dnsConfig | nindent 12 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 12 }} + {{- end }} +{{- end -}} diff --git a/cluster/code-server/templates/headless-service.yaml b/cluster/code-server/templates/headless-service.yaml new file mode 100644 index 0000000..42eaf27 --- /dev/null +++ b/cluster/code-server/templates/headless-service.yaml @@ -0,0 +1,21 @@ +{{- if eq (include "base.controller.kind" . ) "StatefulSet" -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "code-server.fullname" . }}-headless + labels: + {{- include "code-server.labels" . | nindent 4 }} +spec: + clusterIP: None + ports: + {{- range $key, $val := .Values.ports }} + {{- if $val.enabled }} + - name: {{ $key | quote }} + port: {{ $val.port }} + targetPort: {{ $key | quote }} + protocol: {{ default "TCP" $val.protocol | quote }} + {{- end }} + {{- end }} + selector: + {{- include "code-server.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/cluster/code-server/templates/ingressroute.yaml b/cluster/code-server/templates/ingressroute.yaml new file mode 100644 index 0000000..e9d9277 --- /dev/null +++ b/cluster/code-server/templates/ingressroute.yaml @@ -0,0 +1,36 @@ +{{- if and .Values.ingressRoute.create .Values.service.enabled -}} +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: {{ printf "%s-route" (include "code-server.fullname" . ) }} + labels: + {{- include "code-server.labels" . | nindent 4 }} + {{- with .Values.ingressRoute.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.ingressRoute.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with .Values.ingressRoute.entryPoints }} + entryPoints: + {{- toYaml . | nindent 4 }} + {{- end }} + routes: + - kind: Rule + match: {{ required "Mandatory field \".ingressRoute.rule\" is empty!" .Values.ingressRoute.rule }} + services: + - kind: Service + name: {{ include "code-server.fullname" . }} + namespace: {{ .Release.Namespace }} + port: {{ .Values.ports.http.port }} + {{- with .Values.ingressRoute.middlewares }} + middlewares: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with (include "code-server.ingressRoute.tlsSecretName" .) }} + tls: + secretName: {{ . }} + {{- end }} +{{- end }} diff --git a/cluster/code-server/templates/persistantvolumeclaim.yaml b/cluster/code-server/templates/persistantvolumeclaim.yaml new file mode 100644 index 0000000..ecf1ad9 --- /dev/null +++ b/cluster/code-server/templates/persistantvolumeclaim.yaml @@ -0,0 +1,19 @@ +{{- if eq (include "base.persistence.enabled" . ) "true" -}} +{{- if eq (include "base.persistence.type" . ) "volumes" -}} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ printf "%s-storage" (include "code-server.fullname" .) }} + labels: + {{- include "code-server.labels" . | nindent 4 }} + {{- with .Values.persistentVolumeClaim.labels }} + {{ toYaml . | indent 4 }} + {{- end }} + {{- with .Values.persistentVolumeClaim.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- include "base.persistence.spec" . | nindent 2 }} +{{- end }} +{{- end }} diff --git a/cluster/code-server/templates/scripts.yaml b/cluster/code-server/templates/scripts.yaml new file mode 100644 index 0000000..32bfe54 --- /dev/null +++ b/cluster/code-server/templates/scripts.yaml @@ -0,0 +1,26 @@ +{{- if not (empty .Values.repositories) -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ printf "%s-scripts" (include "code-server.fullname" .) }} + labels: + {{- include "code-server.labels" . | nindent 4 }} +data: + repo-updater.sh: | + #!/bin/sh + + # update the given repository + update() { + if [ ! -d "$1/.git" ] + then + git clone "$2" "$1" + else + cd "$1" && git fetch && git pull "$2" + fi + } + + # list of repositories to update + {{- range $i, $repo := .Values.repositories }} + update "{{ $repo.name }}" "{{ $repo.url }}" + {{- end }} +{{- end }} diff --git a/cluster/code-server/templates/secret.yaml b/cluster/code-server/templates/secret.yaml new file mode 100644 index 0000000..f65beb7 --- /dev/null +++ b/cluster/code-server/templates/secret.yaml @@ -0,0 +1,18 @@ +{{- if and .Values.secret.create (empty .Values.secret.existingSecret) -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ printf "%s-password" (include "code-server.fullname" .) }} + labels: + {{- include "code-server.labels" . | nindent 4 }} + {{- with .Values.secret.labels }} + {{ toYaml . | indent 4 }} + {{- end }} + {{- with .Values.secret.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +type: Opaque +data: + password: {{ required "Mandatory field \".secret.password\" is empty!" .Values.secret.password | b64enc }} +{{- end -}} diff --git a/cluster/code-server/templates/service.yaml b/cluster/code-server/templates/service.yaml new file mode 100644 index 0000000..b54fa33 --- /dev/null +++ b/cluster/code-server/templates/service.yaml @@ -0,0 +1,31 @@ +{{- if .Values.service.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "code-server.fullname" . }} + labels: + {{- include "code-server.labels" . | nindent 4 }} + {{- with .Values.service.labels }} + {{ toYaml . | indent 4 }} + {{- end }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + ports: + {{- range $key, $val := .Values.ports }} + {{- if $val.enabled }} + - name: {{ $key | quote }} + port: {{ $val.port }} + targetPort: {{ $key | quote }} + {{- if and (eq $.Values.service.type "NodePort") $val.nodePort }} + nodePort: {{ $val.nodePort }} + {{- end }} + protocol: {{ default "TCP" $val.protocol | quote }} + {{- end }} + {{- end }} + selector: + {{- include "code-server.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/cluster/code-server/values.yaml b/cluster/code-server/values.yaml new file mode 100644 index 0000000..ea65ca6 --- /dev/null +++ b/cluster/code-server/values.yaml @@ -0,0 +1,204 @@ +image: + # -- The repository to pull the image from. + repository: ghcr.io/coder/code-server + # -- The docker tag, if left empty chart's appVersion will be used. + # @default -- `.Chart.AppVersion` + tag: '' + # -- The pull policy for the controller. + pullPolicy: IfNotPresent + +nameOverride: '' +fullnameOverride: '' + +controller: + # -- Create a workload for this chart. + enabled: true + # -- Type of the workload object. + kind: Deployment + # -- The number of replicas. + replicas: 1 + # -- Additional annotations for the controller object. + annotations: {} + # -- Additional labels for the controller object. + labels: {} + +service: + # -- Create a service for exposing this chart. + enabled: true + # -- The service type used. + type: ClusterIP + # -- Additional annotations for the service object. + annotations: {} + # -- Additional labels for the service object. + labels: {} + +ingressRoute: + # -- Create an IngressRoute object for exposing this chart. + create: true + # -- List of [entry points](https://doc.traefik.io/traefik/routing/routers/#entrypoints) on which the ingress route will be available. + entryPoints: + - websecure + # -- [Matching rule](https://doc.traefik.io/traefik/routing/routers/#rule) for the underlying router. + rule: 'Host(`coder.traefik.local`)' + # -- List of [middleware objects](https://doc.traefik.io/traefik/routing/providers/kubernetes-crd/#kind-middleware) for the ingress route. + middlewares: [] + # -- Use an existing secret containing the TLS certificate. + tlsSecretName: 'coder.taefik.local' + # -- Additional annotations for the ingress route object. + annotations: {} + # -- Additional labels for the ingress route object. + labels: {} + +certificate: + # -- Create an Certificate object for the exposed chart. + create: true + # -- List of subject alternative names for the certificate. + dnsNames: + - 'coder.taefik.local' + # -- Name of the secret in which the certificate will be stored. Defaults to the first item in dnsNames. + secretName: 'coder.taefik.local' + issuerRef: + # -- Type of the referenced certificate issuer. Can be "Issuer" or "ClusterIssuer". + kind: ClusterIssuer + # -- Name of the referenced certificate issuer. + name: 'selfsigned' + # -- Additional annotations for the certificate object. + annotations: {} + # -- Additional labels for the certificate object. + labels: {} + +env: + # -- Timezone for the container. + - name: TZ + value: UTC + +# -- List of extra arguments for the container. +extraArgs: [] + # - --disable-telemetry + +ports: + http: + # -- Enable the port inside the `controller` and `Service` objects. + enabled: true + # -- The port used as internal port and cluster-wide port if `.service.type` == `ClusterIP`. + port: 8080 + # -- The external port used if `.service.type` == `NodePort`. + nodePort: null + # -- The protocol used for the service. + protocol: TCP + +configMap: + # -- Create a new config map object. + create: true + # -- Mount path of the config map object. + mountPath: /etc/code-server + # -- Use an existing config map object. + existingConfigMap: '' + # -- Map containing the [configuration of code-server](https://coder.com/docs/code-server/latest/guide). + config: {} + # -- Additional annotations for the config map object. + annotations: {} + # -- Additional labels for the config map object. + labels: {} + +secret: + # -- Create a new secret containing the password. + create: true + # -- Use an existing secret to store the password. + existingSecret: '' + # -- Password used when not using an existing secret. + password: 'pass123' + # -- Additional annotations for the secret object. + annotations: {} + # -- Additional labels for the secret object. + labels: {} + +persistentVolumeClaim: + # -- Create a new persistent volume claim object. + create: true + # -- Mount path of the persistent volume claim object. + mountPath: /home/coder + # -- Access mode of the persistent volume claim object. + accessMode: ReadWriteOnce + # -- Volume mode of the persistent volume claim object. + volumeMode: Filesystem + # -- Storage request size for the persistent volume claim object. + size: 1Gi + # -- Storage class name for the persistent volume claim object. + storageClassName: '' + # -- Project folder inside the persistent volume claim object. + projectsPath: /home/coder/projects + # -- Manage permissions automatically. + ensurePermissions: true + # -- Use an existing persistent volume claim object. + existingPersistentVolumeClaim: '' + # -- Additional annotations for the persistent volume claim object. + annotations: {} + # -- Additional labels for the persistent volume claim object. + labels: {} + +# -- List of repositories to be cloned / updated automatically. +repositories: [] + # - url: https://github.com/pascaliske/helm-charts + # name: helm-charts + +cronJob: + # -- Create a cron job to auto update repositories. + enabled: true + # -- Update schedule for the cron job. + schedule: '0 */4 * * *' + # -- Enable/disable the cron job schedule quickly. + suspend: false + # -- The number of successful finished jobs to retain. + successfulJobsHistoryLimit: 3 + # -- The number of failed finished jobs to retain. + failedJobsHistoryLimit: 1 + # -- Additional annotations for the cronjob object. + annotations: {} + # -- Additional labels for the cronjob object. + labels: {} + +serviceAccount: + # -- Specify the service account used for the controller. + name: '' + +# -- Pod-level security attributes. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context). +securityContext: + fsGroup: 1000 + runAsNonRoot: true + runAsGroup: 1000 + runAsUser: 1000 + +# -- Pod-level dns config. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#hostname-and-name-resolution). +dnsConfig: {} + # nameservers: [] + # searches: [] + # options: + # - name: '' + # value: '' + +# -- Compute resources used by the container. More info [here](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). +resources: {} + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +# -- Pod-level affinity. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling). +affinity: {} + # nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: kubernetes.io/hostname + # operator: In + # values: + # - my-node-xyz + +# -- Pod-level tolerations. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling). +tolerations: [] + # - key: node-role.kubernetes.io/control-plane + # operator: Exists + # effect: NoSchedule