diff --git a/README.md b/README.md index 23294b71..1fbb8796 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,10 @@ View [our docs](https://coder.com/docs/setup/installation) for detailed installa | certs | object | Certificate that will be mounted inside Coder services. | `{"secret":{"key":"","name":""}}` | | certs.secret.key | string | Key pointing to a certificate in the secret. | `""` | | certs.secret.name | string | Name of the secret. | `""` | -| coderd | object | Primary service responsible for all things Coder! | `{"affinity":{"podAntiAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"podAffinityTerm":{"labelSelector":{"matchExpressions":[{"key":"app.kubernetes.io/name","operator":"In","values":["coderd"]}]},"topologyKey":"kubernetes.io/hostname"},"weight":1}]}},"alternateHostnames":[],"builtinProviderServiceAccount":{"annotations":{},"labels":{}},"clientTLS":{"secretName":""},"devurlsHost":"","extraLabels":{},"image":"","networkPolicy":{"enable":true},"oidc":{"enableRefresh":false,"redirectOptions":{}},"podSecurityContext":{"runAsGroup":1000,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}},"proxy":{"exempt":"cluster.local","http":"","https":""},"replicas":1,"resources":{"limits":{"cpu":"250m","memory":"512Mi"},"requests":{"cpu":"250m","memory":"512Mi"}},"reverseProxy":{"headers":[],"trustedOrigins":[]},"satellite":{"accessURL":"","enable":false,"primaryURL":""},"securityContext":{"allowPrivilegeEscalation":false,"readOnlyRootFilesystem":true,"runAsGroup":1000,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}},"serviceNodePorts":{"http":null,"https":null},"serviceSpec":{"externalTrafficPolicy":"Local","loadBalancerIP":"","loadBalancerSourceRanges":[],"type":"LoadBalancer"},"superAdmin":{"passwordSecret":{"key":"password","name":""}},"tls":{"devurlsHostSecretName":"","hostSecretName":""},"trustProxyIP":false}` | +| coderd | object | Primary service responsible for all things Coder! | `{"affinity":{"podAntiAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"podAffinityTerm":{"labelSelector":{"matchExpressions":[{"key":"app.kubernetes.io/name","operator":"In","values":["coderd"]}]},"topologyKey":"kubernetes.io/hostname"},"weight":1}]}},"alternateHostnames":[],"annotations":{},"builtinProviderServiceAccount":{"annotations":{},"labels":{}},"clientTLS":{"secretName":""},"devurlsHost":"","extraLabels":{},"image":"","networkPolicy":{"enable":true},"oidc":{"enableRefresh":false,"redirectOptions":{}},"podSecurityContext":{"runAsGroup":1000,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}},"proxy":{"exempt":"cluster.local","http":"","https":""},"replicas":1,"resources":{"limits":{"cpu":"250m","memory":"512Mi"},"requests":{"cpu":"250m","memory":"512Mi"}},"reverseProxy":{"headers":[],"trustedOrigins":[]},"satellite":{"accessURL":"","enable":false,"primaryURL":""},"securityContext":{"allowPrivilegeEscalation":false,"readOnlyRootFilesystem":true,"runAsGroup":1000,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}},"serviceAnnotations":{},"serviceNodePorts":{"http":null,"https":null},"serviceSpec":{"externalTrafficPolicy":"Local","loadBalancerIP":"","loadBalancerSourceRanges":[],"type":"LoadBalancer"},"superAdmin":{"passwordSecret":{"key":"password","name":""}},"tls":{"devurlsHostSecretName":"","hostSecretName":""},"trustProxyIP":false}` | | coderd.affinity | object | Allows specifying an affinity rule for the `coderd` deployment. The default rule prefers to schedule coderd pods on different nodes, which is only applicable if coderd.replicas is greater than 1. | `{"podAntiAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"podAffinityTerm":{"labelSelector":{"matchExpressions":[{"key":"app.kubernetes.io/name","operator":"In","values":["coderd"]}]},"topologyKey":"kubernetes.io/hostname"},"weight":1}]}}` | | coderd.alternateHostnames | list | A list of hostnames that coderd (including satellites) will allow for OIDC. If this list is not set, all OIDC traffic will go to the configured access URL in the admin settings on the dashboard (or the satellite's primary URL as configured by Helm). | `[]` | +| coderd.annotations | object | Apply annotations to the coderd deployment. https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ | `{}` | | coderd.builtinProviderServiceAccount | object | Customize the built-in Kubernetes provider service account. | `{"annotations":{},"labels":{}}` | | coderd.builtinProviderServiceAccount.annotations | object | A KV mapping of annotations. See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ | `{}` | | coderd.builtinProviderServiceAccount.labels | object | Add labels to the service account used for the built-in provider. | `{}` | @@ -63,6 +64,7 @@ View [our docs](https://coder.com/docs/setup/installation) for detailed installa | coderd.securityContext.runAsNonRoot | bool | Requires that the coderd and migrations containers run as an unprivileged user. If setting runAsUser to 0 (root), this will need to be set to false. | `true` | | coderd.securityContext.runAsUser | int | Sets the user id of the pod. For security reasons, we recommend using a non-root user. | `1000` | | coderd.securityContext.seccompProfile | object | Sets the seccomp profile for the migration and runtime containers. | `{"type":"RuntimeDefault"}` | +| coderd.serviceAnnotations | object | Apply annotations to the coderd service. https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ | `{}` | | coderd.serviceNodePorts | object | Allows manually setting static node ports for the coderd service. This is only helpful if static ports are required, and usually should be left alone. By default these are dynamically chosen. | `{"http":null,"https":null}` | | coderd.serviceNodePorts.http | string | Sets a static 'coderd' service non-TLS nodePort. This should usually be omitted. | `nil` | | coderd.serviceNodePorts.https | string | Sets a static 'coderd' service TLS nodePort This should usually be omitted. | `nil` | @@ -96,7 +98,8 @@ View [our docs](https://coder.com/docs/setup/installation) for detailed installa | metrics.amplitudeKey | string | Enables telemetry pushing to Amplitude. Amplitude records how users interact with Coder, which is used to improve the product. No events store any personal information. Amplitude can be found here: https://amplitude.com/ Keep empty to disable. | `""` | | postgres.connector | string | Option for configuring database connector type. valid values are: - "postgres" -- default connector - "awsiamrds" -- uses AWS IAM account in environment to authenticate using IAM to connect to an RDS instance. | `"postgres"` | | postgres.database | string | Name of the database that Coder will use. You must create this database first. | `""` | -| postgres.default | object | Configure a built-in PostgreSQL deployment. | `{"enable":true,"image":"","networkPolicy":{"enable":true},"resources":{"limits":{"cpu":"250m","memory":"1Gi"},"requests":{"cpu":"250m","memory":"1Gi","storage":"10Gi"}},"storageClassName":""}` | +| postgres.default | object | Configure a built-in PostgreSQL deployment. | `{"annotations":{},"enable":true,"image":"","networkPolicy":{"enable":true},"resources":{"limits":{"cpu":"250m","memory":"1Gi"},"requests":{"cpu":"250m","memory":"1Gi","storage":"10Gi"}},"storageClassName":""}` | +| postgres.default.annotations | object | Apply annotations to the default postgres service. https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ | `{}` | | postgres.default.enable | bool | Deploys a PostgreSQL instance. We recommend using an external PostgreSQL instance in production. If true, all other values are ignored. | `true` | | postgres.default.image | string | Injected by Coder during release. | `""` | | postgres.default.networkPolicy | object | Configure the network policy to apply to the built-in PostgreSQL deployment. | `{"enable":true}` | @@ -119,7 +122,7 @@ View [our docs](https://coder.com/docs/setup/installation) for detailed installa | postgres.sslMode | string | Provides variable levels of protection for the PostgreSQL connection. For acceptable values, see: https://www.postgresql.org/docs/11/libpq-ssl.html | `"require"` | | postgres.user | string | User of the external PostgreSQL instance. | `""` | | services | object | Kubernetes Service configuration that applies to Coder services. | `{"annotations":{},"clusterDomainSuffix":".svc.cluster.local","nodeSelector":{"kubernetes.io/arch":"amd64","kubernetes.io/os":"linux"},"tolerations":[],"type":"ClusterIP"}` | -| services.annotations | object | A KV mapping of annotations. See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ | `{}` | +| services.annotations | object | A KV mapping of annotations. See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ DEPRECATED -- Please use the annotations value for each object. | `{}` | | services.clusterDomainSuffix | string | Custom domain suffix for DNS resolution in your cluster. See: https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/ | `".svc.cluster.local"` | | services.nodeSelector | object | See: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector | `{"kubernetes.io/arch":"amd64","kubernetes.io/os":"linux"}` | | services.tolerations | list | Each element is a toleration object. See: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ | `[]` | diff --git a/templates/coderd.yaml b/templates/coderd.yaml index c0d84187..374b3970 100644 --- a/templates/coderd.yaml +++ b/templates/coderd.yaml @@ -13,7 +13,7 @@ metadata: app.kubernetes.io/component: {{ include "coder.serviceName" . }} app: {{ include "coder.serviceName" . }} coder.deployment: {{ include "coder.serviceName" . }} - annotations: {{ toYaml .Values.services.annotations | nindent 4 }} + annotations: {{ toYaml (merge (.Values.coderd.annotations | default (dict)) (.Values.services.annotations | default (dict))) | nindent 4 }} spec: replicas: {{ default 1 .Values.coderd.replicas }} strategy: @@ -36,7 +36,7 @@ spec: {{- with .Values.coderd.extraLabels -}} {{ toYaml . | nindent 8 }} {{- end }} - annotations: {{ toYaml .Values.services.annotations | nindent 8 }} + annotations: {{ toYaml (merge (.Values.coderd.annotations | default (dict)) (.Values.services.annotations | default (dict))) | nindent 8 }} spec: securityContext: {{ toYaml .Values.coderd.podSecurityContext | nindent 8 }} restartPolicy: Always @@ -261,7 +261,7 @@ metadata: app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/version: {{ .Chart.AppVersion }} app.kubernetes.io/component: {{ include "coder.serviceName" . }} - annotations: {{ toYaml .Values.services.annotations | nindent 4 }} + annotations: {{ toYaml (merge (.Values.coderd.serviceAnnotations | default (dict)) (.Values.services.annotations | default (dict))) | nindent 4 }} spec: {{- if .Values.coderd.serviceSpec }} {{- toYaml .Values.coderd.serviceSpec | nindent 2 }} diff --git a/templates/timescale.yaml b/templates/timescale.yaml index 7c1ce77b..720b9b45 100644 --- a/templates/timescale.yaml +++ b/templates/timescale.yaml @@ -33,7 +33,7 @@ metadata: app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/version: {{ .Chart.AppVersion }} app.kubernetes.io/component: {{ include "timescale.serviceName" . }} - annotations: {{ toYaml .Values.services.annotations | nindent 4 }} + annotations: {{ toYaml (merge (.Values.postgres.default.annotations | default (dict)) (.Values.services.annotations | default (dict))) | nindent 4 }} spec: serviceName: {{ include "timescale.serviceName" . }} replicas: 1 @@ -55,7 +55,7 @@ spec: app.kubernetes.io/component: {{ include "timescale.serviceName" . }} app: timescale coder.deployment: timescale - annotations: {{ toYaml .Values.services.annotations | nindent 8 }} + annotations: {{ toYaml (merge (.Values.postgres.default.annotations | default (dict)) (.Values.services.annotations | default (dict))) | nindent 8 }} spec: serviceAccountName: timescale securityContext: diff --git a/tests/annotations_test.go b/tests/annotations_test.go new file mode 100644 index 00000000..1cfdfa56 --- /dev/null +++ b/tests/annotations_test.go @@ -0,0 +1,107 @@ +package tests + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// Ensures services.annotations values and the individual annotations per object +// are applied correctly. +func TestAnnotations(t *testing.T) { + t.Parallel() + + var ( + chart = LoadChart(t) + + expectedGlobal = map[string]string{ + "global-key": "global-value", + // Should be overwritten by some children. + "key": "global-value", + } + expectedCoderd = map[string]string{ + "key": "value", + "key2": "value2", + } + expectedCoderdService = map[string]string{ + "key": "value", + "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "http", + } + expectedTimescale = map[string]string{ + "key2": "value", + } + ) + + objs := chart.MustRender(t, func(cv *CoderValues) { + // Ensure backwards compatibility and merging order. + cv.Services.Annotations = expectedGlobal + + cv.Coderd.Annotations = expectedCoderd + cv.Coderd.ServiceAnnotations = expectedCoderdService + cv.Postgres.Default.Annotations = expectedTimescale + }) + + depl := MustFindDeployment(t, objs, "coderd") + assert.Equal(t, mergeAnnotations(expectedGlobal, expectedCoderd), depl.Annotations) + + svc := MustFindService(t, objs, "coderd") + assert.Equal(t, mergeAnnotations(expectedGlobal, expectedCoderdService), svc.Annotations) + + db := MustFindStatefulSet(t, objs, "timescale") + assert.Equal(t, mergeAnnotations(expectedGlobal, expectedTimescale), db.Annotations) +} + +func TestAnnotationsEmpty(t *testing.T) { + t.Parallel() + + var ( + chart = LoadChart(t) + objs = chart.MustRender(t, nil) + ) + + depl := MustFindDeployment(t, objs, "coderd") + assert.Empty(t, depl.Annotations) + + svc := MustFindService(t, objs, "coderd") + assert.Empty(t, svc.Annotations) + + db := MustFindStatefulSet(t, objs, "timescale") + assert.Empty(t, db.Annotations) +} + +func TestAnnotationsNull(t *testing.T) { + t.Parallel() + + var ( + chart = LoadChart(t) + objs = chart.MustRender(t, func(cv *CoderValues) { + cv.Coderd.Annotations = nil + cv.Coderd.ServiceAnnotations = nil + cv.Postgres.Default.Annotations = nil + cv.Services.Annotations = nil + }) + ) + + depl := MustFindDeployment(t, objs, "coderd") + assert.Empty(t, depl.Annotations) + + svc := MustFindService(t, objs, "coderd") + assert.Empty(t, svc.Annotations) + + db := MustFindStatefulSet(t, objs, "timescale") + assert.Empty(t, db.Annotations) +} + +// mergeAnnotations copies `a` into a new map, then it copies all key/value +// pairs from `b` on top of that copy. +func mergeAnnotations(a, b map[string]string) map[string]string { + out := map[string]string{} + for k, v := range a { + out[k] = v + } + for k, v := range b { + out[k] = v + } + + return out +} diff --git a/tests/values.go b/tests/values.go index 50017793..8169e538 100644 --- a/tests/values.go +++ b/tests/values.go @@ -95,8 +95,10 @@ type CoderdValues struct { Proxy *CoderdProxyValues `json:"proxy" yaml:"proxy"` ReverseProxy *CoderdReverseProxyValues `json:"reverseProxy" yaml:"reverseProxy"` NetworkPolicy *CoderdNetworkPolicyValues `json:"networkPolicy" yaml:"networkPolicy"` + Annotations map[string]string `json:"annotations" yaml:"annotations"` ClientTLS *CoderdClientTLSValues `json:"clientTLS" yaml:"clientTLS"` AlternateHostnames []string `json:"alternateHostnames" yaml:"alternateHostnames"` + ServiceAnnotations map[string]string `json:"serviceAnnotations" yaml:"serviceAnnotations"` } type CoderdClientTLSValues struct { @@ -253,6 +255,7 @@ type PostgresDefaultValues struct { StorageClassName *string `json:"storageClassName" yaml:"storageClassName"` Resources *corev1.ResourceRequirements `json:"resources" yaml:"resources"` NetworkPolicy *PostgresDefaultNetworkPolicyValues `json:"networkPolicy" yaml:"networkPolicy"` + Annotations map[string]string `json:"annotations" yaml:"annotations"` } // PostgresDefaultNetworkPolicyValues reflect values from @@ -263,11 +266,11 @@ type PostgresDefaultNetworkPolicyValues struct { // ServicesValues reflect the values from services. type ServicesValues struct { - Annotations map[string]string `json:"annotations" yaml:"annotations"` ClusterDomainSuffix *string `json:"clusterDomainSuffix" yaml:"clusterDomainSuffix"` Tolerations *[]corev1.Toleration `json:"tolerations" yaml:"tolerations"` NodeSelector map[string]string `json:"nodeSelector" yaml:"nodeSelector"` Type *corev1.ServiceType `json:"type" yaml:"type"` + Annotations map[string]string `json:"annotations" yaml:"annotations"` } // String returns the string representation of the values. diff --git a/values.yaml b/values.yaml index 3053295a..33417756 100644 --- a/values.yaml +++ b/values.yaml @@ -4,7 +4,12 @@ coderd: image: "" # coderd.replicas -- The number of Kubernetes Pod replicas. replicas: 1 - + # coderd.annotations -- Apply annotations to the coderd deployment. + # https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + annotations: {} + # coderd.serviceAnnotations -- Apply annotations to the coderd service. + # https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + serviceAnnotations: {} # coderd.serviceSpec -- Specification to inject for the coderd service. See: # https://kubernetes.io/docs/concepts/services-networking/service/ serviceSpec: @@ -370,11 +375,15 @@ postgres: # PostgreSQL using Helm. If false, no policies will be created for the # built-in database. enable: true + # postgres.default.annotations -- Apply annotations to the default postgres service. + # https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + annotations: {} # services -- Kubernetes Service configuration that applies to Coder services. services: # services.annotations -- A KV mapping of annotations. See: # https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + # DEPRECATED -- Please use the annotations value for each object. annotations: {} # services.clusterDomainSuffix -- Custom domain suffix for DNS resolution in your cluster. See: # https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/