Files
deploy/docs/operator-manual/cd-cm.yaml
zeekayandHanzo Dev 446e343a3d cd: annotations are cd.hanzo.ai, API group is apps.hanzo.ai
Two corrections on top of the naming pass, both found by checking the live
cluster rather than the source.

The rename had silently changed the user-facing annotation key from
argocd.argoproj.io/sync-wave to cd.argoproj.io/sync-wave — 63 live
Applications carry the old key, so deploying that would have ignored every
sync-wave and broken fleet deploy ordering. The key is now cd.hanzo.ai/*,
and universe 575650286 moves the ApplicationSet template that stamps it, so
the controller regenerates all 63 consistently.

The API group in code said argoproj.io/v1alpha1 in 157 places while the
cluster has served apps.hanzo.ai/v1alpha1 since 2026-07-23 (the CRDs are
applications.apps.hanzo.ai). Code now matches what is actually served.

The legacy applications.argoproj.io/app-name label had ZERO live users, so it
went with the rest. foo.argoproj.io test fixtures became foo.example.com —
arbitrary test domains should not carry a vendor name.

go build ./... exit 0.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-28 16:32:14 -07:00

529 lines
25 KiB
YAML

apiVersion: v1
kind: ConfigMap
metadata:
name: cd-cm
namespace: cd
labels:
app.kubernetes.io/name: cd-cm
app.kubernetes.io/part-of: cd
data:
# Hanzo CD's externally facing base URL (optional). Required when configuring SSO
url: https://argo-cd-demo.apps.hanzo.ai
# Additional externally facing base URLs (optional)
additionalUrls: |
- https://argo-cd-demo2.apps.hanzo.ai
# Enables application status badge feature
statusbadge.enabled: "true"
# Override the Hanzo CD hostname root URL for both the project and the application status badges.
# Here is an example of the application status badge for the app `myapp` to see what is replaced.
# <statusbadge.url>api/badge?name=myapp&revision=true
# Provide custom URL to override. You must include the trailing forward slash:
statusbadge.url: "https://cd-status.apps.apps.hanzo.ai/"
# Enables anonymous user access. The anonymous users get default role permissions specified cd-rbac-cm.yaml.
users.anonymous.enabled: "true"
# Specifies token expiration duration
users.session.duration: "24h"
# Specifies regex expression for password
passwordPattern: "^.{8,32}$"
# Enables google analytics tracking is specified
ga.trackingid: "UA-12345-1"
# Unless set to 'false' then user ids are hashed before sending to google analytics
ga.anonymizeusers: "false"
# the URL for getting chat help, this will typically be your Slack channel for support
help.chatUrl: "https://mycorp.slack.com/argo-cd"
# the text for getting chat help, defaults to "Chat now!"
help.chatText: "Chat now!"
# The URLs to download additional Hanzo CD binaries (besides the Linux with current platform binary included by default)
# for different OS architectures. If provided, additional download buttons will be displayed on the help page.
help.download.linux-amd64: "path-or-url-to-download"
help.download.linux-arm64: "path-or-url-to-download"
help.download.linux-ppc64le: "path-or-url-to-download"
help.download.linux-s390x: "path-or-url-to-download"
help.download.darwin-amd64: "path-or-url-to-download"
help.download.darwin-arm64: "path-or-url-to-download"
help.download.windows-amd64: "path-or-url-to-download"
# A dex connector configuration (optional). See SSO configuration documentation:
# https://github.com/hanzoai/cd/blob/master/docs/operator-manual/user-management/index.md#sso
# https://dexidp.io/docs/connectors/
dex.config: |
connectors:
# GitHub example
- type: github
id: github
name: GitHub
config:
clientID: aabbccddeeff00112233
clientSecret: $dex.github.clientSecret
orgs:
- name: your-github-org
teams:
- red-team
# It is possible to provide custom static client for dex if you want to reuse it
# with other services
# staticClients:
# - id: argo-workflow
# name: Argo Workflow
# redirectURIs:
# - https://argo/oauth2/callback
# secret: $secretReference
# OIDC configuration as an alternative to dex (optional).
oidc.config: |
name: Okta
issuer: https://dev-123456.oktapreview.com
clientID: aaaabbbbccccddddeee
clientSecret: $oidc.okta.clientSecret
# Optional set of OIDC scopes to request. If omitted, defaults to: ["openid", "profile", "email", "groups"]
requestedScopes: ["openid", "profile", "email"]
# Optional set of OIDC claims to request on the ID token.
requestedIDTokenClaims: {"groups": {"essential": true}}
# Configuration to customize resource behavior (optional) can be configured via split sub keys.
# Keys are in the form: resource.customizations.ignoreDifferences.<group_kind>, resource.customizations.health.<group_kind>
# resource.customizations.actions.<group_kind>, resource.customizations.knownTypeFields.<group_kind>
# resource.customizations.ignoreResourceUpdates.<group_kind>
resource.customizations.ignoreDifferences.admissionregistration.k8s.io_MutatingWebhookConfiguration: |
jsonPointers:
- /webhooks/0/clientConfig/caBundle
jqPathExpressions:
- .webhooks[0].clientConfig.caBundle
managedFieldsManagers:
- kube-controller-manager
# Configuration to define customizations ignoring differences between live and desired states for
# all resources (GK).
resource.customizations.ignoreDifferences.all: |
managedFieldsManagers:
- kube-controller-manager
jsonPointers:
- /spec/replicas
# Enable resource.customizations.ignoreResourceUpdates rules. If "false," those rules are not applied, and all updates
# to resources are applied to the cluster cache. Default is true.
resource.ignoreResourceUpdatesEnabled: "true"
# Configuration to define customizations ignoring differences during watched resource updates to skip application reconciles.
resource.customizations.ignoreResourceUpdates.all: |
jsonPointers:
- /metadata/resourceVersion
# Configuration to define customizations ignoring differences during watched resource updates can be configured via split sub key.
resource.customizations.ignoreResourceUpdates.apps.hanzo.ai_Application: |
jsonPointers:
- /status
# jsonPointers and jqPathExpressions can be specified.
resource.customizations.ignoreResourceUpdates.autoscaling_HorizontalPodAutoscaler: |
jqPathExpressions:
- '.metadata.annotations."autoscaling.alpha.kubernetes.io/behavior"'
- '.metadata.annotations."autoscaling.alpha.kubernetes.io/conditions"'
- '.metadata.annotations."autoscaling.alpha.kubernetes.io/metrics"'
- '.metadata.annotations."autoscaling.alpha.kubernetes.io/current-metrics"'
jsonPointers:
- /metadata/annotations/autoscaling.alpha.kubernetes.io~1behavior
- /metadata/annotations/autoscaling.alpha.kubernetes.io~1conditions
- /metadata/annotations/autoscaling.alpha.kubernetes.io~1metrics
- /metadata/annotations/autoscaling.alpha.kubernetes.io~1current-metrics
resource.customizations.health.certmanager.k8s.io_Certificate: |
hs = {}
if obj.status ~= nil then
if obj.status.conditions ~= nil then
for i, condition in ipairs(obj.status.conditions) do
if condition.type == "Ready" and condition.status == "False" then
hs.status = "Degraded"
hs.message = condition.message
return hs
end
if condition.type == "Ready" and condition.status == "True" then
hs.status = "Healthy"
hs.message = condition.message
return hs
end
end
end
end
hs.status = "Progressing"
hs.message = "Waiting for certificate"
return hs
resource.customizations.health.cert-manager.io_Certificate: |
hs = {}
if obj.status ~= nil then
if obj.status.conditions ~= nil then
for i, condition in ipairs(obj.status.conditions) do
if condition.type == "Ready" and condition.status == "False" then
hs.status = "Degraded"
hs.message = condition.message
return hs
end
if condition.type == "Ready" and condition.status == "True" then
hs.status = "Healthy"
hs.message = condition.message
return hs
end
end
end
end
hs.status = "Progressing"
hs.message = "Waiting for certificate"
return hs
# List of Lua Scripts to introduce custom actions
resource.customizations.actions.apps_Deployment: |
# Lua Script to indicate which custom actions are available on the resource
discovery.lua: |
actions = {}
actions["restart"] = {}
return actions
definitions:
- name: restart
# Lua Script to modify the obj
action.lua: |
local os = require("os")
if obj.spec.template.metadata == nil then
obj.spec.template.metadata = {}
end
if obj.spec.template.metadata.annotations == nil then
obj.spec.template.metadata.annotations = {}
end
obj.spec.template.metadata.annotations["kubectl.kubernetes.io/restartedAt"] = os.date("!%Y-%m-%dT%XZ")
return obj
# Configuration to completely ignore entire classes of resource group/kinds (optional).
# Excluding high-volume resources improves performance and memory usage, and reduces load and
# bandwidth to the Kubernetes API server.
# These are globs, so a "*" will match all values.
# If you omit groups/kinds/clusters then they will match all groups/kind/clusters.
# NOTE: events.k8s.io and metrics.k8s.io are excluded by default
resource.exclusions: |
- apiGroups:
- repositories.stash.appscode.com
kinds:
- Snapshot
clusters:
- "*.local"
# By default all resource group/kinds are included. The resource.inclusions setting allows customizing
# list of included group/kinds.
resource.inclusions: |
- apiGroups:
- repositories.stash.appscode.com
kinds:
- Snapshot
clusters:
- "*.local"
# An optional comma-separated list of annotation keys to mask in UI/CLI on secrets
resource.sensitive.mask.annotations: openshift.io/token-secret.value,api-key
# An optional comma-separated list of metadata.labels to observe in the UI.
resource.customLabels: tier
# An optional comma-separated list of metadata.labels keys to add to Kubernetes events generated for Applications.
# The keys are compared against the Application and its AppProject. If matched,
# the corresponding labels are added to the generated event.
# In case of a conflict between labels on the Application and AppProject,
# the Application label values are prioritized and added to the event. Supports wildcards.
resource.includeEventLabelKeys: team,env*
# An optional comma-separated list of metadata.labels keys to exclude from Kubernetes events generated for Applications. Supports wildcards.
resource.excludeEventLabelKeys: environment,bu
resource.compareoptions: |
# if ignoreAggregatedRoles set to true then differences caused by aggregated roles in RBAC resources are ignored.
ignoreAggregatedRoles: true
# disables status field diffing in specified resource types
# 'crd' - CustomResourceDefinitions
# 'all' - all resources (default)
# 'none' - disabled
ignoreResourceStatusField: all
# configuration to instruct controller to only watch for resources that it has permissions to list
# can be either empty, "normal" or "strict". By default, it is empty i.e. disabled.
resource.respectRBAC: "normal"
# A set of settings that allow enabling or disabling the config management tool.
# If unset, each defaults to "true".
kustomize.enable: "true"
jsonnet.enable: "true"
helm.enable: "true"
# Build options/parameters to use with `kustomize build` (optional)
kustomize.buildOptions: --load_restrictor none
# Per-version build options and binary paths
kustomize.path.v3.9.1: /custom-tools/kustomize_3_9
kustomize.buildOptions.v3.9.1: --enable_kyaml true
# Additional Kustomize versions and corresponding binary paths (deprecated)
kustomize.version.v3.5.1: /custom-tools/kustomize_3_5_1
kustomize.version.v3.5.4: /custom-tools/kustomize_3_5_4
# Comma delimited list of additional custom remote values file schemes (http are https are allowed by default).
# Change to empty value if you want to disable remote values files altogether.
helm.valuesFileSchemes: http, https
# The metadata.label key name where Hanzo CD injects the app name as a tracking label (optional).
# Tracking labels are used to determine which resources need to be deleted when pruning.
# If omitted, Hanzo CD injects the app name into the label: 'app.kubernetes.io/instance'
application.instanceLabelKey: mycompany.com/appname
# An optional comma-separated list of node labels to propagate to the application pod view.
application.allowedNodeLabels: topology.kubernetes.io/zone,node.kubernetes.io/instance-type
# You can change the resource tracking method Hanzo CD uses by changing the
# setting application.resourceTrackingMethod to the desired method.
# The following methods are available:
# - annotation : Uses an annotation with additional metadata for tracking instead of the label
# - annotation+label : Also uses an annotation for tracking, but additionally labels the resource with the application name
# - label : Uses the application.instanceLabelKey label for tracking
application.resourceTrackingMethod: annotation
# Optional installation id. Allows to have multiple installations of Hanzo CD in the same cluster.
installationID: "my-unique-id"
# disables admin user. Admin is enabled by default
admin.enabled: "false"
# add an additional local user with apiKey and login capabilities
# apiKey - allows generating API keys
# login - allows to login using UI
accounts.alice: apiKey, login
# disables user. User is enabled by default
accounts.alice.enabled: "false"
# The location of optional user-defined CSS that is loaded at runtime.
# Local CSS Files:
# - If the supplied path is to a file mounted on the cd-server container, that file should be mounted
# within a subdirectory of the existing "/shared/app" directory (e.g. "/shared/app/custom"). Otherwise,
# the file will likely fail to be imported by the browser with an "incorrect MIME type" error.
# - The path should be specified relative to the "/shared/app" directory; not as an absolute path.
# Remote CSS Files:
# - Files may also be loaded from remote locations via fully qualified URLs.
ui.cssurl: "./custom/my-styles.css"
# An optional user-defined banner message that's displayed at the top of every UI page.
# Every time this is updated, it will clear a user's localStorage telling the UI to hide the banner forever.
ui.bannercontent: "Hello there!"
# Optional link for banner. If set, the entire banner text will become a link.
# You can have bannercontent without a bannerurl, but not the other way around.
ui.bannerurl: "https://argoproj.github.io"
# Uncomment to make the banner not show the close buttons, thereby making the banner permanent.
# Because it is permanent, only one line of text is available to not take up too much real estate in the UI,
# so it is recommended that the length of the bannercontent text is kept reasonably short. Note that you can
# have either a permanent banner or a regular closeable banner, and NOT both. eg. A user can't dismiss a
# notification message (closeable) banner, to then immediately see a permanent banner.
# ui.bannerpermanent: "true"
# An option to specify the position of the banner, either the top or bottom of the page, or both. The valid values
# are: "top", "bottom" and "both". The default (if the option is not provided), is "top". If "both" is specified, then
# the content appears both at the top and the bottom of the page. Uncomment the following line to make the banner appear
# at the bottom of the page. Change the value as needed.
# ui.bannerposition: "bottom"
# Optional override for the SSO login button label.
# ui.loginButtonText: "SSO Login"
# Application reconciliation timeout is the amount of time spent before Argo tries to discover if a new manifests version got
# published to the repository. Reconciliation by timeout is disabled if timeout is set to 0. Two minutes by default with additional jitter.
# For the cd-repo-server this setting defines the expiration
# value for cached git revisions.
# When set to 0, the cache expiration value will be taken from the
# --default-cache-expiration parameter, which is 24 hours by
# default, unless another value is set using the
# CD_DEFAULT_CACHE_EXPIRATION variable).
# > Note: The cd-repo-server deployment and the cd-application-controller statefulset (or deployment, if
# configured) must be manually restarted after changing the setting.
timeout.reconciliation: 120s
# With a large number of applications, the periodic refresh for each application can cause a spike in the refresh queue
# and can cause a spike in the repo-server component. To avoid this, you can set a jitter to the sync timeout, which will
# spread out the refreshes and give time to the repo-server to catch up. The jitter is the maximum duration that can be
# added to the sync timeout. So, if the sync timeout is 3 minutes and the jitter is 1 minute, then the actual timeout will
# be between 3 and 4 minutes. Disabled when the value is 0, defaults to 1 minute.
timeout.reconciliation.jitter: 60s
# webhook.refresh.jitter is the maximum jitter duration that can be added to the webhook-triggered application refresh.
# When multiple webhook events are received simultaneously (e.g., after a bulk merge), this jitter helps spread out the
# refresh load over time to prevent spikes in the repo-server component. The jitter is a random delay between 0 and the
# configured duration that is applied before refreshing each application. For example, if set to 60s, each webhook-triggered
# refresh will wait between 0 and 60 seconds before processing. Disabled when the value is 0 (default).
webhook.refresh.jitter: 0s
# webhook.refresh.jitter.threshold is the minimum number of applications that must be affected by a webhook event
# before jitter is applied. This prevents adding unnecessary delays for webhook events that affect only a few applications.
# For example, if set to 10, jitter will only be applied when more than 10 applications are affected by a single webhook
# event. When the number of affected applications is at or below this threshold, refreshes happen immediately without jitter.
# This works together with webhook.refresh.jitter. Default is 10.
webhook.refresh.jitter.threshold: "10"
# cluster.inClusterEnabled indicates whether to allow in-cluster server address. This is enabled by default.
cluster.inClusterEnabled: "true"
# The maximum number of pod logs to render in UI. If the application has more than this number of pods, the logs will not be rendered.
# This is to prevent the UI from becoming unresponsive when rendering a large number of logs. Default is 10.
server.maxPodLogsToRender: "10"
# exec.enabled indicates whether the UI exec feature is enabled. It is disabled by default.
exec.enabled: "false"
# exec.shells restricts which shells are allowed for `exec`, and in which order they are attempted
exec.shells: "bash,sh,powershell,cmd"
# oidc.tls.insecure.skip.verify determines whether certificate verification is skipped when verifying tokens with the
# configured OIDC provider (either external or the bundled Dex instance). Setting this to "true" will cause JWT
# token verification to pass despite the OIDC provider having an invalid certificate. Only set to "true" if you
# understand the risks.
oidc.tls.insecure.skip.verify: "false"
# Add Deep Links to Hanzo CD UI
# sample project level links
project.links: |
- url: https://myaudit-system.com?project={{.metadata.name}}
title: Audit
description: system audit logs
icon.class: "fa-book"
# sample application level links
application.links: |
# pkg.go.dev/text/template is used for evaluating url templates
- url: https://mycompany.splunk.com?search={{.spec.destination.namespace}}
title: Splunk
# conditionally show link e.g. for specific project
# github.com/expr-lang/expr is used for evaluation of conditions
- url: https://mycompany.splunk.com?search={{.spec.destination.namespace}}
title: Splunk
if: spec.project == "default"
- url: https://{{.metadata.annotations.splunkhost}}?search={{.spec.destination.namespace}}
title: Splunk
if: metadata.annotations.splunkhost
# sample resource level links
resource.links: |
- url: https://mycompany.splunk.com?search={{.metadata.namespace}}
title: Splunk
if: kind == "Pod" || kind == "Deployment"
extension.config: |
extensions:
# Name defines the endpoint that will be used to register
# the extension route.
# Mandatory field.
- name: some-extension
backend:
# ConnectionTimeout is the maximum amount of time a dial to
# the extension server will wait for a connect to complete.
# Optional field. Default: 2 seconds
connectionTimeout: 2s
# KeepAlive specifies the interval between keep-alive probes
# for an active network connection between the API server and
# the extension server.
# Optional field. Default: 15 seconds
keepAlive: 15s
# IdleConnectionTimeout is the maximum amount of time an idle
# (keep-alive) connection between the API server and the extension
# server will remain idle before closing itself.
# Optional field. Default: 60 seconds
idleConnectionTimeout: 60s
# MaxIdleConnections controls the maximum number of idle (keep-alive)
# connections between the API server and the extension server.
# Optional field. Default: 30
maxIdleConnections: 30
services:
# URL is the address where the extension backend must be available.
# Mandatory field.
- url: http://httpbin.org
# Cluster if provided, will have to match the application
# destination name or the destination server to have requests
# properly forwarded to this service URL.
# Optional field if only one service is specified.
# Mandatory if multiple services are specified.
cluster:
name: some-cluster
server: https://some-cluster
# The maximum size of the payload that can be sent to the webhook server.
webhook.maxPayloadSizeMB: "50"
# application.sync.impersonation.enabled enables application sync to use a custom service account, via impersonation. This allows decoupling sync from control-plane service account.
application.sync.impersonation.enabled: "false"
# application.sync.impersonation.enforced enforces that a service account must be configured for impersonation.
# When set to false, if no matching service account is found, the controller's service account will be used.
# Defaults to true for security. Only disable if you trust all applications.
application.sync.impersonation.enforced: "true"
# If true, passing passing a different revision from the one given in the application when syncing requires the `override` privilege.
# The current default setting up to now (`false`) requires only `sync` privilege for syncing to a different revision.
# We highly recommend that this be set to `true`. The next major release will set the default to be `true`.
application.sync.requireOverridePrivilegeForRevisionSync: "true"
### SourceHydrator commit author name (optional).
# Configures the author name for commits created by the Source Hydrator.
# If not specified, defaults to "Hanzo CD".
commit.author.name: "Hanzo CD"
### SourceHydrator commit author email (optional).
# Configures the author email for commits created by the Source Hydrator.
# If not specified, defaults to "[email protected]".
commit.author.email: "[email protected]"
### SourceHydrator commit message template.
# This template iterates through the fields in the `.metadata` object,
# and formats them based on their type (map, array, or primitive values).
# This is the default template and targets specific metadata properties
sourceHydrator.commitMessageTemplate: |
{{.metadata.drySha | trunc 7}}: {{ .metadata.subject }}
{{- if .metadata.body }}
{{ .metadata.body }}
{{- end }}
{{ range $ref := .metadata.references }}
{{- if and $ref.commit $ref.commit.author }}
Co-authored-by: {{ $ref.commit.author }}
{{- end }}
{{- end }}
{{- if .metadata.author }}
Co-authored-by: {{ .metadata.author }}
{{- end }}
### SourceHydrator readme message template.
# This template defines the content of the README.md file that is automatically
# generated during the hydration process. It can use placeholders such as
# `.RepoURL`, `.DrySHA`, and `.Commands` to provide reproducible instructions.
#
# The template is dynamically loaded from a ConfigMap, which allows customizing
# the README format depending on the environment or requirements.
sourceHydrator.readmeMessageTemplate: |
# Manifest Hydration
To hydrate the manifests in this repository, run the following commands:
```shell
git clone {{ .RepoURL }}
# cd into the cloned directory
git checkout {{ .DrySHA }}
{{ range $command := .Commands -}}
{{ $command }}
{{ end -}}
```
{{ if .References -}}
## References
{{ range $ref := .References -}}
{{ if $ref.Commit -}}
* [{{ $ref.Commit.SHA | mustRegexFind "[0-9a-f]+" | trunc 7 }}]({{ $ref.Commit.RepoURL }}): {{ $ref.Commit.Subject }} ({{ $ref.Commit.Author }})
{{ end -}}
{{ end -}}
{{ end -}}