Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3e61e2e
feat(otel-container-insights): add ServiceMonitor/PodMonitor v2 OTLP …
wenegiemepraise Jun 19, 2026
b721ec3
feat(observability): support per-node Target Allocator allocation str…
wenegiemepraise Jun 26, 2026
0222e9f
Merge remote-tracking branch 'origin/main' into otel-ci-smpm-scrape-path
wenegiemepraise Jul 9, 2026
a51dc39
Merge branch 'otel-ci-smpm-scrape-path' into helm-per-node-allocation
wenegiemepraise Jul 9, 2026
072635b
feat(observability): bundle ServiceMonitor/PodMonitor CRDs for zero-s…
wenegiemepraise Jul 9, 2026
3e51214
refactor(observability): bundle SM/PM scraping under otelContainerIns…
wenegiemepraise Jul 2, 2026
48e8330
docs(otel-ci): correct $$$1 escaping comment
wenegiemepraise Jul 20, 2026
32b6ae5
feat(observability): grant TA CRD watch RBAC under scraping gate
wenegiemepraise Jul 21, 2026
ee2df6e
fix(observability): restore unhyphenated operator feature-gate IDs
wenegiemepraise Jul 21, 2026
cd3fb5e
fix(observability): gate prometheuscr scope processor on SM/PM enable…
wenegiemepraise Jul 21, 2026
7911c6c
fix(observability): render TA ClusterRole/Binding once, not per agent
wenegiemepraise Jul 21, 2026
8bf3d4e
fix(observability): bump bundled SM/PM CRDs to prometheus-operator v0…
wenegiemepraise Jul 22, 2026
dee02ff
fix(observability): match release-namespace when adopting bundled CRDs
wenegiemepraise Jul 22, 2026
e753a60
test(observability): add shebang and anchor CRD matrix grep patterns
wenegiemepraise Jul 22, 2026
7dd2d8d
fix(observability): fail on invalid prometheusCRDs.install value
wenegiemepraise Jul 22, 2026
1a46b3f
Merge otel-ci-smpm-scrape-path (#329) into helm-crd-bundling
wenegiemepraise Jul 22, 2026
7bfede6
fix(observability): scope per-node allocationStrategy to the otelCI path
wenegiemepraise Jul 22, 2026
d57f318
docs(observability): clarify target_node is user-supplied, not chart …
wenegiemepraise Jul 22, 2026
d8a49d3
fix(observability): stop stamping node on the SM/PM scraping path
wenegiemepraise Jul 22, 2026
c626240
Merge otel-ci-smpm-scrape-path (#329) into helm-per-node-allocation
wenegiemepraise Jul 22, 2026
028a408
Merge helm-per-node-allocation (#330) into helm-crd-bundling
wenegiemepraise Jul 22, 2026
e61d4eb
feat(observability): alias legacy prometheus-scrape flags with deprec…
wenegiemepraise Jul 22, 2026
530c019
Merge helm-crd-bundling (#331) into helm-flag-restructure
wenegiemepraise Jul 23, 2026
6d95450
refactor(observability): drop net-new NOTES.txt deprecation notice
wenegiemepraise Jul 23, 2026
e032f1a
fix(observability): reject prometheusScrape with no monitors
wenegiemepraise Jul 24, 2026
ef3715b
fix(observability): gate needsCR on targetAllocator enabled
wenegiemepraise Jul 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6405,9 +6405,10 @@ spec:
allocationStrategy:

@musa-asad musa-asad Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allocationStrategy gains per-node only in the crds/ copy, which Helm doesn't touch on upgrade, so upgrading a release whose CRD predates the enum makes the API server reject the rendered per-node. Could we move this CRD into templates/ and gate it like the bundled monitor CRDs?

description: |-
AllocationStrategy determines which strategy the target allocator should use for allocation.
The current option is consistent-hashing.
The options are consistent-hashing and per-node.
enum:
- consistent-hashing
- per-node
type: string
enabled:
description: Enabled indicates whether to use a target allocation
Expand Down
77 changes: 77 additions & 0 deletions charts/amazon-cloudwatch-observability/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,33 @@ Expand the name of the chart.
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Whether to bundle the community ServiceMonitor/PodMonitor CRDs. Honours
.Values.otelContainerInsights.prometheusScrape.crds.install:
"always" => true; "never" => empty;
"auto" (default) => true only when otelContainerInsights.enabled AND
otelContainerInsights.prometheusScrape.enabled are both true.
Returns the string "true" when CRDs should be rendered, empty otherwise.
*/}}
{{- define "amazon-cloudwatch-observability.prometheusCRDsEnabled" -}}

@musa-asad musa-asad Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prometheusCRDsEnabled only matches always/never exactly, so a typo like nevr falls through and silently bundles the CRDs when otelCI is on. Could we add a fail branch for anything that isn't auto, always, or never?

{{- $install := (dig "prometheusScrape" "crds" "install" "auto" .Values.otelContainerInsights) -}}
{{- /* Back-compat: honor the legacy top-level prometheusCRDs.install if set (deprecated). */ -}}
{{- if hasKey .Values "prometheusCRDs" -}}
{{- $install = (dig "install" $install .Values.prometheusCRDs) -}}
{{- end -}}
{{- $scrapeEnabled := (dig "prometheusScrape" "enabled" true .Values.otelContainerInsights) -}}
{{- if eq $install "always" -}}
true
{{- else if eq $install "never" -}}
{{- else if eq $install "auto" -}}
{{- if and .Values.otelContainerInsights.enabled $scrapeEnabled -}}
true
{{- end -}}
{{- else -}}
{{- fail (printf "prometheusCRDs.install must be one of \"auto\", \"always\", or \"never\", got: %s" $install) -}}
{{- end -}}
{{- end -}}

{{- define "amazon-cloudwatch-observability.common.tolerations" -}}
{{- $tolerations := .context.Values.tolerations }}
{{- if .component }}
Expand Down Expand Up @@ -191,6 +218,56 @@ Logic:
{{- end -}}
{{- end -}}

{{/*
Returns "true" when otelContainerInsights-driven ServiceMonitor/PodMonitor scraping
applies to the given agent. True when otelContainerInsights is enabled, the agent is
the configured targetAgent, and at least one of serviceMonitor/podMonitor is enabled.
Accepts a dict with "agentName" (string) and "context" (root context $).
*/}}
{{- define "cloudwatch-agent.otelCIScrapeEnabled" -}}

@musa-asad musa-asad Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

otelCIScrapeEnabled no longer short circuits when both serviceMonitor.enabled and podMonitor.enabled are false, so prometheusScrape.enabled=true renders an idle target-allocator that bundles CRDs but discovers nothing. Could we reject that both false case and add a matrix assertion?

{{- $ctx := .context -}}
{{- $agentName := .agentName -}}
{{- if and $ctx.Values.otelContainerInsights.enabled (eq $agentName $ctx.Values.otelContainerInsights.targetAgent) (dig "prometheusScrape" "enabled" true $ctx.Values.otelContainerInsights) -}}
true
{{- end -}}
{{- end -}}

{{/*
Whether ServiceMonitor / PodMonitor discovery is enabled. Honors the legacy
otelContainerInsights.serviceMonitor.enabled / .podMonitor.enabled if set
(deprecated), otherwise otelContainerInsights.prometheusScrape.<monitor>.enabled
(default true). Return "true" when enabled, empty otherwise.
*/}}
{{- define "cloudwatch-agent.serviceMonitorEnabled" -}}
{{- $v := dig "prometheusScrape" "serviceMonitor" "enabled" true .Values.otelContainerInsights -}}
{{- if hasKey .Values.otelContainerInsights "serviceMonitor" -}}
{{- $v = dig "serviceMonitor" "enabled" $v .Values.otelContainerInsights -}}
{{- end -}}
{{- if $v -}}true{{- end -}}
{{- end -}}

{{- define "cloudwatch-agent.podMonitorEnabled" -}}
{{- $v := dig "prometheusScrape" "podMonitor" "enabled" true .Values.otelContainerInsights -}}
{{- if hasKey .Values.otelContainerInsights "podMonitor" -}}
{{- $v = dig "podMonitor" "enabled" $v .Values.otelContainerInsights -}}
{{- end -}}
{{- if $v -}}true{{- end -}}
{{- end -}}

{{/*
Reject a contradictory scraping config. prometheusScrape.enabled=true with BOTH
ServiceMonitor and PodMonitor discovery disabled would render an idle Target Allocator
(and bundle CRDs) that discovers nothing. Fail loudly rather than ship a no-op path.
Invoked from an always-rendered template so it runs regardless of which agents render.
*/}}
{{- define "cloudwatch-agent.validatePrometheusScrape" -}}
{{- if and .Values.otelContainerInsights.enabled (dig "prometheusScrape" "enabled" true .Values.otelContainerInsights) -}}
{{- if and (ne (include "cloudwatch-agent.serviceMonitorEnabled" .) "true") (ne (include "cloudwatch-agent.podMonitorEnabled" .) "true") -}}
{{- fail "otelContainerInsights.prometheusScrape.enabled=true requires at least one of prometheusScrape.serviceMonitor.enabled or prometheusScrape.podMonitor.enabled to be true; enable one, or set prometheusScrape.enabled=false" -}}
{{- end -}}
{{- end -}}
{{- end -}}

{{/*
Helper function to modify cloudwatch-agent config
*/}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,8 @@ processors:
- k8s.job.name
- k8s.cronjob.name
labels:
# $$$1 is Helm escaping: $$$ → $$ (Helm) → $ (OTel env resolver) → literal $1 backreference
# $$$1 -> literal $1 backreference (group 1 = label key). The agent's OTel confmap
# resolves it twice (expandconverter + resolver), each collapsing $$->$; Helm leaves it as-is.
- tag_name: "k8s.pod.label.$$$1"
key_regex: "(.*)"
from: pod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,23 @@ receivers:
- targets:
- ${env:HOST_IP}:10250

{{- if dig "prometheusScrape" "enabled" true .Values.otelContainerInsights }}
# ServiceMonitor/PodMonitor scraping via the Target Allocator (prometheusCR discovery).
# The Target Allocator deployed for the targetAgent serves the scrape jobs derived from
# ServiceMonitor/PodMonitor CRs; this receiver pulls this collector's assigned shard and
# routes the series into the v2 OTLP pipeline (-> CloudWatch/Zeus). Requires the Target
# Allocator + prometheusCR to be enabled for the targetAgent and the POD_NAME env (set below).
prometheus/cw_k8s_ci_v0_prometheuscr:
target_allocator:
endpoint: https://{{ .Values.otelContainerInsights.targetAgent }}-target-allocator-service:80
interval: {{ .Values.otelContainerInsights.metricResolution }}
collector_id: ${env:POD_NAME}
tls:
ca_file: /etc/amazon-cloudwatch-observability-agent-cert/tls-ca.crt
cert_file: /etc/amazon-cloudwatch-observability-agent-ta-client-cert/client.crt
key_file: /etc/amazon-cloudwatch-observability-agent-ta-client-cert/client.key
{{- end }}

{{- if .Values.dcgmExporter.enabled }}
prometheus/cw_k8s_ci_v0_dcgm:
config:
Expand Down Expand Up @@ -329,6 +346,18 @@ processors:
- set(attributes["cloudwatch.solution"], "k8s-otel-container-insights")
- set(attributes["cloudwatch.pipeline"], "efa")

{{- if dig "prometheusScrape" "enabled" true .Values.otelContainerInsights }}
transform/cw_k8s_ci_v0_set_scope_prometheuscr:
error_mode: ignore
metric_statements:
- context: scope
statements:
- set(scope.schema_url, "")
- set(attributes["cloudwatch.source"], "cloudwatch-agent")
- set(attributes["cloudwatch.solution"], "k8s-otel-container-insights")
- set(attributes["cloudwatch.pipeline"], "prometheus-cr")
{{- end }}

transform/cw_k8s_ci_v0_set_scope_ebs_csi:
error_mode: ignore
metric_statements:
Expand Down Expand Up @@ -427,6 +456,8 @@ processors:
- k8s.job.name
- k8s.cronjob.name
labels:
# $$$1 -> literal $1 backreference (group 1 = label key). The agent's OTel confmap
# resolves it twice (expandconverter + resolver), each collapsing $$->$; Helm leaves it as-is.
- tag_name: "k8s.pod.label.$$$1"
key_regex: "(.*)"
from: pod
Expand Down Expand Up @@ -829,6 +860,20 @@ service:
exporters:
- otlphttp/cw_k8s_ci_v0_metrics_dest

{{- if dig "prometheusScrape" "enabled" true .Values.otelContainerInsights }}
metrics/cw_k8s_ci_v0_prometheuscr:
receivers: [prometheus/cw_k8s_ci_v0_prometheuscr]
processors:
- filter/cw_k8s_ci_v0_scrape_metadata
- metricstarttime/cw_k8s_ci_v0
- transform/cw_k8s_ci_v0_set_cluster_name
- transform/cw_k8s_ci_v0_set_scope_prometheuscr
- resourcedetection/cw_k8s_ci_v0
- batch/cw_k8s_ci_v0_metrics_dest
exporters:
- otlphttp/cw_k8s_ci_v0_metrics_dest
{{- end }}

{{- if .Values.dcgmExporter.enabled }}
metrics/cw_k8s_ci_v0_dcgm:
receivers: [prometheus/cw_k8s_ci_v0_dcgm]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{{- if .Values.agent.enabled }}
{{- include "cloudwatch-agent.validatePrometheusScrape" . -}}
{{- if and (.Values.agent.autoGenerateCert.enabled) (not .Values.agent.certManager.enabled) -}}
{{- $altNames := list ( printf "%s-service" (include "dcgm-exporter.name" .) ) ( printf "%s-service" (include "neuron-monitor.name" .) ) ( printf "%s-service.%s.svc" (include "dcgm-exporter.name" .) .Release.Namespace ) ( printf "%s-service.%s.svc" (include "neuron-monitor.name" .) .Release.Namespace ) ( printf "%s-service" (include "node-exporter.name" .) ) ( printf "%s-service.%s.svc" (include "node-exporter.name" .) .Release.Namespace ) ( include "kube-state-metrics.name" . ) ( printf "%s.%s.svc" (include "kube-state-metrics.name" .) .Release.Namespace ) -}}
{{- range $i, $customAgent := .Values.agents }}
Expand Down Expand Up @@ -110,21 +111,50 @@ spec:
{{- else if ne (trimAll " \n\t" $generatedOtelConfig) "{}" }}
otelConfig: {{ include "cloudwatch-agent.modify-otel-config" (merge (dict "OtelConfig" $generatedOtelConfig) $) }}
{{- end }}
{{- $otelCIScrape := eq (include "cloudwatch-agent.otelCIScrapeEnabled" (dict "agentName" $agent.name "context" $)) "true" }}
{{- $taEnabled := or $agent.prometheus.targetAllocator.enabled $otelCIScrape }}
{{- $allocationStrategy := $agent.prometheus.targetAllocator.allocationStrategy | default "consistent-hashing" }}
{{- if $otelCIScrape }}
{{- $allocationStrategy = (dig "prometheusScrape" "allocationStrategy" "per-node" $.Values.otelContainerInsights) }}
{{- end }}
{{- if $agent.prometheus.config }}
prometheus:
{{- with $agent.prometheus.config }}
config:
{{- toYaml . | nindent 6 }}
{{- end }}
{{- else if $taEnabled }}
# The operator builds the Target Allocator config from spec.prometheus and requires
# a prometheus.config containing a scrape_configs key (see operator
# targetallocator/adapters GetPromConfig). With prometheusCR (ServiceMonitor/
# PodMonitor) discovery the static scrape list is empty -- the Target Allocator
# discovers targets from ServiceMonitor/PodMonitor CRs.
prometheus:
config:
scrape_configs: []
{{- end }}
{{- if $agent.prometheus.targetAllocator.enabled }}
{{- if $taEnabled }}
targetAllocator:
enabled: {{ $agent.prometheus.targetAllocator.enabled | default false }}
enabled: true
image: {{ template "target-allocator.image" (merge $agent.prometheus.targetAllocator.image (dict "region" $.Values.region)) }}
allocationStrategy: "consistent-hashing"
{{- if $agent.prometheus.targetAllocator.prometheusCR.enabled }}
allocationStrategy: {{ $allocationStrategy | quote }}
{{- if or $agent.prometheus.targetAllocator.prometheusCR.enabled $otelCIScrape }}
prometheusCR:
enabled: {{ $agent.prometheus.targetAllocator.prometheusCR.enabled | default false }}
enabled: true
{{- if $otelCIScrape }}
scrapeInterval: {{ $.Values.otelContainerInsights.metricResolution | quote }}
{{- if ne (include "cloudwatch-agent.serviceMonitorEnabled" $) "true" }}
# serviceMonitor disabled: select a label no real ServiceMonitor
# carries, so the Target Allocator discovers no ServiceMonitors.
serviceMonitorSelector:
amazon-cloudwatch-observability.aws/otel-ci-scrape: disabled
{{- end }}
{{- if ne (include "cloudwatch-agent.podMonitorEnabled" $) "true" }}
# podMonitor disabled: select a label no real PodMonitor carries.
podMonitorSelector:
amazon-cloudwatch-observability.aws/otel-ci-scrape: disabled
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- with $agent.resources }}
Expand Down Expand Up @@ -281,6 +311,12 @@ spec:
{{- if $.Values.otelContainerInsights.enabled }}
- name: OTEL_CI_VERSION
value: "1.0.0"
# POD_NAME is used as the Target Allocator collector_id by the prometheusCR
# receiver (otel-container-insights.config) so each agent pulls its own shard.
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
{{- end }}
{{- with $agent.env }}
{{- . | toYaml | nindent 2 }}
Expand Down
Loading
Loading