diff --git a/charts/consul/templates/_helpers.tpl b/charts/consul/templates/_helpers.tpl index b78987f908..dc30b1017d 100644 --- a/charts/consul/templates/_helpers.tpl +++ b/charts/consul/templates/_helpers.tpl @@ -145,7 +145,7 @@ substitution for HOST_IP/POD_IP/HOSTNAME. Useful for dogstats telemetry. The out is passed to consul as a -config-file param on command line. */}} {{- define "consul.extraconfig" -}} - cp /consul/tmp/extra-config/extra-from-values.json /consul/extra-config/extra-from-values.json + cp /consul/config/extra-from-values.json /consul/extra-config/extra-from-values.json [ -n "${HOST_IP}" ] && sed -Ei "s|HOST_IP|${HOST_IP?}|g" /consul/extra-config/extra-from-values.json [ -n "${POD_IP}" ] && sed -Ei "s|POD_IP|${POD_IP?}|g" /consul/extra-config/extra-from-values.json [ -n "${HOSTNAME}" ] && sed -Ei "s|HOSTNAME|${HOSTNAME?}|g" /consul/extra-config/extra-from-values.json @@ -519,3 +519,92 @@ Usage: {{ template "consul.validateResourceAPIs" . }} {{fail "When the value global.experiments.resourceAPIs is set, apiGateway.enabled is currently unsupported."}} {{- end }} {{- end }} + +{{/* +Validation for Consul Datadog Integration deployment: + +Fail if global.metrics.datadogIntegration.enabled=true and global.metrics.enabled or global.metrics.enableAgentMetrics are unset + - global.metrics.datadogIntegration.enabled=true + - global.metrics.enableAgentMetrics=false || global.metrics.enabled=false + +Fail if Consul OpenMetrics (Prometheus) and DogStatsD metrics are both enabled. + - global.metrics.datadogIntegration.dogstatsd.enabled (scrapes `/v1/agent/metrics?format=prometheus` via the `use_prometheus_endpoint` option) + - global.metrics.datadogIntegration.openMetricsPrometheus.enabled (scrapes `/v1/agent/metrics?format=prometheus`) + - see https://docs.datadoghq.com/integrations/consul/?tab=host#host for recommendation to not have both + +Usage: {{ template "consul.validateDatadogConfiguration" . }} + +*/}} + +{{- define "consul.validateDatadogConfiguration" -}} +{{- if and .Values.global.metrics.datadogIntegration.enabled (or (not .Values.global.metrics.enableAgentMetrics) (not .Values.global.metrics.enabled) )}} +{{fail "When enabling datadog metrics collection, the /v1/agent/metrics is required to be accessible, therefore global.metrics.enableAgentMetrics and global.metrics.enabled must be also be enabled."}} +{{- end }} +{{- if and .Values.global.metrics.datadogIntegration.dogstatsd.enabled .Values.global.metrics.datadogIntegration.openMetricsPrometheus.enabled }} +{{fail "You must have one of DogStatsD (global.metrics.datadogIntegration.dogstatsd.enabled) or OpenMetrics (global.metrics.datadogIntegration.openMetricsPrometheus.enabled) enabled, not both as this is an unsupported configuration." }} +{{- end }} +{{- end -}} + +{{/* +Sets the dogstatsd_addr field of the agent configuration dependent on the +socket transport type being used: + - "UDS" (Unix Domain Socket): prefixes "unix://" to URL and appends path to socket (i.e., unix:///var/run/datadog/dsd.socket) + - "UDP" (User Datagram Protocol): adds no prefix and appends dogstatsd port number to hostname/IP (i.e., 172.20.180.10:8125) +- global.metrics.enableDatadogIntegration.dogstatsd configuration + +Usage: {{ template "consul.dogstatsdAaddressInfo" . }} +*/}} + +{{- define "consul.dogstatsdAaddressInfo" -}} +{{- if (and .Values.global.metrics.datadogIntegration.enabled .Values.global.metrics.datadogIntegration.dogstatsd.enabled) }} + "dogstatsd_addr": "{{- if eq .Values.global.metrics.datadogIntegration.dogstatsd.socketTransportType "UDS" }}unix://{{ .Values.global.metrics.datadogIntegration.dogstatsd.dogstatsdAddr }}{{- else }}{{ .Values.global.metrics.datadogIntegration.dogstatsd.dogstatsdAddr | trimAll "\"" }}:{{ .Values.global.metrics.datadogIntegration.dogstatsd.dogstatsdPort | toString }}{{- end }}",{{- end }} +{{- end -}} + +{{/* +Configures the metrics prefixing that's required to either allow or dissallow certaing RPC or gRPC server calls: + +Usage: {{ template "consul.metricsPrefixFiltering" . }} +*/}} +{{- define "consul.metricsPrefixFiltering" -}} +{{- $allowList := .Values.global.metrics.metricsPrefixFiltering.allowList }} +{{- $blockList := .Values.global.metrics.metricsPrefixFiltering.blockList }} +{{- if and (not (empty $allowList)) (not (empty $blockList)) }} + "prefix_filter": [{{- range $index, $value := concat $allowList $blockList -}} + "{{- if (has $value $allowList) }}{{ printf "+%s" ($value | trimAll "\"") }}{{- else }}{{ printf "-%s" ($value | trimAll "\"") }}{{- end }}"{{- if lt $index (sub (len (concat $allowList $blockList)) 1) -}},{{- end -}} + {{- end -}}], +{{- else if not (empty $allowList) }} + "prefix_filter": [{{- range $index, $value := $allowList -}} + "{{ printf "+%s" ($value | trimAll "\"") }}"{{- if lt $index (sub (len $allowList) 1) -}},{{- end -}} + {{- end -}}], +{{- else if not (empty $blockList) }} + "prefix_filter": [{{- range $index, $value := $blockList -}} + "{{ printf "-%s" ($value | trimAll "\"") }}"{{- if lt $index (sub (len $blockList) 1) -}},{{- end -}} + {{- end -}}], +{{- end }} +{{- end -}} + +{{/* +Retrieves the global consul/consul-enterprise version string for use with labels or tags. +Requirements for valid labels: + - a valid label must be an empty string or consist of + => alphanumeric characters + => '-', '_' or '.' + => must start and end with an alphanumeric character + (e.g. 'MyValue', or 'my_value', or '12345', regex used for validation is + '(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?') + +Usage: {{ template "consul.versionInfo" }} +*/}} +{{- define "consul.versionInfo" -}} +{{- $imageVersion := regexSplit ":" .Values.global.image -1 }} +{{- $versionInfo := printf "%s" (index $imageVersion 1 ) | trimSuffix "\"" }} +{{- $sanitizedVersion := "" }} +{{- $pattern := "^([A-Za-z0-9][-A-Za-z0-9_.]*[A-Za-z0-9])?$" }} +{{- if not (regexMatch $pattern $versionInfo) -}} + {{- $sanitizedVersion = regexReplaceAll "[^A-Za-z0-9-_.]|sha256" $versionInfo "" }} + {{- $sanitizedVersion = printf "%s" (trimSuffix "-" (trimPrefix "-" $sanitizedVersion)) -}} +{{- else }} + {{- $sanitizedVersion = $versionInfo }} +{{- end -}} +{{- printf "%s" $sanitizedVersion | quote }} +{{- end -}} \ No newline at end of file diff --git a/charts/consul/templates/server-acl-init-job.yaml b/charts/consul/templates/server-acl-init-job.yaml index e29f9cbc8d..511b5bcea2 100644 --- a/charts/consul/templates/server-acl-init-job.yaml +++ b/charts/consul/templates/server-acl-init-job.yaml @@ -2,6 +2,9 @@ {{- if (and $serverEnabled .Values.externalServers.enabled) }}{{ fail "only one of server.enabled or externalServers.enabled can be set" }}{{ end -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if and .Values.global.acls.createReplicationToken (not .Values.global.acls.manageSystemACLs) }}{{ fail "if global.acls.createReplicationToken is true, global.acls.manageSystemACLs must be true" }}{{ end -}} +{{- if .Values.global.metrics.enableDatadogIntegration }} +{{- if and .Values.global.metrics.enableDatadogIntegration.createAgentToken (not .Values.global.acls.manageSystemACLs) }}{{ fail "if global.metrics.enableDatadogIntegration.createAgentToken is true, global.acls.manageSystemACLs must be true" }}{{ end -}} +{{- end }} {{- if .Values.global.bootstrapACLs }}{{ fail "global.bootstrapACLs was removed, use global.acls.manageSystemACLs instead" }}{{ end -}} {{- if .Values.global.acls.manageSystemACLs }} {{- if or (and .Values.global.acls.bootstrapToken.secretName (not .Values.global.acls.bootstrapToken.secretKey)) (and .Values.global.acls.bootstrapToken.secretKey (not .Values.global.acls.bootstrapToken.secretName))}}{{ fail "both global.acls.bootstrapToken.secretKey and global.acls.bootstrapToken.secretName must be set if one of them is provided" }}{{ end -}} @@ -273,6 +276,10 @@ spec: -create-enterprise-license-token=true \ {{- end }} + {{- if (and .Values.global.metrics.datadogIntegration.enabled .Values.global.acls.manageSystemACLs) }} + -create-dd-agent-token=true \ + {{- end }} + {{- if .Values.server.snapshotAgent.enabled }} -snapshot-agent=true \ {{- end }} diff --git a/charts/consul/templates/server-config-configmap.yaml b/charts/consul/templates/server-config-configmap.yaml index 4ce79794b2..f09ba843ed 100644 --- a/charts/consul/templates/server-config-configmap.yaml +++ b/charts/consul/templates/server-config-configmap.yaml @@ -192,8 +192,15 @@ data: telemetry-config.json: |- { "telemetry": { - "prometheus_retention_time": "{{ .Values.global.metrics.agentMetricsRetentionTime }}" - } + "prometheus_retention_time": "{{ .Values.global.metrics.agentMetricsRetentionTime }}", + "disable_hostname": {{ .Values.global.metrics.disableAgentHostName }},{{ template "consul.metricsPrefixFiltering" . }} + "enable_host_metrics": {{ .Values.global.metrics.enableHostMetrics }}{{- if .Values.global.metrics.datadogIntegration.dogstatsd.enabled }},{{ template "consul.dogstatsdAaddressInfo" . }} + {{- if .Values.global.metrics.datadogIntegration.dogstatsd.enabled }} + "dogstatsd_tags": {{ .Values.global.metrics.datadogIntegration.dogstatsd.dogstatsdTags | toJson }} + {{- end }} + {{- end }} + }, + "enable_debug": {{ .Values.global.metrics.enableConsulAgentDebug }} } {{- end }} {{- if and .Values.server.auditLogs.enabled .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-statefulset.yaml b/charts/consul/templates/server-statefulset.yaml index 3a89fff8ba..b923fc935b 100644 --- a/charts/consul/templates/server-statefulset.yaml +++ b/charts/consul/templates/server-statefulset.yaml @@ -19,6 +19,7 @@ {{- end -}} {{ template "consul.validateRequiredCloudSecretsExist" . }} {{ template "consul.validateCloudSecretKeys" . }} +{{ template "consul.validateDatadogConfiguration" . }} # StatefulSet to run the actual Consul server cluster. apiVersion: apps/v1 kind: StatefulSet @@ -62,6 +63,11 @@ spec: release: {{ .Release.Name }} component: server hasDNS: "true" + {{- if .Values.global.metrics.datadogIntegration.enabled }} + "tags.datadoghq.com/version": {{ template "consul.versionInfo" . }} + "tags.datadoghq.com/env": {{ template "consul.name" . }} + "tags.datadoghq.com/service": "consul-server" + {{- end }} {{- if .Values.server.extraLabels }} {{- toYaml .Values.server.extraLabels | nindent 8 }} {{- end }} @@ -133,6 +139,64 @@ spec: "prometheus.io/port": "8500" "prometheus.io/scheme": "http" {{- end }} + {{- if .Values.global.metrics.datadogIntegration.enabled }} + "ad.datadoghq.com/tolerate-unready": "true" + "ad.datadoghq.com/consul.logs": {{ .Values.global.metrics.datadogIntegration.dogstatsd.dogstatsdTags | squote }} + {{- if .Values.global.metrics.datadogIntegration.openMetricsPrometheus.enabled }} + "ad.datadoghq.com/consul.checks": | + { + "openmetrics": { + "init_config": {}, + "instances": [ + { + {{- if .Values.global.tls.enabled }} + "openmetrics_endpoint": "https://consul-server.{{ .Release.Namespace }}.svc:8501/v1/agent/metrics?format=prometheus", + "tls_cert": "/etc/datadog-agent/conf.d/consul.d/certs/tls.crt", + "tls_private_key": "/etc/datadog-agent/conf.d/consul.d/certs/tls.key", + "tls_ca_cert": "/etc/datadog-agent/conf.d/consul.d/ca/tls.crt", + {{- else }} + "openmetrics_endpoint": "http://%%host%%:8500/v1/agent/metrics?format=prometheus", + {{- end }} + {{- if ( .Values.global.acls.manageSystemACLs) }} + "headers": { + "X-Consul-Token": "ENC[k8s_secret@{{ .Release.Namespace }}/{{ .Release.Namespace }}-datadog-agent-metrics-acl-token/token]" + }, + {{- end }} + "namespace": "{{ .Release.Namespace }}", + "metrics": [ ".*" ] + } + ] + } + } + {{- else }} + "ad.datadoghq.com/consul.checks": | + { + "consul": { + "init_config": {}, + "instances": [ + { + {{- if .Values.global.tls.enabled }} + "url": "https://consul-server.{{ .Release.Namespace }}.svc:8501", + "tls_cert": "/etc/datadog-agent/conf.d/consul.d/certs/tls.crt", + "tls_private_key": "/etc/datadog-agent/conf.d/consul.d/certs/tls.key", + "tls_ca_cert": "/etc/datadog-agent/conf.d/consul.d/ca/tls.crt", + {{- else }} + "url": "http://consul-server.consul.svc:8500", + {{- end }} + "use_prometheus_endpoint": true, + {{- if ( .Values.global.acls.manageSystemACLs) }} + "acl_token": "ENC[k8s_secret@{{ .Release.Namespace }}/{{ .Release.Namespace }}-datadog-agent-metrics-acl-token/token]", + {{- end }} + "new_leader_checks": true, + "network_latency_checks": true, + "catalog_checks": true, + "auth_type": "basic" + } + ] + } + } + {{- end }} + {{- end }} {{- end }} spec: {{- if .Values.server.affinity }} @@ -219,6 +283,12 @@ spec: emptyDir: medium: "Memory" {{- end }} + {{- if and .Values.global.metrics.datadogIntegration.enabled .Values.global.metrics.datadogIntegration.dogstatsd.enabled (eq .Values.global.metrics.datadogIntegration.dogstatsd.socketTransportType "UDS" ) }} + - name: dsdsocket + hostPath: + path: /var/run/datadog + type: DirectoryOrCreate + {{- end }} {{- range .Values.server.extraVolumes }} - name: userconfig-{{ .name }} {{ .type }}: @@ -257,7 +327,7 @@ spec: {{- include "consul.restrictedSecurityContext" . | nindent 8 }} containers: - name: consul - image: "{{ default .Values.global.image .Values.server.image }}" + image: "{{ default .Values.global.image .Values.server.image | trimPrefix "\"" | trimSuffix "\"" }}" imagePullPolicy: {{ .Values.global.imagePullPolicy }} env: - name: ADVERTISE_IP @@ -464,6 +534,11 @@ spec: mountPath: /consul/license readOnly: true {{- end }} + {{- if and .Values.global.metrics.datadogIntegration.enabled .Values.global.metrics.datadogIntegration.dogstatsd.enabled (eq .Values.global.metrics.datadogIntegration.dogstatsd.socketTransportType "UDS" ) }} + - name: dsdsocket + mountPath: /var/run/datadog + readOnly: true + {{- end }} {{- range .Values.server.extraVolumes }} - name: userconfig-{{ .name }} readOnly: true diff --git a/charts/consul/test/unit/server-acl-init-job.bats b/charts/consul/test/unit/server-acl-init-job.bats index 2205192ad2..d745839810 100644 --- a/charts/consul/test/unit/server-acl-init-job.bats +++ b/charts/consul/test/unit/server-acl-init-job.bats @@ -2444,3 +2444,35 @@ load _helpers yq 'any(contains("-enable-resource-apis=true"))' | tee /dev/stderr) [ "${actual}" = "true" ] } + +#-------------------------------------------------------------------- +# global.metrics.datadogIntegration + +@test "serverACLInit/Job: -create-dd-agent-token not set when datadogIntegration=false and manageSystemACLs=true" { + cd `chart_dir` + local command=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$( echo "$command" | + yq 'any(contains("-create-dd-agent-token"))' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "serverACLInit/Job: -create-dd-agent-token set when global.metrics.datadogIntegration=true and global.acls.manageSystemACLs=true" { + cd `chart_dir` + local command=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.metrics.datadogIntegration.enabled=true' \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$( echo "$command" | + yq 'any(contains("-create-dd-agent-token"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/server-config-configmap.bats b/charts/consul/test/unit/server-config-configmap.bats index 53f20a91f6..e0f20805b4 100755 --- a/charts/consul/test/unit/server-config-configmap.bats +++ b/charts/consul/test/unit/server-config-configmap.bats @@ -1309,6 +1309,223 @@ load _helpers [ "${configmap}" = "DEBUG" ] } +#-------------------------------------------------------------------- +# Datadog Consul on Kubernetes Integration + +@test "server/ConfigMap: when global.metrics.datadogIntegration.enabled=true, sets default telemetry.dogstatsd_addr config" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.metrics.datadogIntegration.enabled=true' \ + --set 'global.metrics.datadogIntegration.dogstatsd.enabled=true' \ + . | tee /dev/stderr | + yq -r '.data["telemetry-config.json"]' | jq -r .telemetry.dogstatsd_addr | tee /dev/stderr) + + [ "${actual}" = "unix:///var/run/datadog/dsd.socket" ] +} + +@test "server/ConfigMap: when global.metrics.datadogIntegration.enabled=true, sets non-default namespace telemetry.dogstatsd_addr config" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.metrics.datadogIntegration.enabled=true' \ + --set 'global.metrics.datadogIntegration.dogstatsd.enabled=true' \ + --set 'global.metrics.datadogIntegration.dogstatsd.socketTransportType="UDP"' \ + --set 'global.metrics.datadogIntegration.dogstatsd.dogstatsdAddr="127.0.0.1"' \ + . | tee /dev/stderr | + yq -r '.data["telemetry-config.json"]' | jq -r .telemetry.dogstatsd_addr | tee /dev/stderr) + + [ "${actual}" = "127.0.0.1:8125" ] +} + +@test "server/ConfigMap: when global.metrics.datadogIntegration.enabled=true, sets non-default namespace telemetry.dogstatsd_addr with non-default port config" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.metrics.datadogIntegration.enabled=true' \ + --set 'global.metrics.datadogIntegration.dogstatsd.enabled=true' \ + --set 'global.metrics.datadogIntegration.dogstatsd.socketTransportType="UDP"' \ + --set 'global.metrics.datadogIntegration.dogstatsd.dogstatsdAddr="127.0.0.1"' \ + --set 'global.metrics.datadogIntegration.dogstatsd.dogstatsdPort=8000' \ + . | tee /dev/stderr | + yq -r '.data["telemetry-config.json"]' | jq -r .telemetry.dogstatsd_addr | tee /dev/stderr) + + [ "${actual}" = "127.0.0.1:8000" ] +} + +@test "server/ConfigMap: when global.metrics.datadogIntegration.enabled=true, sets default telemetry.dogstatsd_tags config" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.metrics.datadogIntegration.enabled=true' \ + . | tee /dev/stderr | + yq -r '.data["telemetry-config.json"]' | jq -r .telemetry.dogstatsd_tags | jq -r '[ .[] ]| join (" ")' | tee /dev/stderr) + + [ "${actual}" = "source:consul consul_service:consul-server" ] +} + +@test "server/ConfigMap: when global.metrics.enableDatadogIntegration.enabled=true, sets non-default telemetry.dogstatsd_tags config" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.metrics.datadogIntegration.enabled=true' \ + --set 'global.metrics.datadogIntegration.dogstatsd.enabled=true' \ + --set 'global.metrics.datadogIntegration.dogstatsd.dogstatsdTags'='[\"source:consul-dataplane\"\,\"service:consul-server-connection-manager\"]' \ + . | tee /dev/stderr | + yq -r '.data["telemetry-config.json"]' | jq -r .telemetry.dogstatsd_tags | jq -r '[ .[] ]| join (" ")' | tee /dev/stderr) + + [ "${actual}" = "source:consul-dataplane service:consul-server-connection-manager" ] +} + +#-------------------------------------------------------------------- +# Consul Agent Metrics Prefix Filtering + +@test "server/ConfigMap: when global.metrics.metricsPrefixFiltering default, empty telemetry.prefix_filter string list" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + . | tee /dev/stderr | + yq -r '.data["telemetry-config.json"]' | jq -r .telemetry.prefix_filter | jq -r '[ .[] ]| join (" ")' | tee /dev/stderr) + + [ "${actual}" = "" ] +} + +@test "server/ConfigMap: when global.metrics.metricsPrefixFiltering.allowList, sets correctly prepended telemetry.prefix_filter string list" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.metrics.metricsPrefixFiltering.allowList'={'"consul.rpc.server.call"'\,'"consul.grpc.server.call"'} \ + . | tee /dev/stderr | + yq -r '.data["telemetry-config.json"]' | jq -r .telemetry.prefix_filter | jq -r '[ .[] ]| join (" ")' | tee /dev/stderr) + + [ "${actual}" = "+consul.rpc.server.call +consul.grpc.server.call" ] +} + +@test "server/ConfigMap: when global.metrics.metricsPrefixFiltering.blockList, sets correctly prepended telemetry.prefix_filter string list" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.metrics.metricsPrefixFiltering.blockList'={'"consul.rpc.server.call"'\,'"consul.grpc.server.call"'} \ + . | tee /dev/stderr | + yq -r '.data["telemetry-config.json"]' | jq -r .telemetry.prefix_filter | jq -r '[ .[] ]| join (" ")' | tee /dev/stderr) + + [ "${actual}" = "-consul.rpc.server.call -consul.grpc.server.call" ] +} + +@test "server/ConfigMap: when global.metrics.metricsPrefixFiltering.blockList and allowList, sets correctly prepended telemetry.prefix_filter string list" { + cd `chart_dir` + local actual=$(helm template --debug \ + -s templates/server-config-configmap.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.metrics.metricsPrefixFiltering.allowList'={'"consul.rpc.server.call"'\,'"consul.http.GET"'} \ + --set 'global.metrics.metricsPrefixFiltering.blockList'={'"consul.http"'\,'"consul.raft.apply"'} \ + . | tee /dev/stderr | + yq -r '.data["telemetry-config.json"]' | jq -r .telemetry.prefix_filter | jq -r '[ .[] ]| join (" ")' | tee /dev/stderr) + + [ "${actual}" = "+consul.rpc.server.call +consul.http.GET -consul.http -consul.raft.apply" ] +} + +#-------------------------------------------------------------------- +# Consul Agent Debug (PPROF) + +@test "server/ConfigMap: when global.metrics.enableConsulAgentDebug default, sets default enable_debug = false in agent config" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + . | tee /dev/stderr | + yq -r '.data["telemetry-config.json"]' | jq -r .enable_debug | tee /dev/stderr) + + [ "${actual}" = "false" ] +} + +@test "server/ConfigMap: when global.metrics.enableConsulAgentDebug=true, sets enable_debug = true in agent config" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.metrics.enableConsulAgentDebug=true' \ + . | tee /dev/stderr | + yq -r '.data["telemetry-config.json"]' | jq -r .enable_debug | tee /dev/stderr) + + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- +# Consul Agent Telemetry Host Metrics + +@test "server/ConfigMap: when global.metrics.enableHostMetrics is default, telemetry.enable_host_metrics = false in agent config" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + . | tee /dev/stderr | + yq -r '.data["telemetry-config.json"]' | jq -r .telemetry.enable_host_metrics | tee /dev/stderr) + + [ "${actual}" = "false" ] +} + +@test "server/ConfigMap: when global.metrics.enableHostMetrics=true, sets telemetry.enable_host_metrics = true in agent config" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.metrics.enableHostMetrics=true' \ + . | tee /dev/stderr | + yq -r '.data["telemetry-config.json"]' | jq -r .telemetry.enable_host_metrics | tee /dev/stderr) + + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- +# Consul Agent Telemetry Hostname Disable + +@test "server/ConfigMap: when global.metrics.disableAgentHostName is default, telemetry.disableAgentHostName = false in agent config" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + . | tee /dev/stderr | + yq -r '.data["telemetry-config.json"]' | jq -r .telemetry.enable_host_metrics | tee /dev/stderr) + + [ "${actual}" = "false" ] +} + +@test "server/ConfigMap: when global.metrics.disableAgentHostName=true, sets telemetry.disableAgentHostName = true in agent config" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.metrics.enableHostMetrics=true' \ + . | tee /dev/stderr | + yq -r '.data["telemetry-config.json"]' | jq -r .telemetry.enable_host_metrics | tee /dev/stderr) + + [ "${actual}" = "true" ] +} + #-------------------------------------------------------------------- # server.autopilot.min_quorum diff --git a/charts/consul/test/unit/server-statefulset.bats b/charts/consul/test/unit/server-statefulset.bats index 388d4dc986..9ead41ce53 100755 --- a/charts/consul/test/unit/server-statefulset.bats +++ b/charts/consul/test/unit/server-statefulset.bats @@ -783,6 +783,212 @@ load _helpers [ "${actual}" = "https" ] } +@test "server/StatefulSet: when global.metrics.datadogIntegration.enabled=true, adds ad.datadoghq.com annotations" { + cd `chart_dir` + local annotations=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.metrics.datadogIntegration.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations' | tee /dev/stderr) + + local actual=$(echo "$annotations" | \ + yq -r '."ad.datadoghq.com/tolerate-unready"' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo "$annotations" | \ + yq -r '."ad.datadoghq.com/consul.logs"' | tee /dev/stderr) + [ "${actual}" = "[source:consul consul_service:consul-server]" ] + + local consul_checks=$(echo "$annotations" | \ + yq -r '."ad.datadoghq.com/consul.checks"' | tee /dev/stderr) + + local actual="$( echo "$consul_checks" | \ + jq -r .consul.init_config | tee /dev/stderr)" + [ "${actual}" = "{}" ] + + local actual="$( echo "$consul_checks" | \ + jq -r .consul.instances | jq -r .[0].url | tee /dev/stderr)" + [ "${actual}" = "http://consul-server.consul.svc:8500" ] + + local actual="$( echo "$consul_checks" | \ + jq -r .consul.instances | jq -r .[0].new_leader_checks | tee /dev/stderr)" + [ "${actual}" = "true" ] + + local actual="$( echo "$consul_checks" | \ + jq -r .consul.instances | jq -r .[0].catalog_checks | tee /dev/stderr)" + [ "${actual}" = "true" ] + + local actual="$( echo "$consul_checks" | \ + jq -r .consul.instances | jq -r .[0].auth_type | tee /dev/stderr)" + [ "${actual}" = "basic" ] +} + +@test "server/StatefulSet: when global.metrics.datadogIntegration.enabled=true and global.tls.enabled, adds tls altered ad.datadoghq.com annotations" { + cd `chart_dir` + local annotations=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.tls.enabled=true' \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.metrics.datadogIntegration.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations' | tee /dev/stderr) + + local actual=$(echo "$annotations" | \ + yq -r '."ad.datadoghq.com/tolerate-unready"' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo "$annotations" | \ + yq -r '."ad.datadoghq.com/consul.logs"' | tee /dev/stderr) + [ "${actual}" = "[source:consul consul_service:consul-server]" ] + + local consul_checks=$(echo "$annotations" | \ + yq -r '."ad.datadoghq.com/consul.checks"' | tee /dev/stderr) + + local actual="$( echo "$consul_checks" | \ + jq -r .consul.init_config | tee /dev/stderr)" + [ "${actual}" = "{}" ] + + local actual="$( echo "$consul_checks" | \ + jq -r .consul.instances | jq -r .[0].url | tee /dev/stderr)" + [ "${actual}" = "https://consul-server.default.svc:8501" ] + + local actual="$( echo "$consul_checks" | \ + jq -r .consul.instances | jq -r .[0].tls_cert | tee /dev/stderr)" + [ "${actual}" = "/etc/datadog-agent/conf.d/consul.d/certs/tls.crt" ] + + local actual="$( echo "$consul_checks" | \ + jq -r .consul.instances | jq -r .[0].tls_private_key | tee /dev/stderr)" + [ "${actual}" = "/etc/datadog-agent/conf.d/consul.d/certs/tls.key" ] + + local actual="$( echo "$consul_checks" | \ + jq -r .consul.instances | jq -r .[0].tls_ca_cert | tee /dev/stderr)" + [ "${actual}" = "/etc/datadog-agent/conf.d/consul.d/ca/tls.crt" ] + + local actual="$( echo "$consul_checks" | \ + jq -r .consul.instances | jq -r .[0].new_leader_checks | tee /dev/stderr)" + [ "${actual}" = "true" ] + + local actual="$( echo "$consul_checks" | \ + jq -r .consul.instances | jq -r .[0].catalog_checks | tee /dev/stderr)" + [ "${actual}" = "true" ] + + local actual="$( echo "$consul_checks" | \ + jq -r .consul.instances | jq -r .[0].auth_type | tee /dev/stderr)" + [ "${actual}" = "basic" ] +} + +@test "server/StatefulSet: when global.metrics.datadogIntegration.enabled=true and global.acls.manageSystemACLs=true, adds ad.datadoghq.com annotations for datadog-agent-metrics-acl-token secret rendering" { + cd `chart_dir` + local annotations=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.metrics.datadogIntegration.enabled=true' \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations' | tee /dev/stderr) + + local consul_checks=$(echo "$annotations" | \ + yq -r '."ad.datadoghq.com/consul.checks"' | tee /dev/stderr) + + local actual="$( echo "$consul_checks" | \ + jq -r .consul.instances | jq -r .[0].acl_token | tee /dev/stderr)" + [ "${actual}" = "ENC[k8s_secret@default/default-datadog-agent-metrics-acl-token/token]" ] +} + +@test "server/StatefulSet: when global.metrics.datadogIntegration.openMetricsPrometheus.enabled, applicable openmetrics annotation is set" { + cd `chart_dir` + local annotations=$(helm template --debug \ + -s templates/server-statefulset.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.metrics.datadogIntegration.enabled=true' \ + --set 'global.metrics.datadogIntegration.openMetricsPrometheus.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations' | tee /dev/stderr) + + local consul_checks=$(echo "$annotations" | \ + yq -r '."ad.datadoghq.com/consul.checks"' | tee /dev/stderr) + + local actual="$( echo "$consul_checks" | \ + jq -r .openmetrics.init_config | tee /dev/stderr)" + [ "${actual}" = "{}" ] + + local actual="$( echo "$consul_checks" | \ + jq -r .openmetrics.instances | jq -r .[0].openmetrics_endpoint | tee /dev/stderr)" + [ "${actual}" = "http://%%host%%:8500/v1/agent/metrics" ] + + local actual="$( echo "$consul_checks" | \ + jq -r .openmetrics.instances | jq -r .[0].namespace | tee /dev/stderr)" + [ "${actual}" = "default" ] + + local actual="$( echo "$consul_checks" | \ + jq -r .openmetrics.instances | jq -r .[0].metrics[0] | tee /dev/stderr)" + [ "${actual}" = ".*" ] + +} + +@test "server/StatefulSet: when datadogIntegration.openMetricsPrometheus.enabled, applicable openmetrics annotation is set with tls url" { + cd `chart_dir` + local annotations=$(helm template --debug \ + -s templates/server-statefulset.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.tls.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.metrics.datadogIntegration.enabled=true' \ + --set 'global.metrics.datadogIntegration.openMetricsPrometheus.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations' | tee /dev/stderr) + + local consul_checks=$(echo "$annotations" | \ + yq -r '."ad.datadoghq.com/consul.checks"' | tee /dev/stderr) + + local actual="$( echo "$consul_checks" | \ + jq -r .openmetrics.init_config | tee /dev/stderr)" + [ "${actual}" = "{}" ] + + local actual="$( echo "$consul_checks" | \ + jq -r .openmetrics.instances | jq -r .[0].openmetrics_endpoint | tee /dev/stderr)" + [ "${actual}" = "https://%%host%%:8501/v1/agent/metrics" ] + + local actual="$( echo "$consul_checks" | \ + jq -r .openmetrics.instances | jq -r .[0].namespace | tee /dev/stderr)" + [ "${actual}" = "default" ] + + local actual="$( echo "$consul_checks" | \ + jq -r .openmetrics.instances | jq -r .[0].metrics[0] | tee /dev/stderr)" + [ "${actual}" = ".*" ] + +} + + +@test "server/StatefulSet: datadog unified tagging labels get added when global.metrics.datadogIntegration.enabled=true" { + cd `chart_dir` + local labels=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.image=hashicorp/consul-enterprise:1.17.0-ent' \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.metrics.datadogIntegration.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels' | tee /dev/stderr) + + local actual=$( echo "$labels" | \ + yq -r '."tags.datadoghq.com/version"' | tee /dev/stderr ) + [ "${actual}" = "1.17.0-ent" ] + + local actual=$( echo "$labels" | \ + yq -r '."tags.datadoghq.com/env"' | tee /dev/stderr ) + [ "${actual}" = "consul" ] + + local actual=$( echo "$labels" | \ + yq -r '."tags.datadoghq.com/service"' | tee /dev/stderr ) + [ "${actual}" = "consul-server" ] +} + #-------------------------------------------------------------------- # config-configmap @@ -891,16 +1097,16 @@ load _helpers . | tee /dev/stderr | yq -r '.spec.template.spec.securityContext' | tee /dev/stderr) - local actual=$(echo $security_context | jq -r .runAsNonRoot) + local actual=$(echo "$security_context" | yq -r .runAsNonRoot) [ "${actual}" = "true" ] - local actual=$(echo $security_context | jq -r .fsGroup) + local actual=$(echo "$security_context" | yq -r .fsGroup) [ "${actual}" = "1000" ] - local actual=$(echo $security_context | jq -r .runAsUser) + local actual=$(echo "$security_context" | yq -r .runAsUser) [ "${actual}" = "100" ] - local actual=$(echo $security_context | jq -r .runAsGroup) + local actual=$(echo "$security_context" | yq -r .runAsGroup) [ "${actual}" = "1000" ] } @@ -910,14 +1116,26 @@ load _helpers -s templates/server-statefulset.yaml \ --set 'server.securityContext.runAsNonRoot=false' \ --set 'server.securityContext.privileged=true' \ + --set 'server.securityContext.runAsGroup=0' \ + --set 'server.securityContext.runAsUser=0' \ + --set 'server.securityContext.fsGroup=0' \ . | tee /dev/stderr | yq -r '.spec.template.spec.securityContext' | tee /dev/stderr) - local actual=$(echo $security_context | jq -r .runAsNonRoot) + local actual=$(echo "$security_context" | yq -r .runAsNonRoot) [ "${actual}" = "false" ] - local actual=$(echo $security_context | jq -r .privileged) + local actual=$(echo "$security_context" | yq -r .privileged) [ "${actual}" = "true" ] + + local actual=$(echo "$security_context" | yq -r .fsGroup) + [ "${actual}" = "0" ] + + local actual=$(echo "$security_context" | yq -r .runAsUser) + [ "${actual}" = "0" ] + + local actual=$(echo "$security_context" | yq -r .runAsGroup) + [ "${actual}" = "0" ] } #-------------------------------------------------------------------- diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index ef2d0cd877..cb13478911 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -618,6 +618,23 @@ global: # @type: boolean enableAgentMetrics: false + # When set to true, enables Consul to report additional debugging information, including runtime profiling (pprof) data. + # This setting is only required for clusters without ACL enabled. + # If you change this setting, you must restart the agent for the change to take effect. Default is false. + # Only applicable if `global.metrics.enabled` and `global.metrics.AgentMetrics` is true. + # @type: boolean + enableConsulAgentDebug: false + + # Set to true to stop prepending the machine's hostname to gauge-type metrics. Default is false. + # Only applicable if `global.metrics.enabled` and `global.metrics.AgentMetrics` is true. + # @type: boolean + disableAgentHostName: false + + # Configures consul agent metrics. Only applicable if + # Only applicable if `global.metrics.enabled` and `global.metrics.AgentMetrics` is true. + # @type: boolean + enableHostMetrics: false + # Configures the retention time for metrics in Consul clients and # servers. This must be greater than 0 for Consul clients and servers # to expose any metrics at all. @@ -636,6 +653,121 @@ global: # @type: boolean enableTelemetryCollector: false + # This configures the list of filter rules to apply for allowing/blocking + # metrics by prefix in the following format: + # + # A leading "+" will enable any metrics with the given prefix, and a leading "-" will block them. + # If there is overlap between two rules, the more specific rule will take precedence. + # Blocking will take priority if the same prefix is listed multiple times. + # + # - allowList: + metricsPrefixFiltering: + # @type: array + allowList: [] + # @type: array + blockList: [] + + # Configures consul integration configurations for datadog on kubernetes. + # Only applicable if `global.metrics.enabled` and `global.metrics.enableAgentMetrics` is true. + datadogIntegration: + # Enables datadog [Consul autodiscovery integration](https://docs.datadoghq.com/integrations/consul/?tab=containerized#metric-collection) + # by configuring the required `ad.datadoghq.com/consul.checks` annotation. All available datadog-specific checks are enabled. + # + # ~> **Note:** Simply enabling `datadogIntegration` does not enable openmetrics (prometheus) nor DogStatsD + # metrics collection. When using OpenMetrics (i.e., `datadogIntegration.openMetricsPrometheus.enabled=true`) + # This changes the auto-discovery checks datadog will detect to scrape only the `/v1/agent/metrics?format=prometheus` + # API endpoint. When using DogStatsD custom metrics collection, the datadog specific checks will be enabled as + # you can only have either OpenMetrics/Prometheus _or_ DogStatsD enabled at a given time. + # + # @default: false + # @type: boolean + enabled: false + + # Configures Kubernetes Prometheus/OpenMetrics auto-discovery annotations for use with Datadog. + # This configuration is less common and more for advanced usage with custom metrics monitoring + # configurations. See https://docs.datadoghq.com/containers/kubernetes/prometheus/?tab=kubernetesadv2 for more details + # surround further configuration. + openMetricsPrometheus: + # @default: false + # @type: boolean + enabled: false + + datadogOpenTelemetryCollector: + # Enables forwarding of Consul's Telemetry Collector OTLP metrics for + # ingestion by Datadog Agent + # @default: false + # @type: boolean + enable: false + # @default: "http" + # @type: string + protocol: "http" + + # Configuration settings for DogStatsD metrics aggregation service + # that is bundled with the Datadog Agent. + # DogStatsD implements the StatsD protocol and adds a few Datadog-specific extensions: + # - Histogram metric type + # - Service checks + # - Events + # - Tagging + dogstatsd: + enabled: true + # Sets the socket transport type for dogstatsd: + # - "UDS" (Unix Domain Socket): prefixes `unix://` to URL and appends path to socket (i.e., "unix:///var/run/datadog/dsd.socket") + # If set, this will create the required [hostPath](https://kubernetes.io/docs/concepts/storage/volumes/#hostpath) mount for + # managing [DogStatsD with Unix Domain Socket on Kubernetes](https://docs.datadoghq.com/developers/dogstatsd/unix_socket/?tab=kubernetes). + # The volume is mounted using the `DirectoryOrCreate` type, thereby setting `0755` permissions with the same kubelet group ownership. + # + # Applies the following `volumes` and `volumeMounts` to the consul-server stateful set consul containers: + # + # ```yaml + # volumes: + # - name: dsdsocket + # hostPath: + # path: /var/run/datadog + # type: DirectoryOrCreate + # volumeMounts: + # - name: dsdsocket + # mountPath: /var/run/datadog + # readOnly: true + # ``` + # - "UDP" (User Datagram Protocol): assigns address to use `hostname/IP:Port` formatted URL for UDP transport to hostIP based + # dogstatsd sink (i.e., 127.0.0.1:8125). + # + # @default: "UDS" + # @type: string + socketTransportType: "UDS" + # Sets URL path for dogstatsd: + # + # Can be either a path to unix domain socket or an IP Address or Hostname that's reachable from the + # consul-server service, server containers. When using "UDS" the path will be appended. When using "UDP" + # the path will be prepended to the specified `dogstatsdPort`. + # + # @default: "/var/run/datadog/dsd.socket" + # @type: string + dogstatsdAddr: "/var/run/datadog/dsd.socket" + # Configures IP based dogstatsd designated port that will be appended to "UDP" based transport socket IP/Hostname URL. + # + # @default: 8125 + # @type: integer + dogstatsdPort: 8125 + # Configures datadog [autodiscovery](https://docs.datadoghq.com/containers/kubernetes/log/?tab=operator#autodiscovery) + # style [log integration](https://docs.datadoghq.com/integrations/consul/?tab=containerized#log-collection) + # configuration for Consul. + # + # The default settings should handle most Consul Kubernetes deployment schemes. The resultant annotation + # will reside on the consul-server statefulset as autodiscovery annotations. + # (i.e., ad.datadoghq.com/consul.logs: ["source:consul","consul_service:consul-server", ""]) + # + # @default: ["source:consul","consul_service:consul-server"] + # @type: array + dogstatsdTags: ["source:consul","consul_service:consul-server"] + # Namespace + # + # @default: "default" + # @type: string + datadogNamespace: "default" + + # The name (and tag) of the consul-dataplane Docker image used for the # connect-injected sidecar proxies and mesh, terminating, and ingress gateways. # @default: hashicorp/consul-dataplane: diff --git a/control-plane/subcommand/common/common.go b/control-plane/subcommand/common/common.go index 2b1475daa4..4f0d278e55 100644 --- a/control-plane/subcommand/common/common.go +++ b/control-plane/subcommand/common/common.go @@ -28,6 +28,8 @@ const ( // create-federation-secret commands and so lives in this common package. ACLReplicationTokenName = "acl-replication" + DatadogAgentTokenName = "datadog-agent-metrics" + // ACLTokenSecretKey is the key that we store the ACL tokens in when we // create Kubernetes secrets. ACLTokenSecretKey = "token" diff --git a/control-plane/subcommand/server-acl-init/command.go b/control-plane/subcommand/server-acl-init/command.go index 9ceb7f8a11..420b8a1c96 100644 --- a/control-plane/subcommand/server-acl-init/command.go +++ b/control-plane/subcommand/server-acl-init/command.go @@ -60,6 +60,8 @@ type Command struct { flagBindingRuleSelector string flagCreateEntLicenseToken bool + flagCreateDDAgentToken bool + flagDatadogAgentNS string flagSnapshotAgent bool @@ -212,11 +214,15 @@ func (c *Command) init() { c.flags.StringVar((*string)(&c.flagSecretsBackend), "secrets-backend", "kubernetes", `The secrets backend to use. Either "vault" or "kubernetes". Defaults to "kubernetes"`) c.flags.StringVar(&c.flagBootstrapTokenSecretName, "bootstrap-token-secret-name", "", - "The name of the Vault or Kuberenetes secret for the bootstrap token. This token must have `ac::write` permission "+ + "The name of the Vault or Kubernetes secret for the bootstrap token. This token must have `ac::write` permission "+ "in order to create policies and tokens. If not provided or if the secret is empty, then this command will "+ "bootstrap ACLs and write the bootstrap token to this secret.") c.flags.StringVar(&c.flagBootstrapTokenSecretKey, "bootstrap-token-secret-key", "", - "The key within the Vault or Kuberenetes secret containing the bootstrap token.") + "The key within the Vault or Kubernetes secret containing the bootstrap token.") + c.flags.BoolVar(&c.flagCreateDDAgentToken, "create-dd-agent-token", false, + "Enable ACL token creation for datadog agent integration"+ + "Configures the following permissions to grant datadog agent metrics scraping permissions with Consul ACLs enabled"+ + "agent_prefix \"\" {\n policy = \"read\"\n}\nservice_prefix \"\" {\n policy = \"read\"\n}\nnode_prefix \"\" {\n policy = \"read\"\n}") c.flags.DurationVar(&c.flagTimeout, "timeout", 10*time.Minute, "How long we'll try to bootstrap ACLs for before timing out, e.g. 1ms, 2s, 3m") @@ -680,6 +686,20 @@ func (c *Command) Run(args []string) int { } } + if c.flagCreateDDAgentToken { + var err error + rules, err := c.datadogAgentRules() + if err != nil { + c.log.Error("Error templating datadog agent metrics token rules", "err", err) + return 1 + } + err = c.createLocalACL(common.DatadogAgentTokenName, rules, consulDC, primary, dynamicClient) + if err != nil { + c.log.Error(err.Error()) + return 1 + } + } + c.log.Info("server-acl-init completed successfully") return 0 } diff --git a/control-plane/subcommand/server-acl-init/command_test.go b/control-plane/subcommand/server-acl-init/command_test.go index af15429508..e2512cd6c0 100644 --- a/control-plane/subcommand/server-acl-init/command_test.go +++ b/control-plane/subcommand/server-acl-init/command_test.go @@ -176,6 +176,14 @@ func TestRun_TokensPrimaryDC(t *testing.T) { SecretNames: []string{resourcePrefix + "-acl-replication-acl-token"}, LocalToken: false, }, + { + TestName: "Datadog Agent Token", + TokenFlags: []string{"-create-dd-agent-token"}, + PolicyNames: []string{"datadog-agent-metrics-token"}, + PolicyDCs: nil, + SecretNames: []string{resourcePrefix + "-datadog-agent-metrics-acl-token"}, + LocalToken: true, + }, } for _, c := range cases { t.Run(c.TestName, func(t *testing.T) { @@ -325,6 +333,14 @@ func TestRun_TokensReplicatedDC(t *testing.T) { SecretNames: []string{resourcePrefix + "-enterprise-license-acl-token"}, LocalToken: true, }, + { + TestName: "Datadog Agent Token", + TokenFlags: []string{"-create-dd-agent-token"}, + PolicyNames: []string{"datadog-agent-metrics-token-dc2"}, + PolicyDCs: []string{"dc2"}, + SecretNames: []string{resourcePrefix + "-datadog-agent-metrics-acl-token"}, + LocalToken: true, + }, } for _, c := range cases { t.Run(c.TestName, func(t *testing.T) { @@ -401,6 +417,12 @@ func TestRun_TokensWithProvidedBootstrapToken(t *testing.T) { PolicyNames: []string{"acl-replication-token"}, SecretNames: []string{resourcePrefix + "-acl-replication-acl-token"}, }, + { + TestName: "Datadog Agent Token", + TokenFlags: []string{"-create-dd-agent-token"}, + PolicyNames: []string{"datadog-agent-metrics-token"}, + SecretNames: []string{resourcePrefix + "-datadog-agent-metrics-acl-token"}, + }, } for _, c := range cases { t.Run(c.TestName, func(t *testing.T) { diff --git a/control-plane/subcommand/server-acl-init/rules.go b/control-plane/subcommand/server-acl-init/rules.go index 2732188305..f408037157 100644 --- a/control-plane/subcommand/server-acl-init/rules.go +++ b/control-plane/subcommand/server-acl-init/rules.go @@ -401,6 +401,32 @@ partition "default" { return c.renderRules(aclReplicationRulesTpl) } +func (c *Command) datadogAgentRules() (string, error) { + ddAgentRulesTpl := `{{- if .EnablePartitions }} +partition "{{ .PartitionName }}" { +{{- end }} + agent_prefix "" { + policy = "read" + } + node_prefix "" { + policy = "read" + } +{{- if .EnableNamespaces }} + namespace_prefix "" { +{{- end }} + service_prefix "" { + policy = "read" + } +{{- if .EnableNamespaces }} + } +{{- end }} +{{- if .EnablePartitions }} +} +{{- end }} +` + return c.renderRules(ddAgentRulesTpl) +} + func (c *Command) rulesData() rulesData { return rulesData{ EnablePartitions: c.consulFlags.Partition != "",