contour, cert-manager and external-secrets could not be compared or synced: error calculating structured merge diff: error building typed value from live resource: .status.terminatingReplicas: field not declared in schema The ServerSideApply sync option makes Argo CD compute the diff locally against a Kubernetes schema compiled into its own binary. Argo CD v2.13 is older than this cluster: GKE runs 1.35, and Deployments there carry status.terminatingReplicas, which went beta and on-by-default in 1.33. Argo CD's schema has never heard of the field, so the diff aborts before any sync can happen. Nothing is wrong with the manifests, and only the three apps using SSA are affected. ServerSideDiff asks the API server to compute the diff via a dry-run apply, so the schema in use is the cluster's own. Beta since v2.10 and supported on the running version. The generic chart could not express this — Application metadata had no annotations block at all — so it gains an optional per-entry compareOptions list rather than the annotation being hardcoded. This is a workaround for an out-of-date Argo CD, not a fix. The fix is upgrading to a build whose bundled schema matches the cluster; every value key this repo relies on already exists in chart 10.8.4 (Argo CD v3.5.2), so that upgrade is mostly a vendoring exercise plus the 3.0 breaking changes (logs RBAC now enforced, fine-grained RBAC inheritance, resource tracking moving from labels to annotations). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEsTefWWifp4ikvhHF5s6N
96 lines
4.1 KiB
YAML
96 lines
4.1 KiB
YAML
{{- $top := . -}}
|
|
{{- range $i, $config := .Values.appSpec }}
|
|
---
|
|
{{- $appName := $config.name -}}
|
|
{{- $appNameOverride := $config.nameOverride -}}
|
|
{{- $clusterValues := $top.Values.clusterSpec -}}
|
|
{{- $clusterName := $clusterValues.destination.name -}}
|
|
{{- $clusterServer := $clusterValues.destination.server -}}
|
|
|
|
{{- $teamValues := $top.Values.teamSpec.devops -}}
|
|
{{- $labels := $teamValues.labels -}}
|
|
{{- $bu := $labels.bu -}}
|
|
{{- $cluster := $labels.cluster | replace "dp-" "#pqr#" | replace "backup" "#abc#" | replace "p-" "" | replace "prd-" "" | replace "int-" "" | replace "dev-" "" | replace "-cluster" "" | replace "prod-ops" "infra" | replace "-ase1c" "-c" | replace "-ase1a" "-a" | replace "-ase1" "" | replace "k8s-" "" | replace "gke-" "" | replace "#pqr#" "dp-" | replace "#abc#" "backup" -}}
|
|
{{- $team := $labels.team -}}
|
|
{{- $env := $labels.env -}}
|
|
{{- $source := $teamValues.source -}}
|
|
{{- $repoURL := $source.repoURL -}}
|
|
{{- $targetRevision := $source.targetRevision -}}
|
|
|
|
{{- $namespace := $config.namespace -}}
|
|
{{- $path := printf "%s/%s" $source.path $config.chartDir -}}
|
|
{{- $valueFiles := printf "%s/%s/custom-values.yaml" $source.valueFiles $config.valuesDir -}}
|
|
{{- $additionalValueFiles := $config.additionalValueFiles | default list -}}
|
|
|
|
{{ $argoAppNamespace := $top.Values.argocdSpec.namespace }}
|
|
|
|
{{- $compareOptions := $config.compareOptions | default list -}}
|
|
|
|
apiVersion: argoproj.io/v1alpha1
|
|
kind: Application
|
|
metadata:
|
|
name: '{{- if $appNameOverride }}{{ $appNameOverride }}{{- else }}{{ printf "%s-%s-%s" $appName $cluster $env }}{{- end }}'
|
|
namespace: {{ $argoAppNamespace }}
|
|
{{- if $compareOptions }}
|
|
annotations:
|
|
# Opt-in per appSpec entry, as a list of Argo CD compare options.
|
|
#
|
|
# The one that matters here is ServerSideDiff=true, which is the escape
|
|
# hatch for "field not declared in schema" diff failures. Argo CD
|
|
# normally computes the diff locally against a Kubernetes schema baked
|
|
# into its own binary, so a cluster newer than Argo CD has fields Argo
|
|
# CD has never heard of and the diff aborts. This option asks the API
|
|
# server to compute the diff instead (a dry-run apply), and the API
|
|
# server necessarily knows its own fields.
|
|
argocd.argoproj.io/compare-options: {{ join "," $compareOptions | quote }}
|
|
{{- end }}
|
|
labels:
|
|
{{ toYaml $labels | indent 4 }}
|
|
finalizers:
|
|
- resources-finalizer.argocd.argoproj.io
|
|
spec:
|
|
project: {{ $team }}
|
|
source:
|
|
repoURL: {{ $repoURL }}
|
|
targetRevision: {{ $targetRevision }}
|
|
path: {{ $path }}
|
|
helm:
|
|
valueFiles:
|
|
- {{ $valueFiles }}
|
|
{{- if $additionalValueFiles }}
|
|
{{- range $additionalValueFiles }}
|
|
- {{ . }}
|
|
{{- end }}
|
|
{{- end }}
|
|
destination:
|
|
name: {{ $clusterName }}
|
|
server: {{ $clusterServer }}
|
|
namespace: {{ $namespace }}
|
|
syncPolicy:
|
|
syncOptions:
|
|
- CreateNamespace=true
|
|
{{- if $config.replace }}
|
|
# Opt-in per appSpec entry. Needed when adopting a resource that was
|
|
# created outside Argo (no last-applied-configuration annotation for
|
|
# a normal patch to diff against) and has a field the new manifest
|
|
# changes type on (e.g. an env var moving from `value` to
|
|
# `valueFrom`) — a patch can add the new field without clearing the
|
|
# old one, which the API rejects either way, patch or SSA. Replace
|
|
# does a full PUT of the whole object instead of a merge, so the
|
|
# submitted spec never contains the stale field at all.
|
|
- Replace=true
|
|
{{- end }}
|
|
{{- if $config.serverSideApply }}
|
|
# Opt-in per appSpec entry. Distinct from `replace` above — this is
|
|
# for CRDs whose full manifest (schema included) exceeds the 256KiB
|
|
# limit Kubernetes puts on the last-applied-configuration annotation
|
|
# a normal client-side apply writes (external-secrets' CRDs, e.g.
|
|
# ClusterSecretStore, hit this — they embed large OpenAPI schemas to
|
|
# cover many provider-specific fields). SSA doesn't use that
|
|
# annotation at all, so there's no size ceiling to hit. Non-
|
|
# destructive, unlike Replace — safe as a default reach-for for a
|
|
# "CRD too large" error specifically.
|
|
- ServerSideApply=true
|
|
{{- end }}
|
|
{{- end }}
|