diff --git a/build/templates/values.yaml b/build/templates/values.yaml index e011a2a5..420092a9 100644 --- a/build/templates/values.yaml +++ b/build/templates/values.yaml @@ -69,14 +69,35 @@ conf: log: enabled: false # https://www.cockroachlabs.com/docs/v21.1/configure-logs - config: {} + config: # file-defaults: - # dir: /custom/dir/path/ + # dir: /cockroach/cockroach-logs # fluent-defaults: # format: json-fluent # sinks: # stderr: # channels: [DEV] + persistentVolume: + # If enabled, then a PersistentVolumeClaim will be created and + # used to store CockroachDB's logs. + enabled: false + # CockroachDB's logs volume mount path. This gets prepended with + # `/cockroach/` in the stateful set. The `conf.log.config` should have + # `file-defaults.dir` to specify the log path and should reference the + # mounted volume. + path: cockroach-logs + size: 10Gi + # If defined, then `storageClassName: `. + # If set to "-", then `storageClassName: ""`, which disables dynamic + # provisioning. + # If undefined or empty (default), then no `storageClassName` spec is + # set, so the default provisioner will be chosen (gp2 on AWS, standard + # on GKE, AWS & OpenStack). + storageClass: "" + # Additional labels to apply to the created PersistentVolumeClaims. + labels: {} + # Additional annotations to apply to the created PersistentVolumeClaims. + annotations: {} # Logs at or above this threshold to STDERR. Ignored when "log" is enabled logtostderr: INFO diff --git a/cockroachdb/templates/_helpers.tpl b/cockroachdb/templates/_helpers.tpl index 9ef769a7..57030434 100644 --- a/cockroachdb/templates/_helpers.tpl +++ b/cockroachdb/templates/_helpers.tpl @@ -289,3 +289,17 @@ Validate that if user enabled tls, then either self-signed certificates or certi {{- end }} {{- end }} {{- end }} + +{{/* +Validate the log configuration. +*/}} +{{- define "cockroachdb.conf.log.validation" -}} +{{- if and (not .Values.conf.log.enabled) .Values.conf.log.persistentVolume.enabled -}} + {{ fail "Persistent volume for logs can only be enabled if logging is enabled" }} +{{- end -}} +{{- if and .Values.conf.log.persistentVolume.enabled (dig "file-defaults" "dir" "" .Values.conf.log.config) -}} +{{- if not (hasPrefix (printf "/cockroach/%s" .Values.conf.log.persistentVolume.path) (dig "file-defaults" "dir" "" .Values.conf.log.config)) }} + {{ fail "Log configuration should use the persistent volume if enabled" }} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/cockroachdb/templates/statefulset.yaml b/cockroachdb/templates/statefulset.yaml index 318ae770..f118537a 100644 --- a/cockroachdb/templates/statefulset.yaml +++ b/cockroachdb/templates/statefulset.yaml @@ -1,3 +1,4 @@ +{{ template "cockroachdb.conf.log.validation" . }} kind: StatefulSet apiVersion: {{ template "cockroachdb.statefulset.apiVersion" . }} metadata: @@ -290,7 +291,11 @@ spec: mountPath: /cockroach/log-config readOnly: true {{- end }} - {{ with .Values.statefulset.volumeMounts }} + {{- if .Values.conf.log.persistentVolume.enabled }} + - name: logsdir + mountPath: /cockroach/{{ .Values.conf.log.persistentVolume.path }}/ + {{- end }} + {{- with .Values.statefulset.volumeMounts }} {{ toYaml . | nindent 12 }} {{- end }} {{- if .Values.statefulset.customStartupProbe }} @@ -393,6 +398,15 @@ spec: secret: secretName: {{ template "cockroachdb.fullname" . }}-log-config {{- end }} + {{- if .Values.conf.log.enabled }} + - name: logsdir + {{- if .Values.conf.log.persistentVolume.enabled }} + persistentVolumeClaim: + claimName: logsdir + {{- else }} + emptyDir: {} + {{- end }} + {{- end }} {{- if eq (include "cockroachdb.securityContext.versionValidation" .) "true" }} {{- if and .Values.securityContext.enabled }} securityContext: @@ -404,8 +418,9 @@ spec: runAsNonRoot: true {{- end }} {{- end }} -{{- if .Values.storage.persistentVolume.enabled }} +{{- if or .Values.storage.persistentVolume.enabled .Values.conf.log.persistentVolume.enabled }} volumeClaimTemplates: + {{- if .Values.storage.persistentVolume.enabled }} - metadata: name: datadir labels: @@ -432,4 +447,33 @@ spec: resources: requests: storage: {{ .Values.storage.persistentVolume.size | quote }} + {{- end }} + {{- if .Values.conf.log.persistentVolume.enabled }} + - metadata: + name: logsdir + labels: + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + {{- with .Values.conf.log.persistentVolume.labels }} + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.labels }} + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.conf.log.persistentVolume.annotations }} + annotations: {{- toYaml . | nindent 10 }} + {{- end }} + spec: + accessModes: ["ReadWriteOnce"] + {{- if .Values.conf.log.persistentVolume.storageClass }} + {{- if (eq "-" .Values.conf.log.persistentVolume.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: {{ .Values.conf.log.persistentVolume.storageClass | quote}} + {{- end }} + {{- end }} + resources: + requests: + storage: {{ .Values.conf.log.persistentVolume.size | quote }} + {{- end }} {{- end }} diff --git a/cockroachdb/values.yaml b/cockroachdb/values.yaml index 137f8f22..dc0167d9 100644 --- a/cockroachdb/values.yaml +++ b/cockroachdb/values.yaml @@ -70,14 +70,35 @@ conf: log: enabled: false # https://www.cockroachlabs.com/docs/v21.1/configure-logs - config: {} + config: # file-defaults: - # dir: /custom/dir/path/ + # dir: /cockroach/cockroach-logs # fluent-defaults: # format: json-fluent # sinks: # stderr: # channels: [DEV] + persistentVolume: + # If enabled, then a PersistentVolumeClaim will be created and + # used to store CockroachDB's logs. + enabled: false + # CockroachDB's logs volume mount path. This gets prepended with + # `/cockroach/` in the stateful set. The `conf.log.config` should have + # `file-defaults.dir` to specify the log path and should reference the + # mounted volume. + path: cockroach-logs + size: 10Gi + # If defined, then `storageClassName: `. + # If set to "-", then `storageClassName: ""`, which disables dynamic + # provisioning. + # If undefined or empty (default), then no `storageClassName` spec is + # set, so the default provisioner will be chosen (gp2 on AWS, standard + # on GKE, AWS & OpenStack). + storageClass: "" + # Additional labels to apply to the created PersistentVolumeClaims. + labels: {} + # Additional annotations to apply to the created PersistentVolumeClaims. + annotations: {} # Logs at or above this threshold to STDERR. Ignored when "log" is enabled logtostderr: INFO diff --git a/tests/template/cockroachdb_helm_template_test.go b/tests/template/cockroachdb_helm_template_test.go index 2b4dd836..d78a0e0d 100644 --- a/tests/template/cockroachdb_helm_template_test.go +++ b/tests/template/cockroachdb_helm_template_test.go @@ -496,42 +496,42 @@ func TestSelfSignerHelmValidation(t *testing.T) { func TestHelmLogConfigFileStatefulSet(t *testing.T) { t.Parallel() + type expect struct { + statefulsetArgument string + logConfig string + secretExists bool + renderErr string + persistentVolumeCreated bool + } + testCases := []struct { name string values map[string]string - expect struct { - statefulsetArgument string - logConfig string - secretExists bool - } + expect expect }{ { "New logging configuration enabled", map[string]string{"conf.log.enabled": "true"}, - struct { - statefulsetArgument string - logConfig string - secretExists bool - }{ + expect{ "--log-config-file=/cockroach/log-config/log-config.yaml", - "{}", + "", true, + "", + false, }, }, { "New logging configuration overridden", map[string]string{ - "conf.log.enabled": "true", - "conf.log.config": "file-defaults:\ndir: /custom/dir/path/", + "conf.log.enabled": "true", + "conf.log.config.file-defaults.dir": "/cockroach/cockroach-logs", }, - struct { - statefulsetArgument string - logConfig string - secretExists bool - }{ + expect{ "--log-config-file=/cockroach/log-config/log-config.yaml", - "file-defaults:\n dir: /custom/dir/path/", + "file-defaults:\n dir: /cockroach/cockroach-logs", true, + "", + false, }, }, { @@ -540,14 +540,57 @@ func TestHelmLogConfigFileStatefulSet(t *testing.T) { "conf.log.enabled": "false", "conf.logtostderr": "INFO", }, - struct { - statefulsetArgument string - logConfig string - secretExists bool - }{ + expect{ "--logtostderr=INFO", "", false, + "", + false, + }, + }, + { + "New logging configuration disabled, but persistent volume enabled", + map[string]string{ + "conf.log.enabled": "false", + "conf.logtostderr": "INFO", + "conf.log.persistentVolume.enabled": "true", + }, + expect{ + "--logtostderr=INFO", + "", + false, + "Persistent volume for logs can only be enabled if logging is enabled", + false, + }, + }, + { + "New logging configuration not using persistent volume when enabled", + map[string]string{ + "conf.log.enabled": "true", + "conf.log.config.file-defaults.dir": "/wrong/path", + "conf.log.persistentVolume.enabled": "true", + }, + expect{ + "", + "", + false, + "Log configuration should use the persistent volume if enabled", + false, + }, + }, + { + "New logging configuration using the persistent volume", + map[string]string{ + "conf.log.enabled": "true", + "conf.log.config.file-defaults.dir": "/cockroach/cockroach-logs", + "conf.log.persistentVolume.enabled": "true", + }, + expect{ + "--log-config-file=/cockroach/log-config/log-config.yaml", + "file-defaults:\n dir: /cockroach/cockroach-logs", + true, + "", + true, }, }, } @@ -569,15 +612,30 @@ func TestHelmLogConfigFileStatefulSet(t *testing.T) { KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), SetValues: testCase.values, } - output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/statefulset.yaml"}) + + output, err := helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/statefulset.yaml"}) + if err != nil { + require.ErrorContains(subT, err, testCase.expect.renderErr) + return + } else { + require.Empty(subT, testCase.expect.renderErr) + } helm.UnmarshalK8SYaml(t, output, &statefulset) require.Equal(subT, namespaceName, statefulset.Namespace) require.Contains(t, statefulset.Spec.Template.Spec.Containers[0].Args[2], testCase.expect.statefulsetArgument) - output, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/secret.logconfig.yaml"}) + if testCase.expect.persistentVolumeCreated { + // Expect 2 persistent volumes: data, logs + require.Equal(subT, 2, len(statefulset.Spec.VolumeClaimTemplates)) + require.Equal(subT, "logsdir", statefulset.Spec.VolumeClaimTemplates[1].Name) + } else { + // Expect 1 persistent volume: data + require.Equal(subT, 1, len(statefulset.Spec.VolumeClaimTemplates)) + } + output, err = helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{"templates/secret.logconfig.yaml"}) require.Equal(subT, testCase.expect.secretExists, err == nil) if testCase.expect.secretExists {