<?xml version="1.0" encoding="utf-8" standalone="yes"?><?xml-stylesheet type="text/xsl" href="https://perrotta.dev/rss.xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Kubernetes on ¬ just serendipity 🍀</title>
    <link>https://perrotta.dev/</link>
    <description>Recent content in Kubernetes on ¬ just serendipity 🍀</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <managingEditor>serendipity@perrotta.dev (Thiago Perrotta)</managingEditor>
    <webMaster>serendipity@perrotta.dev (Thiago Perrotta)</webMaster>
    <copyright>© 2013 - 2026 Thiago Perrotta ·
  a fork of [hugo ʕ•ᴥ•ʔ bear](https://github.com/janraasch/hugo-bearblog/)
</copyright>
    <lastBuildDate>Thu, 16 Jul 2026 12:39:33 +0200</lastBuildDate>
    <atom:link href="https://perrotta.dev/tags/kubernetes/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>when Gen AI cites docs that don&#39;t exist
      </title>
      <link>https://perrotta.dev/2026/07/when-gen-ai-cites-docs-that-dont-exist/</link>
      <pubDate>Thu, 16 Jul 2026 12:34:22 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>ai</category>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2026/07/when-gen-ai-cites-docs-that-dont-exist/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Today in AGI&lt;/strong&gt;: two Claude agents, same annotation, opposite answers.&lt;/p&gt;&#xA;&lt;p&gt;I had a PR touching an ArgoCD &lt;code&gt;PostSync&lt;/code&gt; hook. Ordering hooks within a phase&#xA;needs an annotation, so I asked; Claude Code (Opus 4.8) and Claude in GitHub&#xA;disagreed:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Claude Code: &lt;code&gt;argocd.argoproj.io/hook-weight&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; exist.&lt;/li&gt;&#xA;&lt;li&gt;Claude in GitHub: it &lt;strong&gt;does&lt;/strong&gt; exist.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Both stated it flatly. So I pushed the GitHub agent, and it doubled down —&#xA;citing documentation(!):&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;This claim is incorrect. &lt;code&gt;argocd.argoproj.io/hook-weight&lt;/code&gt; is a documented&#xA;ArgoCD annotation for controlling hook execution order within a phase. From&#xA;the ArgoCD resource hooks documentation:&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Citing non-existent docs is the clanker equivalent of pulling rank.&lt;/p&gt;&#xA;&lt;p&gt;Confident. Sourced. Wrong. The actual annotation for ordering is&#xA;&lt;a href=&#34;https://argo-cd.readthedocs.io/en/stable/user-guide/sync-waves/&#34;&gt;&lt;code&gt;argocd.argoproj.io/sync-wave&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;They are defined by the &lt;code&gt;argocd.argoproj.io/sync-wave&lt;/code&gt; annotation. The value&#xA;is an integer that defines the ordering.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;metadata:&#xA;  annotations:&#xA;    argocd.argoproj.io/sync-wave: &amp;#34;5&amp;#34;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;There is no &lt;code&gt;hook-weight&lt;/code&gt;. The &amp;ldquo;documentation&amp;rdquo; it quoted from doesn&amp;rsquo;t say what&#xA;it says it says.&lt;/p&gt;&#xA;&lt;p&gt;Eventually it folded:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;You are correct, and I apologize for the confident but incorrect claim in&#xA;round 3.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Round 3. It took three rounds and a fabricated citation before the model&#xA;conceded. The failure mode isn&amp;rsquo;t the hallucination — it&amp;rsquo;s the escalation to&#xA;&lt;code&gt;&amp;quot;&amp;quot;&amp;quot;docs&amp;quot;&amp;quot;&amp;quot;&lt;/code&gt; as authority to defend it.&lt;/p&gt;&#xA;&lt;p&gt;Same model family, same question, one right and one wrong. Verify against the&#xA;upstream source, not against the more confident agent.&lt;/p&gt;&#xA;&lt;p&gt;Human-in-the-loop is still very much needed, folks.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;🤖 &lt;em&gt;Drafted with &lt;code&gt;/bloggify&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: when Gen AI cites docs that don&#39;t exist&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/ai/&#34;&gt;#ai&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>kubectl: atomic upsert
      </title>
      <link>https://perrotta.dev/2026/06/kubectl-atomic-upsert/</link>
      <pubDate>Tue, 09 Jun 2026 16:16:12 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2026/06/kubectl-atomic-upsert/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Problem statement&lt;/strong&gt;: A cronjob refreshes an image-pull secret in every&#xA;namespace so nodes can authenticate to a private registry. It does the obvious™&#xA;thing: delete the old secret, then create the new one right away:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;kubectl delete secret --ignore-not-found aws-ecr-token -n &amp;#34;$namespace&amp;#34;&#xA;kubectl create secret docker-registry aws-ecr-token -n &amp;#34;$namespace&amp;#34; \&#xA;    --docker-server=&amp;#34;https://$AWS_ACCOUNT.dkr.ecr.$AWS_REGION.amazonaws.com&amp;#34; \&#xA;    --docker-username=&amp;#34;AWS&amp;#34; \&#xA;    --docker-password=&amp;#34;$(aws ecr get-login-password --region &amp;#34;$AWS_REGION&amp;#34;)&amp;#34;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Every couple of hours, for a fraction of a second, that secret &lt;strong&gt;does not&#xA;exist&lt;/strong&gt; in the cluster (booo!). Most of the time nobody notices. Occasionally a&#xA;deploy step that copies the secret into a tenant namespace runs into exactly&#xA;that gap, its &lt;code&gt;kubectl get&lt;/code&gt; returns non-zero, and the whole deployment fails&#xA;with &lt;code&gt;ImagePullError&lt;/code&gt;. Occasional flakiness, our favorite kind.&lt;/p&gt;&#xA;&lt;p&gt;The idiomatic fix is to &lt;strong&gt;never delete&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The solution is to employ &lt;code&gt;kubectl create&lt;/code&gt; with a client-side dry-run that&#xA;renders the object to YAML without touching the cluster; pipe that into &lt;code&gt;kubectl apply&lt;/code&gt; and we get an upsert:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;kubectl create secret docker-registry aws-ecr-token -n &amp;#34;$namespace&amp;#34; \&#xA;    --docker-server=&amp;#34;https://$AWS_ACCOUNT.dkr.ecr.$AWS_REGION.amazonaws.com&amp;#34; \&#xA;    --docker-username=&amp;#34;AWS&amp;#34; \&#xA;    --docker-password=&amp;#34;$(aws ecr get-login-password --region &amp;#34;$AWS_REGION&amp;#34;)&amp;#34; \&#xA;    --dry-run=client -o yaml | kubectl apply -f -&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The first run creates the secret; every run afterwards updates it in place.&#xA;There is no moment wherein it&amp;rsquo;s absent.&lt;/p&gt;&#xA;&lt;p&gt;The thing I wanted to be sure of before trusting it: does &lt;code&gt;apply&lt;/code&gt; over a secret&#xA;that was originally &lt;code&gt;create&lt;/code&gt;d actually &lt;em&gt;rotate&lt;/em&gt; the password, or does it merge&#xA;in some way that leaves stale data behind? &lt;code&gt;kubectl apply&lt;/code&gt; does a &lt;a href=&#34;https://perrotta.dev/2026/05/argocd-persistent-pdb-selector-diff/&#34;&gt;3-way&#xA;merge&lt;/a&gt; against the&#xA;&lt;code&gt;last-applied-configuration&lt;/code&gt; annotation, and that merge has bitten me before. A&#xA;secret with no prior annotation falls back to a 2-way merge — so this is worth&#xA;checking, just in case.&lt;/p&gt;&#xA;&lt;p&gt;So I checked. Two consecutive upserts on a throwaway namespace, rotating the&#xA;password between them:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl get secret aws-ecr-token -n &amp;#34;$ns&amp;#34; -o jsonpath=&amp;#39;{.metadata.uid}&amp;#39;&#xA;38d4c676-7169-4d1d-9911-9de068bba29a   # run 1&#xA;# [...]&#xA;38d4c676-7169-4d1d-9911-9de068bba29a   # run 2 — same uid: never deleted&#xA;&#xA;% kubectl get secret aws-ecr-token -n &amp;#34;$ns&amp;#34; \&#xA;    -o jsonpath=&amp;#39;{.data.\.dockerconfigjson}&amp;#39; | base64 -d&#xA;{&amp;#34;auths&amp;#34;:{&amp;#34;...&amp;#34;:{&amp;#34;username&amp;#34;:&amp;#34;AWS&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;PASSWORD_V2_ROTATED&amp;#34;,...}}}&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The &lt;code&gt;uid&lt;/code&gt; is stable across runs — proof the object was never deleted and&#xA;recreated, only updated. &lt;code&gt;resourceVersion&lt;/code&gt; bumped, the password rotated to the&#xA;new value, and no trace of the old one lingered. The 2-way-merge worry didn&amp;rsquo;t&#xA;materialize: for a flat field like &lt;code&gt;.data.\.dockerconfigjson&lt;/code&gt;, apply replaces&#xA;the value outright.&lt;/p&gt;&#xA;&lt;p&gt;A &lt;code&gt;uid&lt;/code&gt; that survives a refresh is the whole proof. Delete-then-create mints a&#xA;new &lt;code&gt;uid&lt;/code&gt; every cycle; an in-place update keeps it. If we ever want to know&#xA;whether something &lt;em&gt;replaced&lt;/em&gt; an object or &lt;em&gt;mutated&lt;/em&gt; it, the &lt;code&gt;uid&lt;/code&gt; is the way to&#xA;go.&lt;/p&gt;&#xA;&lt;p&gt;Two more things worth highlighting:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;It&amp;rsquo;s atomic from a &lt;em&gt;consumer&amp;rsquo;s&lt;/em&gt; point of view — the secret is never missing.&#xA;It is not a transaction. &lt;code&gt;apply&lt;/code&gt; is still a single API write; we&amp;rsquo;re trading a&#xA;delete-then-create pair (two writes, with a gap) for one write (no gap).&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;--dry-run=client&lt;/code&gt; is the spelling since k8s 1.18. Older clusters used a bare&#xA;&lt;code&gt;--dry-run&lt;/code&gt; or &lt;code&gt;--dry-run=true&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The same pattern works for configmaps, or anything else &lt;code&gt;kubectl create&lt;/code&gt; knows&#xA;how to build. &lt;code&gt;create ... --dry-run=client -o yaml | apply -f -&lt;/code&gt; is the way to&#xA;do whenever we&amp;rsquo;d otherwise reach for delete-then-create.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: kubectl: atomic upsert&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>ArgoCD: persistent PDB selector diff
      </title>
      <link>https://perrotta.dev/2026/05/argocd-persistent-pdb-selector-diff/</link>
      <pubDate>Fri, 22 May 2026 16:51:12 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2026/05/argocd-persistent-pdb-selector-diff/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://perrotta.dev/2026/05/argocd-stale-vault-secret-after-population/&#34;&gt;Previously&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Problem statement&lt;/strong&gt;: An ArgoCD application shows a persistent diff on a&#xA;&lt;code&gt;PodDisruptionBudget&lt;/code&gt; — &lt;code&gt;spec.selector.matchExpressions&lt;/code&gt; is in the rendered&#xA;manifest but missing from the live object. Sync doesn&amp;rsquo;t clear it. Hard refresh&#xA;doesn&amp;rsquo;t clear it. Syncing with replace or with server-side apply or with force&#xA;doesn&amp;rsquo;t clear it. Ugh!&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;policy/PodDisruptionBudget/default/my-super-cool-app&lt;/code&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;selector:&#xA;  matchExpressions:&#xA;    - key: job-name&#xA;      operator: DoesNotExist&#xA;  matchLabels:&#xA;    release: my-super-cool-app&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The chart was updated to add &lt;code&gt;matchExpressions&lt;/code&gt; to the PDB selector. The diff&#xA;shows up. Sync runs. Diff stays.&lt;/p&gt;&#xA;&lt;p&gt;First idea: &lt;em&gt;Kubernetes mutates PDB selectors server-side&lt;/em&gt; — no.&#xA;The PDB registry strategy in&#xA;&lt;a href=&#34;https://github.com/kubernetes/kubernetes/blob/master/pkg/registry/policy/poddisruptionbudget/strategy.go&#34;&gt;&lt;code&gt;pkg/registry/policy/poddisruptionbudget/strategy.go&lt;/code&gt;&lt;/a&gt;&#xA;only touches &lt;code&gt;Status&lt;/code&gt; and &lt;code&gt;Generation&lt;/code&gt;. No selector mutation, no admission&#xA;plugin in tree that injects this. Patterns like&#xA;&lt;code&gt;matchExpressions: [{key: job-name, operator: DoesNotExist}]&lt;/code&gt; come from chart&#xA;authors, not from K8s. This is all according to the LLM.&lt;/p&gt;&#xA;&lt;p&gt;Second idea: &lt;em&gt;PDB selectors are immutable on older K8s&lt;/em&gt; — is also wrong&#xA;here. Selector mutability landed in 1.27. The cluster is on 1.33+.&lt;/p&gt;&#xA;&lt;p&gt;The actual culprit is &lt;strong&gt;client-side &lt;code&gt;kubectl apply&lt;/code&gt; 3-way merge&lt;/strong&gt;, which is&#xA;what ArgoCD uses by default. The merge needs three inputs:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;The previous applied state, read from&#xA;&lt;code&gt;metadata.annotations.kubectl.kubernetes.io/last-applied-configuration&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;The live object.&lt;/li&gt;&#xA;&lt;li&gt;The new desired manifest.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Check the annotation on the live PDB:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl get pdb my-super-cool-app -n default -o yaml | yq &amp;#39;.metadata.annotations.&amp;#34;kubectl.kubernetes.io/last-applied-configuration&amp;#34;&amp;#39;&#xA;{&amp;#34;apiVersion&amp;#34;:&amp;#34;policy/v1&amp;#34;,&amp;#34;kind&amp;#34;:&amp;#34;PodDisruptionBudget&amp;#34;,&amp;#34;metadata&amp;#34;:{...},&amp;#34;spec&amp;#34;:{&amp;#34;maxUnavailable&amp;#34;:1,&amp;#34;selector&amp;#34;:{&amp;#34;matchLabels&amp;#34;:{&amp;#34;release&amp;#34;:&amp;#34;my-super-cool-app&amp;#34;}}}}&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;No &lt;code&gt;matchExpressions&lt;/code&gt;. So the 3-way merge sees: &lt;em&gt;the previous apply didn&amp;rsquo;t have&#xA;the field, the live object doesn&amp;rsquo;t have the field, the new manifest has the&#xA;field&lt;/em&gt;. It treats this as an addition — but the existing implementation in this&#xA;path doesn&amp;rsquo;t reliably propagate the new key into the live selector when the&#xA;parent (&lt;code&gt;spec.selector&lt;/code&gt;) is owned as an opaque value. The &lt;code&gt;managedFields&lt;/code&gt; confirm&#xA;it:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- manager: argocd-controller&#xA;  operation: Update&#xA;  fieldsV1:&#xA;    f:spec:&#xA;      f:maxUnavailable: {}&#xA;      f:selector: {}   # opaque — no sub-field ownership&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;code&gt;f:selector: {}&lt;/code&gt; means argocd-controller owns the whole selector blob with no&#xA;sub-field tracking. New fields under &lt;code&gt;spec.selector&lt;/code&gt; get lost in the merge.&lt;/p&gt;&#xA;&lt;p&gt;The fix is to delete the live PDB and let ArgoCD recreate it on the next sync:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl delete pdb my-super-cool-app -n default&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This can alternatively be done via the ArgoCD Web UI.&lt;/p&gt;&#xA;&lt;p&gt;It is a safe operation: a PDB only gates voluntary disruptions (drains,&#xA;evictions). A brief gap doesn&amp;rsquo;t affect running pods. The recreated object gets a&#xA;fresh &lt;code&gt;last-applied-configuration&lt;/code&gt; containing &lt;code&gt;matchExpressions&lt;/code&gt;, and the diff&#xA;clears.&lt;/p&gt;&#xA;&lt;p&gt;The longer-term fix is to switch the application to server-side apply, which&#xA;tracks ownership at the field level and handles this correctly:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;syncPolicy:&#xA;  syncOptions:&#xA;    - ServerSideApply=true&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;It&amp;rsquo;s always a dilemma whether or not to use server-side apply.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: ArgoCD: persistent PDB selector diff&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>ArgoCD: stale Vault secret after population
      </title>
      <link>https://perrotta.dev/2026/05/argocd-stale-vault-secret-after-population/</link>
      <pubDate>Wed, 13 May 2026 12:34:36 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2026/05/argocd-stale-vault-secret-after-population/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Problem statement&lt;/strong&gt;: An ArgoCD application references a Vault secret via&#xA;&lt;code&gt;helm-secrets&lt;/code&gt; / &lt;code&gt;vals&lt;/code&gt; (e.g. &lt;code&gt;vault://kv-v2/services/foo#/apiKey&lt;/code&gt;). The&#xA;application is created &lt;em&gt;before&lt;/em&gt; the secret exists in Vault, or while the&#xA;corresponding key is blank. The key is populated later on. The application keeps&#xA;behaving as if the value is still missing — usually a &lt;code&gt;404&lt;/code&gt; from Vault, or a&#xA;&lt;code&gt;permission denied&lt;/code&gt; on the cached lookup:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;Failed to load target state: failed to generate manifest for source 1 of 2:&#xA;rpc error: code = Unknown desc = Manifest generation error (cached): failed to&#xA;execute helm template command: [...] vals error: expand&#xA;vault://kv-v2/services/foo#/apiKey: Error making API request. URL: GET&#xA;http://vault.infra-services:8200/v1/sys/internal/ui/mounts/kv-v2/services/foo&#xA;Code: 404.&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Note the &lt;code&gt;(cached)&lt;/code&gt; part of the error. The &lt;code&gt;argocd-repo-server&lt;/code&gt; caches the&#xA;result of manifest generation, &lt;em&gt;including&lt;/em&gt; failed &lt;code&gt;vals&lt;/code&gt; / &lt;code&gt;helm-secrets&lt;/code&gt;&#xA;lookups. A normal sync or refresh happily reuses the cached (stale) failure and&#xA;never re-resolves the Vault reference. So the freshly-populated secret goes&#xA;unnoticed. A blank to non-blank update counts as well, besides outright&#xA;token rotations.&lt;/p&gt;&#xA;&lt;p&gt;The fix is to perform a &lt;strong&gt;Hard Refresh&lt;/strong&gt; on the application — that&amp;rsquo;s what busts&#xA;the manifest cache and forces a fresh Vault lookup:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;argocd app get &amp;lt;app&amp;gt; --hard-refresh&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Or, in the web UI: open the application → &lt;code&gt;REFRESH ▾&lt;/code&gt; dropdown → &lt;code&gt;HARD REFRESH&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;If a hard refresh alone doesn&amp;rsquo;t pick up the new value, restart the argocd repo&#xA;server:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;kubectl rollout restart deployment -n argocd argocd-repo-server&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;A regular refresh won&amp;rsquo;t do. The cache key for the failed lookup outlives the&#xA;refresh.&lt;/p&gt;&#xA;&lt;p&gt;By default, the application cache lasts for &lt;strong&gt;24h&lt;/strong&gt; (though it can be changed&#xA;via helm values); hence, another way to resolve the issue is to simply sit out&#xA;on it and wait.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: ArgoCD: stale Vault secret after population&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>ArgoCD: sync stuck in terminating
      </title>
      <link>https://perrotta.dev/2026/05/argocd-sync-stuck-in-terminating/</link>
      <pubDate>Tue, 05 May 2026 13:51:10 -0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2026/05/argocd-sync-stuck-in-terminating/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Problem statement&lt;/strong&gt;: An ArgoCD application&amp;rsquo;s sync operation is stuck in&#xA;&lt;code&gt;Terminating&lt;/code&gt; for hours. The web UI shows &lt;em&gt;operation is terminating due to&#xA;timeout&lt;/em&gt;. A PreSync hook resource keeps appearing as &lt;code&gt;Running&lt;/code&gt; even though&#xA;the underlying &lt;code&gt;Job&lt;/code&gt; already completed in the cluster.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl get application &amp;lt;app&amp;gt; -n argocd -o yaml | yq &amp;#39;.status.operationState.phase&amp;#39;&#xA;Terminating&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The application controller is stuck in a busy loop:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;&amp;#34;Resuming in-progress operation. phase: Terminating, message: operation is terminating due to timeout&amp;#34;&#xA;&amp;#34;No operation updates necessary to &amp;#39;&amp;lt;ns&amp;gt;/&amp;lt;app&amp;gt;&amp;#39;. Skipping patch&amp;#34;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;In my case this followed a transient &lt;code&gt;ImagePullBackOff&lt;/code&gt; on a PreSync hook job:&#xA;the image was being mirrored to a regional ECR, the first hook attempt failed,&#xA;and by the time the image landed and the retry succeeded, the parent operation&#xA;had already moved into &lt;code&gt;Terminating&lt;/code&gt; and never recovered.&lt;/p&gt;&#xA;&lt;p&gt;The instinct is to clear the operation:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;kubectl patch application &amp;lt;app&amp;gt; -n argocd --type merge -p &amp;#39;{&amp;#34;operation&amp;#34;: null}&amp;#39;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;It&amp;rsquo;s a no-op. The wedge doesn&amp;rsquo;t live in &lt;code&gt;spec.operation&lt;/code&gt; (the request) — it&#xA;lives in &lt;code&gt;status.operationState&lt;/code&gt; (the controller&amp;rsquo;s bookkeeping). And &lt;code&gt;status&lt;/code&gt;&#xA;is owned by the controller, so a regular &lt;code&gt;--type merge&lt;/code&gt; patch won&amp;rsquo;t touch it.&lt;/p&gt;&#xA;&lt;p&gt;We need the &lt;code&gt;status&lt;/code&gt; subresource:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;kubectl patch application &amp;lt;app&amp;gt; -n argocd --type merge \&#xA;  -p &amp;#39;{&amp;#34;operation&amp;#34;: null}&amp;#39;&#xA;kubectl patch application &amp;lt;app&amp;gt; -n argocd --subresource status --type merge \&#xA;  -p &amp;#39;{&amp;#34;status&amp;#34;:{&amp;#34;operationState&amp;#34;:null}}&amp;#39;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Both patches together. Neither touches cluster resources — they only modify&#xA;ArgoCD&amp;rsquo;s view of the operation. After that, re-trigger the sync from the UI&#xA;and the application reconciles cleanly.&lt;/p&gt;&#xA;&lt;p&gt;Restarting &lt;code&gt;argocd-application-controller&lt;/code&gt; does &lt;em&gt;not&lt;/em&gt; help: the stuck state&#xA;sits in etcd, and the freshly-elected leader picks it up on startup and resumes&#xA;the same loop.&lt;/p&gt;&#xA;&lt;p&gt;One caveat: verify the underlying hook resource has actually finished before&#xA;clearing. &lt;code&gt;kubectl describe job -n argocd &amp;lt;hook-job&amp;gt;&lt;/code&gt; should show&#xA;&lt;code&gt;1 Succeeded&lt;/code&gt;. Otherwise we&amp;rsquo;ll lose in-flight progress.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: ArgoCD: sync stuck in terminating&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>external-secrets: bust the ClusterSecretStore cache
      </title>
      <link>https://perrotta.dev/2026/04/external-secrets-bust-the-clustersecretstore-cache/</link>
      <pubDate>Fri, 24 Apr 2026 01:22:06 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2026/04/external-secrets-bust-the-clustersecretstore-cache/</guid>
      <description>&lt;p&gt;♠ This post is hyper-specific but I am quite sure I&amp;rsquo;ll forget how to do it the&#xA;next time it happens again, so I&amp;rsquo;m better off documenting it.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://external-secrets.io/&#34;&gt;External Secrets&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;External Secrets Operator is a Kubernetes operator that integrates external&#xA;secret management systems like AWS Secrets Manager, HashiCorp Vault, Google&#xA;Secrets Manager, Azure Key Vault, IBM Cloud Secrets Manager, CyberArk Secrets&#xA;Manager, Pulumi ESC and many more. The operator reads information from&#xA;external APIs and automatically injects the values into a Kubernetes Secret.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Problem statement&lt;/strong&gt;: a &lt;code&gt;ClusterSecretStore&lt;/code&gt; is stuck on a past&#xA;authentication failure long after the Vault-side config was fixed, and every&#xA;&lt;code&gt;ExternalSecret&lt;/code&gt; that references it reports &lt;code&gt;SecretSyncedError: the desired SecretStore is not ready&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Verify the store really is healthy now:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl describe clustersecretstore external-secrets-vault-backend&#xA;...&#xA;Status:&#xA;  Conditions:&#xA;    Last Transition Time:  2026-04-21T23:16:45Z   # &amp;lt;- stale&#xA;    Message:               unable to create client&#xA;    Reason:                InvalidProviderConfig&#xA;    Status:                False&#xA;    Type:                  Ready&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;code&gt;Last Transition Time&lt;/code&gt; is from before I fixed the auth role. The operator&#xA;hasn&amp;rsquo;t re-evaluated since. Poke it with an annotation to force a reconcile:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl annotate clustersecretstore external-secrets-vault-backend \&#xA;    force-reconcile=$(date &amp;#43;%s) --overwrite&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Now it validates:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl describe clustersecretstore external-secrets-vault-backend | grep -A3 Status:&#xA;Status:&#xA;  Capabilities:  ReadWrite&#xA;  Conditions:&#xA;    Last Transition Time:  2026-04-23T23:11:30Z&#xA;    Message:               store validated&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;But the &lt;code&gt;ExternalSecret&lt;/code&gt;s are &lt;em&gt;still&lt;/em&gt; stuck — the operator caches store&#xA;readiness per-ES, not just per-CSS. Same trick:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl annotate externalsecret -n argocd --all \&#xA;    force-reconcile=$(date &amp;#43;%s) --overwrite&#xA;externalsecret.external-secrets.io/argocd-external-secret annotated&#xA;externalsecret.external-secrets.io/argocd-external-secret-github-webhook annotated&#xA;...&#xA;&#xA;% kubectl get externalsecret -n argocd&#xA;NAME                       STORE                            STATUS         READY&#xA;argocd-external-secret     external-secrets-vault-backend   SecretSynced   True&#xA;...&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;No controller restart is necessary.&lt;/p&gt;&#xA;&lt;p&gt;The annotation value doesn&amp;rsquo;t matter — any change to the object metadata&#xA;re-queues it.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: external-secrets: bust the ClusterSecretStore cache&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Kubernetes: pod memory usage
      </title>
      <link>https://perrotta.dev/2025/09/kubernetes-pod-memory-usage/</link>
      <pubDate>Fri, 26 Sep 2025 11:54:57 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/09/kubernetes-pod-memory-usage/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Problem statement&lt;/strong&gt;: Given a pod in Kubernetes, check its memory (RAM) usage.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://stackoverflow.com/questions/54531646/checking-kubernetes-pod-cpu-and-memory-utilization&#34;&gt;Stack&#xA;Overflow&lt;/a&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;kubectl top pod -n {namespace} {pod name} [--containers]&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;--containers=false:&#xA;    If present, print usage of containers within a pod.&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This only works if &lt;a href=&#34;https://github.com/kubernetes-sigs/metrics-server&#34;&gt;metrics&#xA;server&lt;/a&gt; is installed (it&#xA;should be!).&lt;/p&gt;&#xA;&lt;p&gt;There&amp;rsquo;s an alternative:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;kubectl describe PodMetrics {pod name}&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;It must be &lt;code&gt;PodMetrics&lt;/code&gt;. &lt;code&gt;PodMetric&lt;/code&gt; does not work (ugh!&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;https://perrotta.dev/2025/09/kubernetes-pod-memory-usage/#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;):&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl get PodMetric&#xA;error: the server doesn&amp;#39;t have a resource type &amp;#34;PodMetric&amp;#34;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;If metrics server is not available for some reason, then one needs to dive into&#xA;the container:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;kubectl exec -it {pod name} -n {namespace} -- /bin/sh&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&amp;hellip;which won&amp;rsquo;t always work; not every container has &lt;code&gt;/bin/sh&lt;/code&gt;. Then read the&#xA;contents of &lt;code&gt;/sys/fs/cgroup/memory/memory.usage_in_bytes&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;Who designed this? Is it so hard to achieve consistency? Both &lt;code&gt;kubectl get pod&lt;/code&gt; and &lt;code&gt;kubectl get pods&lt;/code&gt; work. Why can&amp;rsquo;t it be the same with &lt;code&gt;PodMetric&lt;/code&gt;?&#xA;And, no, &lt;code&gt;PodMetricses&lt;/code&gt; does not work either.&amp;#160;&lt;a href=&#34;https://perrotta.dev/2025/09/kubernetes-pod-memory-usage/#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Kubernetes: pod memory usage&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>kubectl: verbose logging
      </title>
      <link>https://perrotta.dev/2025/09/kubectl-verbose-logging/</link>
      <pubDate>Fri, 05 Sep 2025 18:10:33 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/09/kubectl-verbose-logging/</guid>
      <description>&lt;p&gt;♠ When running &lt;code&gt;kubectl&lt;/code&gt; commands, to get a glimpse of what is happening under the&#xA;hood, pass &lt;code&gt;-v={N}&lt;/code&gt; to it.&lt;/p&gt;&#xA;&lt;p&gt;For example:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl get pod -n argocd -v=6&#xA;I0905 16:10:19.893576 3929356 loader.go:395] Config loaded from file:  /home/ubuntu/.kube/config&#xA;I0905 16:10:19.919935 3929356 round_trippers.go:553] GET https://{REDACTED}.us-east-1.elb.amazonaws.com/api/v1/namespaces/argocd/pods?limit=500 200 OK in 17 milliseconds&#xA;NAME                                                  READY   STATUS    RESTARTS      AGE&#xA;argo-workflows-server-6ffcfbb779-kxdzk                1/1     Running   0             4d18h&#xA;argo-workflows-workflow-controller-85c667cf96-rm9dw   1/1     Running   0             2d4h&#xA;argocd-application-controller-0                       1/1     Running   0             48m&#xA;[...]&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;code&gt;-v=10&lt;/code&gt; is super verbose. Fine-tune as needed.&lt;/p&gt;&#xA;&lt;p&gt;Another example:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl get deploy -n argocd -w -v6&#xA;I0905 16:12:49.277471 3929550 loader.go:395] Config loaded from file:  /home/ubuntu/.kube/config&#xA;I0905 16:12:49.302783 3929550 round_trippers.go:553] GET https://{REDACTED}.us-east-1.elb.amazonaws.com/apis/apps/v1/namespaces/argocd/deployments?limit=500 200 OK in 17 milliseconds&#xA;NAME                                 READY   UP-TO-DATE   AVAILABLE   AGE&#xA;argo-workflows-server                1/1     1            1           91d&#xA;argo-workflows-workflow-controller   1/1     1            1           44d&#xA;argocd-applicationset-controller     1/1     1            1           23d&#xA;argocd-dex-server                    1/1     1            1           451d&#xA;argocd-notifications-controller      1/1     1            1           449d&#xA;argocd-redis                         1/1     1            1           451d&#xA;argocd-repo-server                   1/1     1            1           451d&#xA;argocd-server                        3/3     3            3           428d&#xA;I0905 16:12:49.316961 3929550 round_trippers.go:553] GET https://{REDACTED}.us-east-1.elb.amazonaws.com/apis/apps/v1/namespaces/argocd/deployments?resourceVersion=695374153&amp;amp;watch=true 200 OK in 2 milliseconds&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: kubectl: verbose logging&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Uninstall Kyverno
      </title>
      <link>https://perrotta.dev/2025/09/uninstall-kyverno/</link>
      <pubDate>Fri, 05 Sep 2025 17:03:04 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/09/uninstall-kyverno/</guid>
      <description>&lt;p&gt;♠ It&amp;rsquo;s always a bit frustrating to do it.&lt;/p&gt;&#xA;&lt;p&gt;Despite the excellent &lt;a href=&#34;https://kyverno.io/docs/installation/uninstallation/&#34;&gt;upstream&#xA;documentation&lt;/a&gt;, our&#xA;Kubernetes clusters often leave cruft behind when doing so.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl rollout restart sts -n argocd argocd-redis-ha-server&#xA;E0905 12:29:24.284879 3908207 memcache.go:287] couldn&amp;#39;t get resource list for wgpolicyk8s.io/v1alpha2: the server is currently unable to handle the request&#xA;E0905 12:29:24.287856 3908207 memcache.go:287] couldn&amp;#39;t get resource list for reports.kyverno.io/v1: the server is currently unable to handle the request&#xA;E0905 12:29:24.290250 3908207 memcache.go:121] couldn&amp;#39;t get resource list for reports.kyverno.io/v1: the server is currently unable to handle the request&#xA;E0905 12:29:24.292666 3908207 memcache.go:121] couldn&amp;#39;t get resource list for wgpolicyk8s.io/v1alpha2: the server is currently unable to handle the request&#xA;E0905 12:29:24.295959 3908207 memcache.go:121] couldn&amp;#39;t get resource list for reports.kyverno.io/v1: the server is currently unable to handle the request&#xA;E0905 12:29:24.298226 3908207 memcache.go:121] couldn&amp;#39;t get resource list for wgpolicyk8s.io/v1alpha2: the server is currently unable to handle the request&#xA;E0905 12:29:24.302703 3908207 memcache.go:121] couldn&amp;#39;t get resource list for reports.kyverno.io/v1: the server is currently unable to handle the request&#xA;E0905 12:29:24.305009 3908207 memcache.go:121] couldn&amp;#39;t get resource list for wgpolicyk8s.io/v1alpha2: the server is currently unable to handle the request&#xA;[...]&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;That output junk is emitted on every &lt;code&gt;kubectl&lt;/code&gt; command.&lt;/p&gt;&#xA;&lt;p&gt;This time the culprit was:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl get apiservice | egrep -i &amp;#34;(kyverno|wgpolicy)&amp;#34;&#xA;v1.reports.kyverno.io                           reports-server/kyverno-reports-server   False (MissingEndpoints)   76d&#xA;v1alpha2.wgpolicyk8s.io                         reports-server/kyverno-reports-server   False (MissingEndpoints)   76d&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Deleting these two entries resolves the issue:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl delete apiservice v1.reports.kyverno.io v1alpha2.wgpolicyk8s.io -n reports-server&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Uninstall Kyverno&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>helm: diff manifests
      </title>
      <link>https://perrotta.dev/2025/08/helm-diff-manifests/</link>
      <pubDate>Thu, 28 Aug 2025 17:57:32 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/08/helm-diff-manifests/</guid>
      <description>&lt;p&gt;♠ Lately I&amp;rsquo;ve been employing (with great success) the following pattern in order&#xA;to test whether changes to helm charts are harmless (and expected):&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;cd path/to/my/helm/chart&#xA;git checkout origin/master  # or main&#xA;helm template -f values.yaml . &amp;gt; golden&#xA;git switch --create thiagowfx/my-feature-branch&#xA;# develop, develop, develop&#xA;helm template -f values.yaml . &amp;gt; candidate&#xA;diff candidate golden  # or icdiff&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The &lt;code&gt;diff&lt;/code&gt; output will reliably demonstrate whether your changes are WAI.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: If needed, pass &lt;code&gt;--release-name {foo}&lt;/code&gt; to &lt;code&gt;helm template&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;This pattern is comparable to running a golden &lt;code&gt;helm install&lt;/code&gt; followed by a&#xA;&lt;a href=&#34;https://github.com/databus23/helm-diff&#34;&gt;&lt;code&gt;helm-diff&lt;/code&gt;&lt;/a&gt; (&lt;code&gt;helm diff upgrade&lt;/code&gt;)&#xA;candidate in the cluster.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: helm: diff manifests&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>ArgoCD: git index.lock issue
      </title>
      <link>https://perrotta.dev/2025/08/argocd-git-index.lock-issue/</link>
      <pubDate>Sat, 23 Aug 2025 12:39:14 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>git</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/08/argocd-git-index.lock-issue/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Problem statement&lt;/strong&gt;: Every ArgoCD application is stuck in &lt;em&gt;Unknown&lt;/em&gt; state,&#xA;with the following error:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;Failed to load target state: failed to generate manifest for source 1 of 2:&#xA;rpc error: code = Unknown desc = failed to initialize repository resources: rpc&#xA;error: code = Internal desc = Failed to checkout FETCH_HEAD: failed to checkout&#xA;FETCH_HEAD: `git checkout --force FETCH_HEAD` failed exit status 128: fatal:&#xA;Unable to create &amp;#39;&amp;lt;path to cached source&amp;gt;/.git/index.lock&amp;#39;: File exists. Another&#xA;git process seems to be running in this repository, e.g. an editor opened by&#xA;&amp;#39;git commit&amp;#39;. Please make sure all processes are terminated then try again. If&#xA;it still fails, a git process may have crashed in this repository earlier:&#xA;remove the file manually to continue.&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;It is a lock contention race condition issue within the ArgoCD repo server&#xA;component, with the git &lt;code&gt;index.lock&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The easiest way to address it is to restart the repo server:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;kubectl rollout restart deploy -n argocd argocd-repo-server&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Afterwards, refresh all applications in the cluster via the web UI:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;click Applications on the left-side nav bar&lt;/li&gt;&#xA;&lt;li&gt;click Refresh apps at the top&lt;/li&gt;&#xA;&lt;li&gt;select ALL applications, set refresh type to NORMAL&lt;/li&gt;&#xA;&lt;li&gt;click refresh&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: ArgoCD: git index.lock issue&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/git/&#34;&gt;#git&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>★ Kubernetes: list all ImagePullBackOff container images
      </title>
      <link>https://perrotta.dev/2025/08/kubernetes-list-all-imagepullbackoff-container-images/</link>
      <pubDate>Thu, 21 Aug 2025 13:34:27 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>bestof</category>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/08/kubernetes-list-all-imagepullbackoff-container-images/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Problem statement&lt;/strong&gt; Upgrade the&#xA;&lt;a href=&#34;https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack&#34;&gt;&lt;code&gt;kube-prometheus-stack&lt;/code&gt;&lt;/a&gt;&#xA;helm chart in &lt;a href=&#34;https://portal.azure.cn/&#34;&gt;Azure China&lt;/a&gt; to the latest version.&#xA;&lt;em&gt;Side effect&lt;/em&gt;: Many container images are now missing from our internal&#xA;&lt;a href=&#34;https://azure.microsoft.com/en-us/products/container-registry&#34;&gt;ACR&lt;/a&gt; (Azure&#xA;Container Registry). They need to be synced with &lt;a href=&#34;https://github.com/containers/skopeo&#34;&gt;&lt;code&gt;skopeo&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Which images need to be synced? Use the command below, it is quite readable&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;https://perrotta.dev/2025/08/kubernetes-list-all-imagepullbackoff-container-images/#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;kubectl get pods --all-namespaces -o json | jq -r &amp;#39;.items[] | select(any(.status.containerStatuses[]; .state.waiting.reason == &amp;#34;ImagePullBackOff&amp;#34;)) | .metadata.namespace &amp;#43; &amp;#34;/&amp;#34; &amp;#43; .metadata.name &amp;#43; &amp;#34;\t&amp;#34; &amp;#43; ( [.status.containerStatuses[] | select(.state.waiting.reason == &amp;#34;ImagePullBackOff&amp;#34;).image] | join(&amp;#34;, &amp;#34;) )&amp;#39;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Sample run:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;ubuntu@universe:~ $ kubectl get pods --all-namespaces -o json | jq -r &amp;#39;.items[]&#xA;      | select(any(.status.containerStatuses[]; .state.waiting.reason == &amp;#34;ImagePullBackOff&amp;#34;))&#xA;      | .metadata.namespace &amp;#43; &amp;#34;/&amp;#34; &amp;#43; .metadata.name &amp;#43; &amp;#34;\t&amp;#34; &amp;#43;&#xA;        ( [.status.containerStatuses[] | select(.state.waiting.reason == &amp;#34;ImagePullBackOff&amp;#34;).image] | join(&amp;#34;, &amp;#34;) )&amp;#39;&#xA;monitoring/clustermon-kube-prometheus-operator-67c8b6c87c-w72km {my-registry-name}.azurecr.cn/quay.io/prometheus-operator/prometheus-operator:v0.83.0&#xA;monitoring/clustermon-prometheus-node-exporter-v7bpt    {my-registry-name}.azurecr.cn/quay.io/prometheus/node-exporter:v1.9.1&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;How to &lt;code&gt;skopeo&lt;/code&gt;? First, log in:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;echo -n &amp;#34;{password}&amp;#34; | skopeo login {my-registry-name}.azurecr.cn --password-stdin --username {my-username}&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;How do you get these credentials? Log in to the Azure China Portal, find the ACR&#xA;resource, navigate to Settings &amp;gt; Access keys.&lt;/p&gt;&#xA;&lt;p&gt;Then sync each image&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;https://perrotta.dev/2025/08/kubernetes-list-all-imagepullbackoff-container-images/#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% skopeo sync \&#xA;  --override-os linux --override-arch amd64 \&#xA;  --src docker --dest docker \&#xA;  quay.io/prometheus/node-exporter:v1.9.1 \&#xA;  {my-registry-name}.azurecr.cn/quay.io/prometheus&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;I cannot promise I am able to craft this by hand without looking up man&#xA;pages or getting assistance from an LLM. &lt;em&gt;C&amp;rsquo;est la vie&lt;/em&gt;.&amp;#160;&lt;a href=&#34;https://perrotta.dev/2025/08/kubernetes-list-all-imagepullbackoff-container-images/#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:2&#34;&gt;&#xA;&lt;p&gt;If there are many images, consider using an&#xA;&lt;a href=&#34;https://www.gnu.org/software/bash/manual/html_node/Arrays.html&#34;&gt;array&lt;/a&gt; +&#xA;&lt;code&gt;for&lt;/code&gt; loop.&amp;#160;&lt;a href=&#34;https://perrotta.dev/2025/08/kubernetes-list-all-imagepullbackoff-container-images/#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Kubernetes: list all ImagePullBackOff container images&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/bestof/&#34;&gt;#bestof&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>ArgoCD: app version notifications
      </title>
      <link>https://perrotta.dev/2025/08/argocd-app-version-notifications/</link>
      <pubDate>Tue, 12 Aug 2025 12:07:20 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/08/argocd-app-version-notifications/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Problem statement&lt;/strong&gt;: when using &lt;a href=&#34;https://argo-cd.readthedocs.io/en/stable/operator-manual/notifications/&#34;&gt;ArgoCD&#xA;notifications&lt;/a&gt;,&#xA;starting off from their&#xA;&lt;a href=&#34;https://argo-cd.readthedocs.io/en/stable/operator-manual/notifications/catalog/&#34;&gt;catalog&lt;/a&gt;,&#xA;print the app chart version in addition to existing information.&lt;/p&gt;&#xA;&lt;p&gt;More specifically, currently, a Slack notification message from Argo looks like&#xA;the following:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;Cluster 37: Application heart-of-gold is unhealthy.&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;We want to improve it to:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;Cluster 37: Application heart-of-gold (~1.18.3) is unhealthy.&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The following diff (under a &lt;code&gt;template&lt;/code&gt; block) accomplishes that:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-diff&#34;&gt;-          {{if eq .serviceType &amp;#34;slack&amp;#34;}}:interrobang:{{end}} Cluster &amp;lt;{{.context.argocdUrl}}|*{{.context.clusterId}}*&amp;gt;: Application &amp;lt;{{.context.argocdUrl}}/applications/{{.app.metadata.name}}|`{{.app.metadata.name}}`&amp;gt; _sync_ by {{.app.status.operationState.operation.initiatedBy.username | default &amp;#34;_automation_&amp;#34; }} is _unknown_.&#xA;&amp;#43;          {{if eq .serviceType &amp;#34;slack&amp;#34;}}:interrobang:{{end}} Cluster &amp;lt;{{.context.argocdUrl}}|*{{.context.clusterId}}*&amp;gt;: Application &amp;lt;{{.context.argocdUrl}}/applications/{{.app.metadata.name}}|`{{.app.metadata.name}}`&amp;gt;{{if .app.spec.source.targetRevision}} ({{.app.spec.source.targetRevision}}){{else}}{{range .app.spec.sources}}{{if .targetRevision}} ({{.targetRevision}}){{break}}{{end}}{{end}}{{end}} _sync_ by {{.app.status.operationState.operation.initiatedBy.username | default &amp;#34;_automation_&amp;#34; }} is _unknown_.&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Diving into it with pretty-printing&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;https://perrotta.dev/2025/08/argocd-app-version-notifications/#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go-template&#34;&gt;{{if .app.spec.source.targetRevision}}&#xA;  ({{.app.spec.source.targetRevision}})&#xA;{{else}}&#xA;  {{range .app.spec.sources}}&#xA;    {{if .targetRevision}}&#xA;      ({{.targetRevision}})&#xA;      {{break}}&#xA;    {{end}}&#xA;  {{end}}&#xA;{{end}}&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;If there&amp;rsquo;s a single source in the helm chart, we extract its &lt;code&gt;targetRevision&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Otherwise, when using a &lt;a href=&#34;https://argo-cd.readthedocs.io/en/latest/user-guide/multiple_sources/&#34;&gt;multi-source&#xA;app&lt;/a&gt;, we&#xA;extract the first available &lt;code&gt;targetRevision&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;These are&#xA;&lt;a href=&#34;https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/GoTemplate/&#34;&gt;golang&lt;/a&gt;&#xA;&lt;a href=&#34;https://pkg.go.dev/text/template&#34;&gt;templates&lt;/a&gt;.&amp;#160;&lt;a href=&#34;https://perrotta.dev/2025/08/argocd-app-version-notifications/#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: ArgoCD: app version notifications&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>ArgoCD: recovery when traefik is broken
      </title>
      <link>https://perrotta.dev/2025/08/argocd-recovery-when-traefik-is-broken/</link>
      <pubDate>Sat, 09 Aug 2025 15:24:53 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/08/argocd-recovery-when-traefik-is-broken/</guid>
      <description>&lt;p&gt;♠ It&amp;rsquo;s Friday. The best day to push to prod™.&#xA;An internal scheduled upgrade for &lt;code&gt;traefik&lt;/code&gt; goes awry.&#xA;Internal services are no longer accessible.&#xA;HTTP requests won&amp;rsquo;t be properly routed.&lt;/p&gt;&#xA;&lt;p&gt;The most sensible SRE / DevOps will simply roll it back. Roll back first,&#xA;investigate later: it&amp;rsquo;s crucial to minimize downtime.&lt;/p&gt;&#xA;&lt;p&gt;How do we roll back?&#xA;We&amp;rsquo;re using GitOps, the most sensible approach is to make a new git commit by&#xA;submitting a pull request.&#xA;However, this is an emergency situation. Can we roll back faster?&lt;/p&gt;&#xA;&lt;p&gt;Sure, let&amp;rsquo;s make temporary changes directly in the cluster.&lt;/p&gt;&#xA;&lt;p&gt;The &lt;code&gt;traefik&lt;/code&gt; app is managed with ArgoCD. Let&amp;rsquo;s log in to the ArgoCD web UI and&#xA;roll back to the previous version.&lt;/p&gt;&#xA;&lt;p&gt;Oh, wait. We can&amp;rsquo;t. The ArgoCD web UI isn&amp;rsquo;t accessible, because traefik is&#xA;broken. &lt;em&gt;Sighs&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s use the ArgoCD CLI then.&lt;/p&gt;&#xA;&lt;p&gt;Aaaah, no. The ArgoCD CLI is really a REST client for the ArgoCD API. HTTP(S) is&#xA;not accessible, so the CLI won&amp;rsquo;t work either.&lt;/p&gt;&#xA;&lt;p&gt;What&amp;rsquo;s next? A plain &lt;code&gt;helm&lt;/code&gt; upgrade should work. But &lt;code&gt;helm&lt;/code&gt; is picky and will&#xA;refuse to touch manifests it doesn&amp;rsquo;t own. The manifests are managed by Argo.&#xA;&lt;code&gt;helm&lt;/code&gt; won&amp;rsquo;t budge. Oh gosh.&lt;/p&gt;&#xA;&lt;p&gt;Last option: the ArgoCD controllers are still running. We don&amp;rsquo;t need the ArgoCD&#xA;HTTPS API to communicate with them. Let&amp;rsquo;s simply edit the k8s manifests&#xA;directly.&lt;/p&gt;&#xA;&lt;p&gt;First we patch the root app (app-of-apps pattern):&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;kubectl edit app -n argocd root&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;We remove the &lt;code&gt;automated&lt;/code&gt; block, to effectively pause auto sync, otherwise our&#xA;subsequent changes to traefik would be immediately reverted:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;spec:&#xA;#  automated:&#xA;#    prune: [..]&#xA;#    selfHeal: [...]&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;You can comment or delete the &lt;code&gt;automated:&lt;/code&gt; block, either is fine.&lt;/p&gt;&#xA;&lt;p&gt;Next, we need to remove traefik from all application project (app project) sync&#xA;windows, so that changes to it will be immediately applied:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;kubectl edit appproject -n argocd cluster&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The traefik entry got removed from all sync windows.&lt;/p&gt;&#xA;&lt;p&gt;Now, finally, let&amp;rsquo;s instruct Argo to roll traefik back.&lt;/p&gt;&#xA;&lt;p&gt;The broken version is 2.5.1, the previous working version was 2.4.1.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;kubectl edit app -n argocd traefik&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Update &lt;code&gt;targetRevision: 2.4.1&lt;/code&gt; under the &lt;code&gt;helm&lt;/code&gt; block.&lt;/p&gt;&#xA;&lt;p&gt;Wait a few seconds. Observe traefik pods as they are rolling restarted:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;kubectl get deploy -n kube-system traefik -w&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Finally, all services are once again accessible!&lt;/p&gt;&#xA;&lt;p&gt;From this point on, we can go back to &lt;code&gt;git&lt;/code&gt; and do a proper pull request revert&#xA;to reconcile the codebase with the live cluster state.&lt;/p&gt;&#xA;&lt;p&gt;This was an interesting exercise. I&amp;rsquo;m quite familiar with ArgoCD and GitOps at&#xA;this point, and I instinctively knew what to do in this situation, without&#xA;needing to consult the official documentation or even an LLM. I wouldn&amp;rsquo;t have&#xA;minded doing either of these if needed, but the point is that it feels good to&#xA;be well familiar with how a system works and how to apply your knowledge to fix&#xA;day-to-day problems.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: ArgoCD: recovery when traefik is broken&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>ArgoCD: all applications stuck on refresh
      </title>
      <link>https://perrotta.dev/2025/08/argocd-all-applications-stuck-on-refresh/</link>
      <pubDate>Fri, 01 Aug 2025 14:30:58 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/08/argocd-all-applications-stuck-on-refresh/</guid>
      <description>&lt;p&gt;♠ The problem is very likely the Application Controller.&lt;/p&gt;&#xA;&lt;p&gt;You can restart it:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;kubectl rollout restart sts -n argocd argocd-application-controller&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Or inspect its logs:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;kubectl logs -n argocd argocd-application-controller-0 --tail=100 | less&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;While we&amp;rsquo;re here, make sure there is at least one replica running:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;% kubectl get sts -n argocd argocd-application-controller&#xA;NAME                            READY   AGE&#xA;argocd-application-controller   1/1     415d&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/argoproj/argo-cd/issues/11831&#34;&gt;Upstream issue&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: ArgoCD: all applications stuck on refresh&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>ArgoCD: deployment spec.selector: field is immutable
      </title>
      <link>https://perrotta.dev/2025/06/argocd-deployment-spec.selector-field-is-immutable/</link>
      <pubDate>Mon, 30 Jun 2025 13:04:51 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/06/argocd-deployment-spec.selector-field-is-immutable/</guid>
      <description>&lt;p&gt;♠ When using &lt;a href=&#34;https://argo-cd.readthedocs.io/en/stable/&#34;&gt;ArgoCD&lt;/a&gt; to manage an app,&#xA;the Deployment manifest needs to undergo significant changes, updating its&#xA;selector labels. When trying to sync the argocd app, we get the following error:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;one or more objects failed to apply, reason: error when patching &amp;#34;/dev/shm/3469418025&amp;#34;: Deployment.apps &amp;#34;myapp&amp;#34; is invalid: spec.selector: Invalid value: v1.LabelSelector{MatchLabels:v1.LabelSelectorRequirement(nil)}: field is immutable. Retrying attempt #2 at 11:02AM.&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This error doesn&amp;rsquo;t come directly from Argo, it is from &lt;code&gt;kubectl apply&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;There are two ways to address it:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Sync the argo app with the&#xA;&lt;a href=&#34;https://argo-cd.readthedocs.io/en/latest/user-guide/sync-options/#force-sync&#34;&gt;&amp;ldquo;force&amp;rdquo;&lt;/a&gt;&#xA;option enabled&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Run &lt;code&gt;kubectl apply&lt;/code&gt; with &lt;code&gt;--force&lt;/code&gt; (which is ultimately what the&#xA;aforementioned option maps to).&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;In action (&lt;code&gt;-w&lt;/code&gt; is short for &lt;code&gt;--watch&lt;/code&gt;):&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;% kubectl get endpoints -w&#xA;NAME         ENDPOINTS         AGE&#xA;myapp        &amp;lt;none&amp;gt;            5y44d&#xA;myapp                          5y44d&#xA;myapp        10.1.13.40:8080   5y44d&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: ArgoCD: deployment spec.selector: field is immutable&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Helm repository with a trailing slash
      </title>
      <link>https://perrotta.dev/2025/06/helm-repository-with-a-trailing-slash/</link>
      <pubDate>Tue, 24 Jun 2025 11:32:50 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <category>pre-commit</category>
      <guid>https://perrotta.dev/2025/06/helm-repository-with-a-trailing-slash/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://github.com/argoproj/argo-cd/issues/9857&#34;&gt;A repository URL with a trailing slash, and the same URL without a trailing&#xA;slash, are treated as different&#xA;repos&lt;/a&gt;. We found this out the&#xA;hard way:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;Failed to load target state: failed to generate manifest for source 1 of 2: rpc error: code = Unknown desc = error building helm chart dependencies: failed to add helm repository https://chartmuseum.{corp intranet}/orgs/{corp}/: failed to add repository: failed to get command args to log: `helm repo add https:--chartmuseum.{corp intranet}-orgs-{corp}- https://chartmuseum.{corp intranet}/orgs/{corp}/` failed exit status 1: Error: looks like &amp;#34;https://chartmuseum.{corp intranet}/orgs/{corp}/&amp;#34; is not a valid chart repository or cannot be reached: failed to fetch https://chartmuseum.{corp intranet}/orgs/{corp}/index.yaml : 401 Unauthorized&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s put aside the fact the error message REALLY likes to repeat the repo name.&lt;/p&gt;&#xA;&lt;p&gt;This is a repository that we&amp;rsquo;ve already configured, with proper authentication&#xA;credentials. Why would it start to fail now, all of a sudden?&lt;/p&gt;&#xA;&lt;p&gt;You already got spoiled in the post title. Given &lt;code&gt;Chart.yaml&lt;/code&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: v2&#xA;name: corp-postgres-operator&#xA;description: Corp Postgres Operator&#xA;type: application&#xA;version: 1.1.13&#xA;&#xA;dependencies:&#xA;  - name: postgres-operator&#xA;    repository: https://chartmuseum.{corp intranet}/orgs/archive&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;And also:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: v2&#xA;name: corp-postgres-operator&#xA;description: Corp Postgres Operator&#xA;type: application&#xA;version: 1.1.13&#xA;&#xA;dependencies:&#xA;  - name: postgres-operator&#xA;    repository: https://chartmuseum.{corp intranet}/orgs/archive/&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;These two dependencies end up being treated differently by Argo, even though&#xA;they are effectively the same. It&amp;rsquo;s an upstream bug.&lt;/p&gt;&#xA;&lt;p&gt;To work around it, I decided to simply remove all trailing slashes from&#xA;helm repository dependencies. Canonicalize everything!&lt;/p&gt;&#xA;&lt;p&gt;A git &lt;a href=&#34;https://pre-commit.com&#34;&gt;pre-commit.com hook&lt;/a&gt; is a good mechanism to do so:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;repos:&#xA;  repo: local&#xA;  hooks:&#xA;    - id: helm-repo-trailing-slash&#xA;      name: Helm repositories must not contain trailing slashes in Chart.yaml&#xA;      files: Chart.yaml$&#xA;      language: pygrep&#xA;      entry: &amp;#39;\s*repository: [\w:/.]&amp;#43;/$&amp;#39;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Run &lt;code&gt;pre-commit run -a helm-repo-trailing-slash&lt;/code&gt;, fix all violations, and then&#xA;never think about this again.&lt;/p&gt;&#xA;&lt;p&gt;Should the upstream bug ever be fixed, we can remove this workaround.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Helm repository with a trailing slash&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/pre-commit/&#34;&gt;#pre-commit&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>★ ArgoCD: every application in unknown state
      </title>
      <link>https://perrotta.dev/2025/06/argocd-every-application-in-unknown-state/</link>
      <pubDate>Fri, 13 Jun 2025 22:34:33 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>bestof</category>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/06/argocd-every-application-in-unknown-state/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Symptom&lt;/strong&gt;: Every single application in the Kubernetes cluster that is managed&#xA;by Argo is in &lt;code&gt;Unknown&lt;/code&gt; sync status. A disaster! 😱&lt;/p&gt;&#xA;&lt;p&gt;This has only happened in one of our clusters. More specifically, in an&#xA;&lt;a href=&#34;https://learn.microsoft.com/en-us/azure/aks/core-aks-concepts&#34;&gt;AKS&lt;/a&gt; cluster&#xA;(from Microsoft Azure).&lt;/p&gt;&#xA;&lt;p&gt;An observed effect:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl get --raw=&amp;#39;/openapi/v2&amp;#39; | head -c 200&#xA;Error from server (ServiceUnavailable): the server is currently unable to handle the request&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Aha! The Kubernetes OpenAPI server is not responding properly.&#xA;That explains why Argo is having so much trouble to figure out the state of affairs in&#xA;the cluster.&lt;/p&gt;&#xA;&lt;p&gt;It was quite difficult to troubleshoot this issue. There are barely any&#xA;resources about it in the open web. We filed an Azure Support ticket, but it was&#xA;completely useless. They offered to restart&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;https://perrotta.dev/2025/06/argocd-every-application-in-unknown-state/#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; the OpenAPI server, but that&#xA;didn&amp;rsquo;t help (of course it wouldn&amp;rsquo;t!).&lt;/p&gt;&#xA;&lt;p&gt;Because this is a managed Kubernetes cluster, we have limited access to it,&#xA;which includes lack of access to raw logs. The Azure portal doesn&amp;rsquo;t make it easy&#xA;but, after digging quite deep into it, eventually we found this error message in&#xA;the logs:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt; {&amp;#34;pod&amp;#34;:&amp;#34;kube-apiserver-7785d59f4f-scqmp&amp;#34;,&amp;#34;stream&amp;#34;:&amp;#34;stderr&amp;#34;,&amp;#34;containerID&amp;#34;:&amp;#34;925345f6501626aa6cd40dd187d5cf57837389eec9cb39b2292fb9ae3f66242f&amp;#34;,&amp;#34;log&amp;#34;:&amp;#34;E0512 12:01:36.876768       1 handler.go:160] Error in OpenAPI handler: failed to build merge specs: unable to merge: duplicated path \/apis\/reports.kyverno.io\/v1\/clusterephemeralreports\n&amp;#34;}&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;That was the first clue! Now we know it&amp;rsquo;s related to&#xA;&lt;a href=&#34;https://kyverno.io/&#34;&gt;Kyverno&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;That error eventually led us to&#xA;&lt;a href=&#34;https://github.com/kubernetes/kubernetes/issues/122668#issuecomment-2531243040&#34;&gt;this&lt;/a&gt;&#xA;issue in Kubernetes:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;We have the same issue here, actually also triggered by installing the kyverno&#xA;reports-server with both apiservices (different groups served by the same&#xA;api-server).&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Aha!&lt;/p&gt;&#xA;&lt;p&gt;Deleting the duplicate CRDs installed by &lt;a href=&#34;https://github.com/kyverno/reports-server&#34;&gt;kyverno reports&#xA;server&lt;/a&gt; has immediately resolved the&#xA;OpenAPI server unresponsiveness. It wasn&amp;rsquo;t even necessary to restart it:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl delete crd ephemeralreports.reports.kyverno.io clusterephemeralreports.reports.kyverno.io clusterpolicyreports.wgpolicyk8s.io policyreports.wgpolicyk8s.io&#xA;customresourcedefinition.apiextensions.k8s.io &amp;#34;ephemeralreports.reports.kyverno.io&amp;#34; deleted&#xA;customresourcedefinition.apiextensions.k8s.io &amp;#34;clusterephemeralreports.reports.kyverno.io&amp;#34; deleted&#xA;customresourcedefinition.apiextensions.k8s.io &amp;#34;clusterpolicyreports.wgpolicyk8s.io&amp;#34; deleted&#xA;customresourcedefinition.apiextensions.k8s.io &amp;#34;policyreports.wgpolicyk8s.io&amp;#34; deleted&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;And now:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ kubectl get --raw=&amp;#39;/openapi/v2&amp;#39; | head -c 200&#xA;{&amp;#34;swagger&amp;#34;:&amp;#34;2.0&amp;#34;,&amp;#34;info&amp;#34;:{&amp;#34;title&amp;#34;:&amp;#34;Kubernetes&amp;#34;,&amp;#34;version&amp;#34;:&amp;#34;v1.30.11&amp;#34;},&amp;#34;paths&amp;#34;:{&amp;#34;/.well-known/openid-configuration/&amp;#34;:{&amp;#34;get&amp;#34;:{&amp;#34;description&amp;#34;:&amp;#34;get service account issuer OpenID configuration, also known as&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;That was a tricky one! Interestingly: it has only occurred in AKS.&#xA;&lt;a href=&#34;https://aws.amazon.com/eks/&#34;&gt;EKS&lt;/a&gt; was fine.&lt;/p&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://en.wikiquote.org/wiki/The_IT_Crowd&#34;&gt;Hello, IT. Have you tried turning it off and on&#xA;again?&lt;/a&gt;.&amp;#160;&lt;a href=&#34;https://perrotta.dev/2025/06/argocd-every-application-in-unknown-state/#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: ArgoCD: every application in unknown state&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/bestof/&#34;&gt;#bestof&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>★ ArgoCD: custom health check for StatefulSet (OnDelete)
      </title>
      <link>https://perrotta.dev/2025/06/argocd-custom-health-check-for-statefulset-ondelete/</link>
      <pubDate>Fri, 13 Jun 2025 21:40:01 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>bestof</category>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/06/argocd-custom-health-check-for-statefulset-ondelete/</guid>
      <description>&lt;p&gt;♠ It turns out the&#xA;&lt;a href=&#34;https://github.com/argoproj/gitops-engine/blob/master/pkg/health/health_statefulset.go&#34;&gt;built-in&lt;/a&gt;&#xA;health check for&#xA;&lt;a href=&#34;https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/&#34;&gt;StatefulSets&lt;/a&gt;&#xA;in ArgoCD is not comprehensive enough.&lt;/p&gt;&#xA;&lt;p&gt;It covers&#xA;&lt;a href=&#34;https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies&#34;&gt;&lt;code&gt;RollingUpdate&lt;/code&gt;&lt;/a&gt;&#xA;well enough, but &lt;code&gt;OnDelete&lt;/code&gt; is incomplete.&lt;/p&gt;&#xA;&lt;p&gt;More specifically:&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Observed&lt;/strong&gt;: whenever you push an update to an STS whose &lt;code&gt;.spec.updateStrategy&lt;/code&gt;&#xA;is &lt;code&gt;OnDelete&lt;/code&gt;, its health status stays as &lt;code&gt;Healthy&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Expected&lt;/strong&gt;: there should be an indication that the STS pods are not&#xA;up-to-date, and hence need to be restarted to incorporate the latest changes.&lt;/p&gt;&#xA;&lt;p&gt;There is &lt;a href=&#34;https://github.com/argoproj/argo-cd/issues/6527&#34;&gt;an open bug&lt;/a&gt;:&#xA;&lt;code&gt;Statefulset healthcheck does not work #6527&lt;/code&gt; for it.&lt;/p&gt;&#xA;&lt;p&gt;I managed to work around it by defining my own health check. This is an use case&#xA;where Gen AI is helpful, though it does not shine. I could not have Gen AI&#xA;produce a fully working health check for this scenario. Instead, I used it to&#xA;bootstrap a basic example, then had to tweak it until it worked. This has been&#xA;tested multiple times, I am reasonably confident it works well (some edge cases&#xA;could be missing though).&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-lua&#34;&gt;health_status = {}&#xA;&#xA;if obj.status == nil then&#xA;  health_status.status = &amp;#34;Progressing&amp;#34;&#xA;  health_status.message = &amp;#34;Waiting for status to be reported&amp;#34;&#xA;  return health_status&#xA;end&#xA;&#xA;local strategy = obj.spec.updateStrategy and obj.spec.updateStrategy.type or &amp;#34;RollingUpdate&amp;#34;&#xA;local spec_replicas = obj.spec.replicas or 1&#xA;local ready_replicas = obj.status.readyReplicas or 0&#xA;local updated_replicas = obj.status.updatedReplicas or 0&#xA;local status_replicas = obj.status.replicas or 0&#xA;local current_revision = obj.status.currentRevision or &amp;#34;&amp;#34;&#xA;local update_revision = obj.status.updateRevision or &amp;#34;&amp;#34;&#xA;&#xA;if strategy == &amp;#34;OnDelete&amp;#34; then&#xA;  if ready_replicas == status_replicas and updated_replicas == spec_replicas then&#xA;    health_status.status = &amp;#34;Healthy&amp;#34;&#xA;    health_status.message = &amp;#34;All replicas are ready and updated (OnDelete strategy)&amp;#34;&#xA;  elseif updated_replicas == 0 then&#xA;    health_status.status = &amp;#34;Degraded&amp;#34;&#xA;    health_status.message = &amp;#34;No replicas have been updated (OnDelete strategy): 0/&amp;#34; .. spec_replicas .. &amp;#34; updated. Still running an old revision!&amp;#34;&#xA;  else&#xA;    health_status.status = &amp;#34;Progressing&amp;#34;&#xA;    health_status.message = &amp;#34;Progressing (OnDelete): &amp;#34; .. ready_replicas .. &amp;#34;/&amp;#34; .. status_replicas .. &amp;#34; ready, &amp;#34; .. updated_replicas .. &amp;#34;/&amp;#34; .. spec_replicas .. &amp;#34; updated&amp;#34;&#xA;  end&#xA;else&#xA;  if ready_replicas == status_replicas and updated_replicas == spec_replicas then&#xA;    if current_revision ~= update_revision then&#xA;      health_status.status = &amp;#34;Progressing&amp;#34;&#xA;      health_status.message = &amp;#34;Waiting for rollout to complete: revision mismatch (&amp;#34; .. current_revision .. &amp;#34; ≠ &amp;#34; .. update_revision .. &amp;#34;)&amp;#34;&#xA;    else&#xA;      health_status.status = &amp;#34;Healthy&amp;#34;&#xA;      health_status.message = &amp;#34;All replicas updated, ready, and revisions match&amp;#34;&#xA;    end&#xA;  else&#xA;    health_status.status = &amp;#34;Progressing&amp;#34;&#xA;    health_status.message = &amp;#34;RollingUpdate progressing: &amp;#34; .. ready_replicas .. &amp;#34;/&amp;#34; .. status_replicas .. &amp;#34; ready, &amp;#34; .. updated_replicas .. &amp;#34;/&amp;#34; .. spec_replicas .. &amp;#34; updated&amp;#34;&#xA;  end&#xA;end&#xA;return health_status&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;It is necessary to distinguish between &lt;code&gt;OnDelete&lt;/code&gt; and &lt;code&gt;RollingUpdate&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;OnDelete&lt;/code&gt; should not use &lt;code&gt;currentRevision&lt;/code&gt; / &lt;code&gt;update_revision&lt;/code&gt;, as these values&#xA;don&amp;rsquo;t mean anything for that update strategy. This can be observed&#xA;&lt;a href=&#34;https://github.com/argoproj/gitops-engine/blob/f8f1b61ba3fd5fcb647563db3106977e1364de31/pkg/health/health_statefulset.go#L63&#34;&gt;upstream&lt;/a&gt;&#xA;as well.&lt;/p&gt;&#xA;&lt;p&gt;It is easy to write / tweak the health check when you look at the live STS&#xA;resource / manifest in the cluster. &lt;code&gt;obj&lt;/code&gt; refers to the STS manifest (&lt;code&gt;kubectl get sts -o yaml&lt;/code&gt;). The most relevant field is &lt;code&gt;status:&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;If you deploy ArgoCD via its helm chart, add that check for &lt;code&gt;argocd-cm&lt;/code&gt;. The&#xA;&lt;code&gt;values.yaml&lt;/code&gt; file:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;argo-cd:&#xA;  configs:&#xA;    cm:&#xA;      resource.customizations.health.apps_StatefulSet: |&#xA;        health_status = {}&#xA;        [...]&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This custom health check was partly inspired by a Prometheus alert we have:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;alert: StatefulSetUpdateNotRolledOut&#xA;expr: max without (revision) (kube_statefulset_status_current_revision unless kube_statefulset_status_update_revision) * (kube_statefulset_replicas != kube_statefulset_status_replicas_updated)&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;How long will it take until LLMs &lt;del&gt;steal&lt;/del&gt; adopt this novel solution from me?&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: ArgoCD: custom health check for StatefulSet (OnDelete)&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/bestof/&#34;&gt;#bestof&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>ArgoCD: application stuck in unknown
      </title>
      <link>https://perrotta.dev/2025/06/argocd-application-stuck-in-unknown/</link>
      <pubDate>Thu, 12 Jun 2025 11:51:32 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/06/argocd-application-stuck-in-unknown/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Problem statement&lt;/strong&gt;: An application in ArgoCD that was previously running&#xA;smoothly has suddenly started to fail. Its status has changed to &amp;ldquo;Unknown&amp;rdquo;.&#xA;The &amp;ldquo;Refresh&amp;rdquo; button is grayed out in the Argo web UI.&lt;/p&gt;&#xA;&lt;p&gt;I found the following works in this scenario, which is typically a transient&#xA;issue:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;restart the argo server&lt;/li&gt;&#xA;&lt;li&gt;restart the argo repo server&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;In terms of commands:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl rollout restart deployment argocd-server -n argocd&#xA;% kubectl rollout restart deployment argocd-repo-server -n argocd&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: ArgoCD: application stuck in unknown&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>ArgoCD: upgrade to v3, remove helm labels
      </title>
      <link>https://perrotta.dev/2025/06/argocd-upgrade-to-v3-remove-helm-labels/</link>
      <pubDate>Mon, 02 Jun 2025 13:03:24 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/06/argocd-upgrade-to-v3-remove-helm-labels/</guid>
      <description>&lt;p&gt;♠ I am leading the effort to upgrade ArgoCD to its &lt;a href=&#34;https://blog.argoproj.io/announcing-argo-cd-v3-small-but-mighty-df05c0b39ad6&#34;&gt;newest major version&#xA;(v3)&lt;/a&gt;&#xA;across our fleet.&lt;/p&gt;&#xA;&lt;p&gt;One of its &lt;a href=&#34;https://argo-cd.readthedocs.io/en/stable/operator-manual/upgrading/2.14-3.0/&#34;&gt;breaking&#xA;changes&lt;/a&gt;&#xA;is the removal of helm labels from argo applications, like such:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;metadata:&#xA;# [...]&#xA;  labels:&#xA;    app.kubernetes.io/managed-by: Helm&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Applying a normal sync does not get rid of the label.&lt;/p&gt;&#xA;&lt;p&gt;I found out that doing an one-off &lt;a href=&#34;https://argo-cd.readthedocs.io/en/latest/user-guide/sync-options/#server-side-apply&#34;&gt;server-side apply&#xA;sync&lt;/a&gt;&#xA;addresses the label removal, and it is safe to do (incurs no downtime):&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;However, there are some cases where you want to use &lt;code&gt;kubectl apply --server-side&lt;/code&gt; over &lt;code&gt;kubectl apply&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;p&gt;[&amp;hellip;]&lt;/p&gt;&#xA;&lt;p&gt;Patching of existing resources on the cluster that are not fully managed by&#xA;Argo CD.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;There&amp;rsquo;s no need to make the server-side apply a permanent config, i.e.:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: argoproj.io/v1alpha1&#xA;kind: Application&#xA;spec:&#xA;  syncPolicy:&#xA;    syncOptions:&#xA;    - ServerSideApply=true&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&amp;hellip;though there&amp;rsquo;s no harm either in doing so.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: ArgoCD: upgrade to v3, remove helm labels&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>PDB: minAvailable and maxUnavailable cannot be both set
      </title>
      <link>https://perrotta.dev/2025/06/pdb-minavailable-and-maxunavailable-cannot-be-both-set/</link>
      <pubDate>Mon, 02 Jun 2025 12:00:05 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/06/pdb-minavailable-and-maxunavailable-cannot-be-both-set/</guid>
      <description>&lt;p&gt;♠ In Kubernetes it&amp;rsquo;s not possible to have a &lt;a href=&#34;https://kubernetes.io/docs/tasks/run-application/configure-pdb/&#34;&gt;&lt;code&gt;PodDisruptionBudget&lt;/code&gt;&#xA;(PDB)&lt;/a&gt; with&#xA;both &lt;code&gt;minAvailable&lt;/code&gt; and &lt;code&gt;maxUnavailable&lt;/code&gt; set simultaneously:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;one or more objects failed to apply, reason: PodDisruptionBudget.policy &amp;#34;pgbouncer-pgbouncer&amp;#34; is invalid: spec: Invalid value: policy.PodDisruptionBudgetSpec{MinAvailable:(*intstr.IntOrString)(0xc02c03dd20), Selector:(*v1.LabelSelector)(0xc02c03dd40), MaxUnavailable:(*intstr.IntOrString)(0xc02c03dd60), UnhealthyPodEvictionPolicy:(*policy.UnhealthyPodEvictionPolicyType)(nil)}: minAvailable and maxUnavailable cannot be both set (retried 5 times).&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Our manifests did not violate this rule though. Then where did the error message&#xA;come from?&lt;/p&gt;&#xA;&lt;p&gt;It turns out &lt;a href=&#34;https://argo-cd.readthedocs.io/en/stable/&#34;&gt;ArgoCD&lt;/a&gt; was creating an&#xA;intermediate state containing both properties:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;# [...]&#xA;spec:&#xA;  minAvailable: 1&#xA;  maxUnavailable: 1&#xA;# [...]&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;One solution is to sync the underlying application PDB manifest with&#xA;&lt;a href=&#34;https://argo-cd.readthedocs.io/en/latest/user-guide/sync-options/#replace-resource-instead-of-applying-changes&#34;&gt;replace&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;: It&amp;rsquo;s better to &amp;ldquo;sync with replace&amp;rdquo; &lt;strong&gt;only&lt;/strong&gt; the PDB. Do not do it&#xA;in the entire application.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: PDB: minAvailable and maxUnavailable cannot be both set&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>kubernetes: list pending pods in daemonset
      </title>
      <link>https://perrotta.dev/2025/05/kubernetes-list-pending-pods-in-daemonset/</link>
      <pubDate>Tue, 20 May 2025 16:41:00 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/05/kubernetes-list-pending-pods-in-daemonset/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Problem statement&lt;/strong&gt;: Given a DaemonSet with a couple of pods stuck in&#xA;&amp;ldquo;Pending&amp;rdquo; state during their rollout, find out which nodes did not yet complete&#xA;the pod initialization.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl get nodes -o name | grep -vFf &amp;lt;(kubectl get pods -n {namespace} -l {ds-label-selector} -o wide --no-headers | awk &amp;#39;{print &amp;#34;node/&amp;#34; $7}&amp;#39;)&#xA;node/aks-database-12345678-vmss000000&#xA;node/aks-database-12345678-vmss000002&#xA;node/aks-systempool-87654321-vmss000000&#xA;node/aks-systempool-87654321-vmss000001&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Notes:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;kubectl get nodes -o name&lt;/code&gt;: list all node names in the &lt;code&gt;node/&amp;lt;name&amp;gt;&lt;/code&gt; form&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;kubectl get pods [...] -o wide&lt;/code&gt;: get the node name for each running DaemonSet pod&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;awk &#39;{print &amp;quot;node/&amp;quot; $7}&#39;&lt;/code&gt;: get the node name matching the output of &lt;code&gt;get nodes&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;grep [...]&lt;/code&gt;: find the nodes we are looking for&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Surely there must be an easier way to accomplish this?&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: kubernetes: list pending pods in daemonset&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>helm-secrets &#43; vals: string interpolation
      </title>
      <link>https://perrotta.dev/2025/05/helm-secrets--vals-string-interpolation/</link>
      <pubDate>Thu, 08 May 2025 15:25:14 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/05/helm-secrets--vals-string-interpolation/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://github.com/jkroepke/helm-secrets&#34;&gt;helm-secrets&lt;/a&gt; is:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;a helm plugin that helps to manage secrets with Git workflow and store them&#xA;anywhere.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;It uses &lt;a href=&#34;https://github.com/helmfile/vals&#34;&gt;vals&lt;/a&gt; underneath, which is a:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Helm-like configuration values loader with support for various sources&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;&amp;hellip;including, in particular, &lt;a href=&#34;https://www.hashicorp.com/en/products/vault&#34;&gt;HashiCorp Vault&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The &lt;code&gt;vals&lt;/code&gt; syntax is documented&#xA;&lt;a href=&#34;https://github.com/helmfile/vals?tab=readme-ov-file#expression-syntax&#34;&gt;in the vals documentation&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Problem statement&lt;/strong&gt;: construct a string composed of the following two vault&#xA;entries:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;vault://kv-v2/services/common/postgres/#url&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;vault://kv-v2/services/common/postgres/#params&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&amp;hellip;to yield &lt;code&gt;{url}?{params}&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;How do we concatenate two vault entries?&#xA;The secret is in the &lt;code&gt;+&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Finally, the optional trailing &lt;code&gt;+&lt;/code&gt; is the explicit &amp;ldquo;end&amp;rdquo; of the expression.&#xA;You usually don&amp;rsquo;t need it, as if omitted, it treats anything after &lt;code&gt;ref+&lt;/code&gt; and&#xA;before the new-line or the end-of-line as an expression to be evaluated. An&#xA;explicit &lt;code&gt;+&lt;/code&gt; is handy when you want to do a simple string interpolation. That&#xA;is, &lt;code&gt;foo ref+SECRET1+ ref+SECRET2+ bar&lt;/code&gt; evaluates to &lt;code&gt;foo SECRET1_VALUE SECRET2_VALUE bar&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The final result:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;secrets&amp;#43;literal://ref&amp;#43;vault://kv-v2/services/common/postgres/#url&amp;#43;?ref&amp;#43;vault://kv-v2/services/common/postgres/#params&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&amp;hellip;wherein &lt;code&gt;secrets+literal://&lt;/code&gt; comes from &lt;code&gt;helm-secrets&lt;/code&gt;. It needs to appear only&#xA;once. &lt;code&gt;ref+vault://&lt;/code&gt; comes from &lt;code&gt;vals&lt;/code&gt;, it appears once for each vault entry.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: helm-secrets + vals: string interpolation&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>HashiCorp Vault from helm values
      </title>
      <link>https://perrotta.dev/2025/04/hashicorp-vault-from-helm-values/</link>
      <pubDate>Wed, 23 Apr 2025 16:56:16 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/04/hashicorp-vault-from-helm-values/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Problem statement&lt;/strong&gt;: Populate HashiCorp Vault with select keys from &lt;code&gt;helm get values&lt;/code&gt; for a given chart release.&lt;/p&gt;&#xA;&lt;p&gt;The following script works:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash&#xA;# Usage: Tweak entries as needed and then run the script.&#xA;&#xA;set -euo pipefail&#xA;&#xA;# It&amp;#39;s an associative array: a dictionary, a hash map&#xA;#&#xA;#   Key: the entry to be populated in vault&#xA;#   Value: a `jq` JSON selector from the output of `helm get values -o json`&#xA;declare -A entries=(&#xA;    # keep-sorted start&#xA;    [config.aws.accessKey]=&amp;#34;.config.aws.accessKey&amp;#34;&#xA;    [config.aws.secretKey]=&amp;#34;.config.aws.secretKey&amp;#34;&#xA;    [config.cronjob.apiPassword]=&amp;#34;.config.cronjob.apiPassword&amp;#34;&#xA;    [config.cronjob.apiUser]=&amp;#34;.config.cronjob.apiUser&amp;#34;&#xA;    [config.postgresql.adminpass]=&amp;#34;.config.postgresql.adminpass&amp;#34;&#xA;    [config.postgresql.password]=&amp;#34;.config.postgresql.password&amp;#34;&#xA;    [config.postgresql.ropass]=&amp;#34;.config.postgresql.ropass&amp;#34;&#xA;    [config.postgresql.rwpass]=&amp;#34;.config.postgresql.rwpass&amp;#34;&#xA;    [config.slack.token]=&amp;#34;.config.slack.token&amp;#34;&#xA;    [config.slack.verificationToken]=&amp;#34;.config.slack.verificationToken&amp;#34;&#xA;    # keep-sorted end&#xA;)&#xA;&#xA;main() {&#xA;        vault login&#xA;&#xA;        FIRST=1&#xA;        for entry in &amp;#34;${!entries[@]}&amp;#34;; do&#xA;                key=&amp;#34;$entry&amp;#34;&#xA;                value=&amp;#34;${entries[$entry]}&amp;#34;&#xA;                content=&amp;#34;$(helm get values -n tools mychart -o json | jq -r &amp;#34;$value&amp;#34;)&amp;#34;&#xA;&#xA;                echo &amp;#34;Populating vault $key from helm value $key: $content...&amp;#34;&#xA;&#xA;                if [[ &amp;#34;$FIRST&amp;#34; == &amp;#34;1&amp;#34; ]]; then&#xA;                  vault kv put -mount=&amp;#34;kv-v2&amp;#34; &amp;#34;services/mychart/&amp;#34; &amp;#34;$key=$content&amp;#34;&#xA;                  FIRST=0&#xA;                else&#xA;                  vault kv patch -mount=&amp;#34;kv-v2&amp;#34; &amp;#34;services/mychart/&amp;#34; &amp;#34;$key=$content&amp;#34;&#xA;                fi&#xA;        done&#xA;&#xA;        vault kv get -mount=&amp;#34;kv-v2&amp;#34; &amp;#34;services/mychart&amp;#34;&#xA;&#xA;        rm ~/.vault-token&#xA;}&#xA;&#xA;main&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;code&gt;FIRST&lt;/code&gt;: In the first time we need to create an entry (&lt;code&gt;vault kv put&lt;/code&gt;), in the&#xA;second time we need to patch it (&lt;code&gt;vault kv patch&lt;/code&gt;).&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: HashiCorp Vault from helm values&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>ArgoCD: vertical-pod-autoscaler sync loop
      </title>
      <link>https://perrotta.dev/2025/02/argocd-vertical-pod-autoscaler-sync-loop/</link>
      <pubDate>Thu, 13 Feb 2025 13:17:36 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/02/argocd-vertical-pod-autoscaler-sync-loop/</guid>
      <description>&lt;p&gt;♠ We&amp;rsquo;ve had a long time issue wherein our&#xA;&lt;a href=&#34;https://artifacthub.io/packages/helm/cowboysysop/vertical-pod-autoscaler&#34;&gt;vertical-pod-autoscaler&lt;/a&gt;&#xA;(VPA) ArgoCD application, deployed via its helm chart, got stuck in a sync loop.&lt;/p&gt;&#xA;&lt;p&gt;More specifically: the helm chart automatically generates a self-signed&#xA;certificate via the&#xA;&lt;a href=&#34;https://helm.sh/docs/chart_template_guide/function_list/#genca&#34;&gt;GenCA&lt;/a&gt; helm&#xA;function upon every change to our gitops repository. It turns out&#xA;&lt;a href=&#34;https://github.com/cowboysysop/charts/blob/9527602f8f2ea59f51cd3a7d3380810ba60c39fc/charts/vertical-pod-autoscaler/README.md?plain=1#L236&#34;&gt;this&lt;/a&gt;&#xA;is documented and we can see its corresponding manifest&#xA;&lt;a href=&#34;https://github.com/cowboysysop/charts/blob/9527602f8f2ea59f51cd3a7d3380810ba60c39fc/charts/vertical-pod-autoscaler/templates/admission-controller/tls-secret.yaml#L3C1-L3C91&#34;&gt;in the chart template&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;So that&amp;rsquo;s where the sync loop was coming from! ArgoCD was constantly reporting&#xA;(and resolving) a diff in the admission controller secret.&lt;/p&gt;&#xA;&lt;p&gt;The manifest:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: v1&#xA;data:&#xA;  ca.crt: &amp;#43;&amp;#43;&amp;#43;&amp;#43;&amp;#43;&amp;#43;&amp;#43;&amp;#43;&#xA;  tls.crt: &amp;#43;&amp;#43;&amp;#43;&amp;#43;&amp;#43;&amp;#43;&amp;#43;&amp;#43;&#xA;  tls.key: &amp;#43;&amp;#43;&amp;#43;&amp;#43;&amp;#43;&amp;#43;&amp;#43;&amp;#43;&#xA;kind: Secret&#xA;metadata:&#xA;[...]&#xA;  name: vertical-pod-autoscaler-admission-controller-tls&#xA;  namespace: kube-system&#xA;[...]&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The problematic are all the ones under &lt;code&gt;data:&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;I confirmed that was indeed the case in the &lt;a href=&#34;https://cloud-native.slack.com/archives/C01TSERG0KZ/p1739442822542539&#34;&gt;CNCF&#xA;Slack&lt;/a&gt;&#xA;&lt;code&gt;#argo-cd&lt;/code&gt;. Thanks Tim!&lt;/p&gt;&#xA;&lt;p&gt;How to address it?&lt;/p&gt;&#xA;&lt;p&gt;We leverage&#xA;&lt;a href=&#34;https://argo-cd.readthedocs.io/en/stable/user-guide/diffing/&#34;&gt;&lt;code&gt;ignoreDifferences&lt;/code&gt;&lt;/a&gt;&#xA;(c.f. &lt;a href=&#34;https://stackoverflow.com/questions/77510338/argocd-show-as-out-of-sync-on-every-push-for-auto-generate-certs-password&#34;&gt;Stack&#xA;Overflow&lt;/a&gt;)&#xA;in the ArgoCD app manifest:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;% git diff HEAD~2..HEAD&#xA;diff --git apps/base/vertical-pod-autoscaler/vertical-pod-autoscaler.yaml apps/base/vertical-pod-autoscaler/vertical-pod-autoscaler.yaml&#xA;index 92bfbcc..90d8435 100644&#xA;--- apps/base/vertical-pod-autoscaler/vertical-pod-autoscaler.yaml&#xA;&amp;#43;&amp;#43;&amp;#43; apps/base/vertical-pod-autoscaler/vertical-pod-autoscaler.yaml&#xA;@@ -34,3 &amp;#43;34,15 @@ spec:&#xA;         maxDuration: 1m&#xA;     syncOptions:&#xA;       - CreateNamespace=true&#xA;&amp;#43;      - RespectIgnoreDifferences=true&#xA;&amp;#43;  ignoreDifferences:&#xA;&amp;#43;    - group: &amp;#34;&amp;#34;&#xA;&amp;#43;      kind: Secret&#xA;&amp;#43;      name: vertical-pod-autoscaler-admission-controller-tls&#xA;&amp;#43;      jsonPointers:&#xA;&amp;#43;        - /data&#xA;&amp;#43;    - group: apps&#xA;&amp;#43;      kind: Deployment&#xA;&amp;#43;      name: vertical-pod-autoscaler-admission-controller&#xA;&amp;#43;      jsonPointers:&#xA;&amp;#43;        - /spec/template/metadata/annotations/checksum~1tls-secret&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;In &lt;a href=&#34;https://www.rfc-editor.org/rfc/rfc6901&#34;&gt;JSON Pointers&lt;/a&gt; (RFC 6901), one&#xA;escapes the slash character with a &lt;code&gt;~1&lt;/code&gt; (c.f. section 3 in the spec):&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;Because the characters &amp;#39;~&amp;#39; (%x7E) and &amp;#39;/&amp;#39; (%x2F) have special meanings in&#xA;JSON Pointer, &amp;#39;~&amp;#39; needs to be encoded as &amp;#39;~0&amp;#39; and &amp;#39;/&amp;#39; needs to be encoded as&#xA;&amp;#39;~1&amp;#39; when these characters appear in a reference token.&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Problem resolved!&lt;/p&gt;&#xA;&lt;p&gt;In general all helm charts that generate self-signed certificates (with &lt;code&gt;genCA&lt;/code&gt;)&#xA;may experience this sync loop when used with a CD framework such as Argo.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: ArgoCD: vertical-pod-autoscaler sync loop&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Kubernetes: create a pod in pending state
      </title>
      <link>https://perrotta.dev/2025/02/kubernetes-create-a-pod-in-pending-state/</link>
      <pubDate>Fri, 07 Feb 2025 13:58:36 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/02/kubernetes-create-a-pod-in-pending-state/</guid>
      <description>&lt;p&gt;♠ We are making a non-trivial change to our cluster monitoring architecture, and I&#xA;needed to verify the new rollout is properly working in our infratesting&#xA;environment.&lt;/p&gt;&#xA;&lt;p&gt;A simple way to do so is to create a pod that never leaves the &amp;ldquo;Pending&amp;rdquo; state,&#xA;because we have an alert (a prometheus rule) that fires whenever a pod is in&#xA;that stack for too long (15 minutes is our threshold in most cases).&lt;/p&gt;&#xA;&lt;p&gt;Use the following manifest:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: v1&#xA;kind: Pod&#xA;metadata:&#xA;  name: pending-pod&#xA;spec:&#xA;  affinity:&#xA;    podAffinity:&#xA;      requiredDuringSchedulingIgnoredDuringExecution:&#xA;      - labelSelector:&#xA;          matchExpressions:&#xA;          - key: &amp;#34;non/existent&amp;#34;&#xA;            operator: In&#xA;            values:&#xA;            - &amp;#34;true&amp;#34;&#xA;        topologyKey: &amp;#34;kubernetes.io/hostname&amp;#34;&#xA;  containers:&#xA;  - name: busybox&#xA;    image: busybox&#xA;    command: [&amp;#39;sh&amp;#39;, &amp;#39;-c&amp;#39;, &amp;#39;echo Hello, Kubernetes! &amp;amp;&amp;amp; sleep 3600&amp;#39;]&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&amp;hellip;it creates a &lt;code&gt;pending-pod&lt;/code&gt; in the &lt;code&gt;default&lt;/code&gt; namespace that should never be&#xA;scheduled, due to its strict pod affinity rules.&lt;/p&gt;&#xA;&lt;p&gt;Save it as &lt;code&gt;pending-pod.yaml&lt;/code&gt;, deploy it with &lt;code&gt;kubectl apply -f pending-pod.yaml&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;$ kubectl get pod pending-pod -w&#xA;NAME          READY   STATUS    RESTARTS   AGE&#xA;pending-pod   0/1     Pending   0          21s&#xA; pending-pod   0/1     Pending   0          65s&#xA;pending-pod   0/1     Pending   0          74s&#xA;pending-pod   0/1     Pending   0          2m6s&#xA;pending-pod   0/1     Pending   0          3m8s&#xA;pending-pod   0/1     Pending   0          4m8s&#xA;pending-pod   0/1     Pending   0          4m18s&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;At some point the alert fires, as expected.&#xA;And now we can roll out these changes to prod!&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Kubernetes: create a pod in pending state&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>ArgoCD: cache: key is missing
      </title>
      <link>https://perrotta.dev/2025/01/argocd-cache-key-is-missing/</link>
      <pubDate>Thu, 09 Jan 2025 20:20:03 -0300</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/01/argocd-cache-key-is-missing/</guid>
      <description>&lt;p&gt;♠ If you experience the title error message in ArgoCD (e.g. via its web UI and/or&#xA;in pod logs), it&amp;rsquo;s related to&#xA;&lt;a href=&#34;https://github.com/argoproj/argo-cd/issues/5068&#34;&gt;argo-cd#5068&lt;/a&gt;. The full error&#xA;message is:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;Unable to load data: cache: key is missing&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;As I&#xA;&lt;a href=&#34;https://github.com/argoproj/argo-cd/issues/5068#issuecomment-2580878251&#34;&gt;commented&lt;/a&gt;&#xA;in the bug, here&amp;rsquo;s how to fix it:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;The following workaround sufficed for me: &lt;code&gt;kubectl delete pod -n argocd argocd-application-controller-0&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Initially I ran &lt;code&gt;kubectl rollout restart deployment -n argocd argocd-application-controller&lt;/code&gt; but it didn&amp;rsquo;t work.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;In my experience, this error only happens once, ephemerally, during ArgoCD&#xA;bootstrapping. Once fixed it does not reoccur.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Update(2025-06-06)&lt;/strong&gt;: This error reoccurs intermittently 🤷.&lt;/p&gt;&#xA;&lt;p&gt;Another potential workaround is to disable redis / caching altogether, as ArgoCD&#xA;can fully operate without it, but I wouldn&amp;rsquo;t recommend that.&lt;/p&gt;&#xA;&lt;p&gt;See also: &lt;a href=&#34;https://github.com/argoproj/argo-cd/issues/18503&#34;&gt;#18503&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: ArgoCD: cache: key is missing&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Helm: migrate chart dependencies from HTTPS to OCI
      </title>
      <link>https://perrotta.dev/2025/01/helm-migrate-chart-dependencies-from-https-to-oci/</link>
      <pubDate>Wed, 08 Jan 2025 18:45:57 -0300</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2025/01/helm-migrate-chart-dependencies-from-https-to-oci/</guid>
      <description>&lt;p&gt;♠ I got aware of this due to&#xA;&lt;a href=&#34;https://blog.bitnami.com/2024/10/bitnami-helm-charts-moving-to-oci.html&#34;&gt;Bitnami&lt;/a&gt;&amp;rsquo;s&#xA;blog post.&lt;/p&gt;&#xA;&lt;p&gt;In &lt;code&gt;Chart.yaml&lt;/code&gt;, instead of depending on:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;- https://charts.bitnami.com/bitnami/airflow-18.3.2.tgz&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Instead, depend on:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;- oci://registry-1.docker.io/bitnamicharts/airflow:18.3.2&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The switch is transparent, it works out-of-the-box.&lt;/p&gt;&#xA;&lt;p&gt;However there&amp;rsquo;s one caveat: You cannot &lt;code&gt;helm repo add oci://registry-1.docker.io/bitnamicharts&lt;/code&gt;. That only works for &lt;code&gt;https://&lt;/code&gt;&#xA;registries.&lt;/p&gt;&#xA;&lt;p&gt;The workaround is to &lt;code&gt;helm pull oci://[...] --version {version}&lt;/code&gt; and/or to&#xA;browse available tags/versions via docker hub.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Helm: migrate chart dependencies from HTTPS to OCI&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>★ skopeo: operate container images and registries
      </title>
      <link>https://perrotta.dev/2024/12/skopeo-operate-container-images-and-registries/</link>
      <pubDate>Tue, 17 Dec 2024 17:49:31 -0300</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>aws</category>
      <category>bestof</category>
      <category>dev</category>
      <category>docker</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2024/12/skopeo-operate-container-images-and-registries/</guid>
      <description>&lt;p&gt;♠ When working with &lt;code&gt;docker&lt;/code&gt; and private image registries, a common workflow is to copy images from one private registry in the cloud to another. This can be done with &lt;a href=&#34;https://github.com/containers/skopeo/&#34;&gt;&lt;code&gt;skopeo&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;This post includes some common recipes for it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;usage-1-default--root-to-staging&#34;&gt;&#xA;  Usage 1) Default / Root to Staging&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2024/12/skopeo-operate-container-images-and-registries/#usage-1-default--root-to-staging&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;From the default / root account registry to the staging registry:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;skopeo sync \&#xA;  --src-creds &amp;#34;AWS:$(aws ecr get-login-password --region {region} --profile default)&amp;#34; \&#xA;  --dest-creds &amp;#34;AWS:$(aws ecr get-login-password --region {region} --profile staging)&amp;#34; \&#xA;  --override-os linux --override-arch amd64 \&#xA;  --src docker --dest docker \&#xA;  {account_id_root}.dkr.ecr.{region}.amazonaws.com/{org}/{repository}:{tag} \&#xA;  {account_id_staging}.dkr.ecr.{region}.amazonaws.com/{org}&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Example values&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;https://perrotta.dev/2024/12/skopeo-operate-container-images-and-registries/#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;repository: &lt;code&gt;argocd-gitops-tools&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;tag (version): &lt;code&gt;1.0.1&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;region: &lt;code&gt;us-east-1&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;usage-2-public-to-mfa&#34;&gt;&#xA;  Usage 2) Public to MFA&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2024/12/skopeo-operate-container-images-and-registries/#usage-2-public-to-mfa&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;From a public registry to a private registry that uses MFA (multi-factor authentication).&lt;/p&gt;&#xA;&lt;p&gt;First, it&amp;rsquo;s necessary to get MFA credentials. Source the script below&#xA;(&lt;code&gt;china.mfa.sh&lt;/code&gt;):&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash&#xA;# shellcheck disable=SC2155&#xA;&#xA;export AWS_PROFILE=china&#xA;&#xA;echo -n &amp;#34;Enter the MFA token code for your AWS China account: &amp;#34; &amp;amp;&amp;amp; read -r token&#xA;&#xA;mfa_arn=&amp;#34;$(aws iam --profile &amp;#34;$AWS_PROFILE&amp;#34; get-user --output text --query User.Arn | sed &amp;#39;s/:user\//:mfa\//&amp;#39;)&amp;#34;&#xA;credentials=&amp;#34;$(aws --profile &amp;#34;$AWS_PROFILE&amp;#34; sts get-session-token --serial-number &amp;#34;$mfa_arn&amp;#34; --token-code &amp;#34;$token&amp;#34; --duration-seconds 86400)&amp;#34;&#xA;&#xA;echo &amp;#34;Got credentials: $credentials&amp;#34;&#xA;&#xA;export AWS_ACCESS_KEY_ID=$(echo &amp;#34;$credentials&amp;#34; | jq -r &amp;#39;.Credentials.AccessKeyId&amp;#39;)&#xA;export AWS_SECRET_ACCESS_KEY=$(echo &amp;#34;$credentials&amp;#34; | jq -r &amp;#39;.Credentials.SecretAccessKey&amp;#39;)&#xA;export AWS_SESSION_TOKEN=$(echo &amp;#34;$credentials&amp;#34; | jq -r &amp;#39;.Credentials.SessionToken&amp;#39;)&#xA;&#xA;[[ -n &amp;#34;$AWS_SESSION_TOKEN&amp;#34; ] &amp;amp;&amp;amp; echo &amp;#34;Success!&amp;#34;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: It is necessary to do &lt;code&gt;source china-mfa.sh&lt;/code&gt;. Doing &lt;code&gt;./china-mfa.sh&lt;/code&gt;&#xA;will &lt;strong&gt;not&lt;/strong&gt; work.&lt;/p&gt;&#xA;&lt;p&gt;Then we can proceed with the image sync:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;skopeo sync \&#xA;  --dest-creds &amp;#34;AWS:$(aws ecr get-login-password --region cn-north-1)&amp;#34; \&#xA;  --override-os linux --override-arch amd64 \&#xA;  --src docker \&#xA;  --dest docker \&#xA;  quay.io/argoproj/argocd:v2.12.6 \&#xA;  {account_id_mfa}.dkr.ecr.cn-north-1.amazonaws.com.cn/quay.io/argoproj&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Notes:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;A public registry does not need authentication, hence there&amp;rsquo;s no &lt;code&gt;--src-creds&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;--dest-creds&lt;/code&gt; does not specify a &lt;code&gt;--profile&lt;/code&gt;. Likewise, no &lt;code&gt;AWS_PROFILE&lt;/code&gt; env var should be defined.&lt;/li&gt;&#xA;&lt;li&gt;In this example, China (&lt;code&gt;cn-north-1&lt;/code&gt;) is an AWS account with MFA enabled.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;potpourri&#34;&gt;&#xA;  Potpourri&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2024/12/skopeo-operate-container-images-and-registries/#potpourri&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;It&amp;rsquo;s possible to pass &lt;code&gt;--scoped&lt;/code&gt; to prefix images at destination using the&#xA;full source image path as scope.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;To sync multiple image architectures, pass &lt;code&gt;-a&lt;/code&gt; or &lt;code&gt;--all&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;If syncing from/to Azure (ACR), use &lt;code&gt;az acr login&lt;/code&gt;. Find the &lt;code&gt;username&lt;/code&gt; in the&#xA;Azure portal. For example:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;az acr login -n {container registry name} --expose-token | jq -r &amp;#39;.accessToken&amp;#39; | skopeo login {registry} --password-stdin --username {username}&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;image = registry (includes the org name) + repository + tag&amp;#160;&lt;a href=&#34;https://perrotta.dev/2024/12/skopeo-operate-container-images-and-registries/#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: skopeo: operate container images and registries&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/aws/&#34;&gt;#aws&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/bestof/&#34;&gt;#bestof&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/docker/&#34;&gt;#docker&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Kubernetes: tail logs from pods with stern
      </title>
      <link>https://perrotta.dev/2024/12/kubernetes-tail-logs-from-pods-with-stern/</link>
      <pubDate>Tue, 17 Dec 2024 16:37:16 -0300</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2024/12/kubernetes-tail-logs-from-pods-with-stern/</guid>
      <description>&lt;p&gt;♠ You can always use &lt;code&gt;kubectl logs -n {namespace} {pod} [-c {container}] -f&lt;/code&gt; to&#xA;inspect logs from a specific pod&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;https://perrotta.dev/2024/12/kubernetes-tail-logs-from-pods-with-stern/#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Nonetheless that doesn&amp;rsquo;t scale when you don&amp;rsquo;t know which pod you want in the&#xA;first place.&lt;/p&gt;&#xA;&lt;p&gt;You could start with deployments, dive into replica sets, and then into&#xA;individual pods, one by one, but&amp;hellip; that is tedious and slow.&lt;/p&gt;&#xA;&lt;p&gt;We can do better with &lt;a href=&#34;https://github.com/stern/stern&#34;&gt;stern&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;⎈ Multi pod and container log tailing for Kubernetes&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Usage:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;stern pod-query [flags]&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;The pod-query is a regular expression or a Kubernetes resource in the form&#xA;&lt;code&gt;&amp;lt;resource&amp;gt;/&amp;lt;name&amp;gt;&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Example:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% stern -n argocd argocd&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;We can also &lt;code&gt;grep&lt;/code&gt; for specific strings in log lines with &lt;code&gt;--include&lt;/code&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% stern -n argocd argocd --include level=error&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Or &lt;code&gt;grep -v&lt;/code&gt; with &lt;code&gt;--exclude&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;It&amp;rsquo;s better to use &lt;code&gt;--include&lt;/code&gt; / &lt;code&gt;--exclude&lt;/code&gt; than to &lt;code&gt;| grep&lt;/code&gt;, because it&#xA;preserves color attributes (each pod has a different color in the aggregated&#xA;log output).&lt;/p&gt;&#xA;&lt;p&gt;The logs stream by default (like &lt;code&gt;tail -f&lt;/code&gt;, or &lt;code&gt;kubectl logs -f&lt;/code&gt;).&lt;/p&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;&lt;code&gt;-c&lt;/code&gt; to specify a container within it, and &lt;code&gt;-f&lt;/code&gt; to stream logs à la &lt;code&gt;tail -f&lt;/code&gt;.&amp;#160;&lt;a href=&#34;https://perrotta.dev/2024/12/kubernetes-tail-logs-from-pods-with-stern/#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Kubernetes: tail logs from pods with stern&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Kubernetes: debugging services
      </title>
      <link>https://perrotta.dev/2024/12/kubernetes-debugging-services/</link>
      <pubDate>Mon, 16 Dec 2024 10:14:11 -0300</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2024/12/kubernetes-debugging-services/</guid>
      <description>&lt;p&gt;♠ I am deploying a new helm chart to our staging environments, and needed a way to&#xA;ensure its dependencies in &lt;code&gt;values.yaml&lt;/code&gt; are properly configured.&lt;/p&gt;&#xA;&lt;p&gt;From the official kubernetes&#xA;&lt;a href=&#34;https://kubernetes.io/docs/tasks/debug/debug-application/debug-service/&#34;&gt;documentation&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;An issue that comes up rather frequently for new installations of Kubernetes&#xA;is that a Service is not working properly. You&amp;rsquo;ve run your Pods through a&#xA;Deployment (or other workload controller) and created a Service, but you get&#xA;no response when you try to access it. This document will hopefully help you&#xA;to figure out what&amp;rsquo;s going wrong.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h2 id=&#34;run-commands-in-a-brand-new-pod&#34;&gt;&#xA;  Run commands in a brand new pod&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2024/12/kubernetes-debugging-services/#run-commands-in-a-brand-new-pod&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl run -it --rm --restart=Never busybox --image=gcr.io/google-containers/busybox sh&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&amp;hellip;quite verbose, but it works reliably, spawning a basic pod with&#xA;&lt;a href=&#34;https://www.busybox.net/&#34;&gt;busybox&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;I prefer to have a package manager at my fingertips, hence &lt;code&gt;alpine&lt;/code&gt; is arguably&#xA;a better choice:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl run -it --rm --restart=Never busybox --image=alpine ash&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;You don&amp;rsquo;t need to remember that alpine uses &lt;code&gt;ash&lt;/code&gt; by default, &lt;code&gt;sh&lt;/code&gt; works as&#xA;well. In fact:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;/ # ls -al /bin | grep sh&#xA;lrwxrwxrwx    1 root     root            12 Dec  5 12:17 ash -&amp;gt; /bin/busybox&#xA;lrwxrwxrwx    1 root     root            12 Dec  5 12:17 fdflush -&amp;gt; /bin/busybox&#xA;lrwxrwxrwx    1 root     root            12 Dec  5 12:17 sh -&amp;gt; /bin/busybox&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Then I could do, for example, &lt;code&gt;apk update&lt;/code&gt; + &lt;code&gt;apk add mtr nmap&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Alpine is marvellous for this because &lt;code&gt;apk&lt;/code&gt; is very lightweight (and, hence,&#xA;fast!) and the available packages from upstream are quite comprehensive.&lt;/p&gt;&#xA;&lt;h2 id=&#34;run-commands-in-an-already-existing-pod&#34;&gt;&#xA;  Run commands in an already existing pod&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2024/12/kubernetes-debugging-services/#run-commands-in-an-already-existing-pod&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl exec &amp;lt;POD-NAME&amp;gt; -c &amp;lt;CONTAINER-NAME&amp;gt; -- &amp;lt;COMMAND&amp;gt;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&amp;hellip;however you cannot choose a custom image this way. You&amp;rsquo;re stuck with whatever&#xA;the pod is running.&lt;/p&gt;&#xA;&lt;p&gt;The way around this is with the use of ephemeral debug containers. Newer&#xA;versions of kubernetes have the &lt;code&gt;kubectl debug&lt;/code&gt; command:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% kubectl debug -it &amp;lt;POD-NAME&amp;gt; --image=alpine [--target=&amp;lt;POD-NAME&amp;gt;]&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Happy debugging!&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Kubernetes: debugging services&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Helm: JSON schema generation
      </title>
      <link>https://perrotta.dev/2024/11/helm-json-schema-generation/</link>
      <pubDate>Thu, 14 Nov 2024 15:05:49 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2024/11/helm-json-schema-generation/</guid>
      <description>&lt;p&gt;♠ Helm charts support the inclusion of a &lt;code&gt;values.schema.json&lt;/code&gt; file to validate&#xA;&lt;code&gt;values.yaml&lt;/code&gt;. Documentation: &lt;a href=&#34;https://helm.sh/docs/topics/charts/#schema-files&#34;&gt;https://helm.sh/docs/topics/charts/#schema-files&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;A JSON schema is akin to defining the structure of and type-annotating a JSON&#xA;file. It helps to &amp;ldquo;shift left&amp;rdquo; the lifecycle of your helm releases.&lt;/p&gt;&#xA;&lt;p&gt;For a pre-existing &lt;code&gt;values.yaml&lt;/code&gt; file, it may be quite tedious to generate the&#xA;schema from scratch, by hand.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Can we automate this process?&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Of course we can.&lt;/p&gt;&#xA;&lt;p&gt;One possibility is to use GenAI / LLMs. This is out of scope of this post.&lt;/p&gt;&#xA;&lt;p&gt;Another possibility is to leverage a helm plug-in to do so.&lt;/p&gt;&#xA;&lt;p&gt;I had a good experience with &lt;a href=&#34;https://github.com/karuppiah7890/helm-schema-gen&#34;&gt;https://github.com/karuppiah7890/helm-schema-gen&lt;/a&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;helm plugin install https://github.com/karuppiah7890/helm-schema-gen.git&#xA;cd path/to/helm/chart&#xA;helm schema-gen values.yaml | tee values.schema.json&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Afterwards, test the generated schema with:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;helm lint path/to/helm/chart&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Usually I need to do some follow-up edits to the generated file, including:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;add string enum types for a tighter validation of strings&lt;/li&gt;&#xA;&lt;li&gt;mark certain fields as required&lt;/li&gt;&#xA;&lt;li&gt;mark certain fields with &lt;code&gt;additionalProperties: &amp;quot;false&amp;quot;&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;augment &lt;code&gt;[&amp;quot;null&amp;quot;]&lt;/code&gt; to &lt;code&gt;[&amp;quot;null&amp;quot;, &amp;quot;string&amp;quot;]&lt;/code&gt; for keys that do not have a&#xA;default value&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;It&amp;rsquo;s worth to observe that, at the time of this publication, the following&#xA;notice is present in the repository of the plug-in:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;This repository has been archived by the owner on Aug 31, 2021. It is now&#xA;read-only.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Nonetheless, it still works well.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Helm: JSON schema generation&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>★ Helm: enforce the directory name matches the chart name
      </title>
      <link>https://perrotta.dev/2024/11/helm-enforce-the-directory-name-matches-the-chart-name/</link>
      <pubDate>Tue, 12 Nov 2024 11:13:06 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>bestof</category>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2024/11/helm-enforce-the-directory-name-matches-the-chart-name/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Problem statement&lt;/strong&gt;: Given a helm chart called &lt;code&gt;foo&lt;/code&gt;, enforce that its &lt;code&gt;Chart.yaml&lt;/code&gt; file lives in a directory called &lt;code&gt;foo&lt;/code&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;https://perrotta.dev/2024/11/helm-enforce-the-directory-name-matches-the-chart-name/#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;background&#34;&gt;&#xA;  Background&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2024/11/helm-enforce-the-directory-name-matches-the-chart-name/#background&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;In 2016, this &lt;a href=&#34;https://github.com/helm/helm/pull/818/&#34;&gt;used to be&lt;/a&gt; the default behavior in Helm:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;fix(helm): produce error if package name is inconsistent&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;In 2018, this enforcement was &lt;a href=&#34;https://github.com/helm/helm/pull/4141&#34;&gt;removed&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;remove dirname constraint on helm package&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;We would like to reintroduce this requirement in our Helm charts codebase, as a best practice, to prevent chart name collisions.&lt;/p&gt;&#xA;&lt;p&gt;What would be the most native way to accomplish that?&lt;/p&gt;&#xA;&lt;p&gt;I would probably write a git pre-commit hook if there is no native way (e.g. via some &lt;code&gt;helm lint&lt;/code&gt; flag).&lt;/p&gt;&#xA;&lt;h2 id=&#34;solution&#34;&gt;&#xA;  Solution&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2024/11/helm-enforce-the-directory-name-matches-the-chart-name/#solution&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Use the following script with &lt;a href=&#34;https://pre-commit.com/&#34;&gt;pre-commit&lt;/a&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash&#xA;#&#xA;# Check that the directory name matches the chart name in Chart.yaml.&#xA;#&#xA;# Examples:&#xA;#   - foo/Chart.yaml with &amp;#34;name: hey-foo&amp;#34; fails the check.&#xA;#   - foo/Chart.yaml with &amp;#34;name: foo&amp;#34; passes the check.&#xA;#&#xA;# Usage: $0 [path/to/chart/Chart.yaml ...]&#xA;&#xA;for chart in &amp;#34;$@&amp;#34;; do&#xA;&#x9;dirname=&amp;#34;$(basename &amp;#34;$(dirname &amp;#34;$chart&amp;#34;)&amp;#34;)&amp;#34;&#xA;&#xA;&#x9;# Remove trailing slash.&#xA;&#x9;dirname=&amp;#34;${dirname%/}&amp;#34;&#xA;&#xA;&#x9;# Fetch chart name from Chart.yaml.&#xA;&#x9;chart_name=&amp;#34;$(yq e &amp;#39;.name&amp;#39; &amp;#34;$chart&amp;#34;)&amp;#34;&#xA;&#xA;&#x9;if [[ $dirname != &amp;#34;$chart_name&amp;#34; ]]; then&#xA;&#x9;&#x9;echo &amp;#34;error: directory name &amp;#39;${dirname}&amp;#39; does not match chart name &amp;#39;${chart_name}&amp;#39;&amp;#34;&#xA;&#x9;&#x9;exit 1&#xA;&#x9;fi&#xA;done&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h2 id=&#34;reference&#34;&gt;&#xA;  Reference&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2024/11/helm-enforce-the-directory-name-matches-the-chart-name/#reference&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;I asked and self-answered this question on &lt;a href=&#34;https://stackoverflow.com/questions/79166730/how-to-enforce-that-the-directory-name-must-match-the-chart-name/79180650#79180650&#34;&gt;Stack Overflow&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;&lt;em&gt;Why&lt;/em&gt;? For ease of management, simplicity, consistency &amp;amp; uniformity.&amp;#160;&lt;a href=&#34;https://perrotta.dev/2024/11/helm-enforce-the-directory-name-matches-the-chart-name/#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Helm: enforce the directory name matches the chart name&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/bestof/&#34;&gt;#bestof&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>kubectl: list all node pods
      </title>
      <link>https://perrotta.dev/2024/10/kubectl-list-all-node-pods/</link>
      <pubDate>Wed, 16 Oct 2024 12:26:24 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2024/10/kubectl-list-all-node-pods/</guid>
      <description>&lt;p&gt;♠ Recipe to list all pods that belong to a given node:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ kubectl get pod -o wide --field-selector spec.nodeName={node_name} -A&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;: &lt;a href=&#34;https://stackoverflow.com/questions/39231880/kubernetes-api-get-pods-on-specific-nodes&#34;&gt;https://stackoverflow.com/questions/39231880/kubernetes-api-get-pods-on-specific-nodes&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;This &lt;em&gt;ought to&lt;/em&gt; be easier to remember&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: kubectl: list all node pods&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>kubectl: force delete pods
      </title>
      <link>https://perrotta.dev/2024/10/kubectl-force-delete-pods/</link>
      <pubDate>Tue, 15 Oct 2024 13:14:40 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2024/10/kubectl-force-delete-pods/</guid>
      <description>&lt;p&gt;♠ Our installation of &lt;code&gt;traefik&lt;/code&gt; via &lt;code&gt;helm&lt;/code&gt; got stuck today in such a way that its&#xA;pods did not terminate, even with successive &lt;code&gt;kubectl delete&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;--force&lt;/code&gt; did not work either :mildshock:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;--grace-period=0&lt;/code&gt; (alongside &lt;code&gt;--force&lt;/code&gt;) did the trick:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;kubectl delete pod traefik-{...} --grace-period=0 --force --namespace kube-system&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;: &lt;a href=&#34;https://stackoverflow.com/questions/35453792/pods-stuck-in-terminating-status&#34;&gt;https://stackoverflow.com/questions/35453792/pods-stuck-in-terminating-status&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: kubectl: force delete pods&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>New APKBUILD: argocd
      </title>
      <link>https://perrotta.dev/2024/10/new-apkbuild-argocd/</link>
      <pubDate>Wed, 09 Oct 2024 23:03:32 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>alpine-linux</category>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2024/10/new-apkbuild-argocd/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://argo-cd.readthedocs.io/en/stable/&#34;&gt;ArgoCD&lt;/a&gt; is a widely used GitOps&#xA;software for Kubernetes Continuous Delivery (see&#xA;&lt;a href=&#34;https://github.com/argoproj/argo-cd/blob/master/USERS.md&#34;&gt;USERS.md&lt;/a&gt;).&lt;/p&gt;&#xA;&lt;p&gt;I am quite surprised no one bothered to create an Alpine Linux package for it.&lt;/p&gt;&#xA;&lt;p&gt;Until&amp;hellip;&lt;a href=&#34;https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/73305&#34;&gt;now&lt;/a&gt;,&#xA;by yours truly.&lt;/p&gt;&#xA;&lt;p&gt;This &lt;code&gt;APKBUILD&lt;/code&gt; took a bit longer to create than the usual.&#xA;There were a couple of issues with &lt;code&gt;-buildmode=pie&lt;/code&gt;, addressed with&#xA;&lt;code&gt;export CGO_ENABLED=1&lt;/code&gt; (via &lt;code&gt;make CGO_FLAG=1&lt;/code&gt;).&lt;/p&gt;&#xA;&lt;p&gt;Also, not every architecture is compatible with it. The following error message&#xA;appears in ARM builds:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;cannot use math.MaxInt64 (untyped int constant 9223372036854775807) as int value in argument to env.ParseNumFromEnv (overflows)&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Anyway, once it is merged upstream, enjoy!&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% doas apk add argocd&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: New APKBUILD: argocd&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/alpine-linux/&#34;&gt;#alpine-linux&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>helm: list recent chart upgrades
      </title>
      <link>https://perrotta.dev/2024/09/helm-list-recent-chart-upgrades/</link>
      <pubDate>Tue, 24 Sep 2024 14:21:20 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2024/09/helm-list-recent-chart-upgrades/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Problem statement&lt;/strong&gt;: Given a kubernetes cluster with many helm charts in&#xA;different namespaces, how to use &lt;code&gt;helm&lt;/code&gt; to query the list of the last recently&#xA;&amp;ldquo;touched&amp;rdquo; charts?&lt;/p&gt;&#xA;&lt;p&gt;By &amp;ldquo;touched&amp;rdquo; we mean either installed (via &lt;code&gt;helm install&lt;/code&gt;) or upgraded (via&#xA;&lt;code&gt;helm upgrade&lt;/code&gt;).&lt;/p&gt;&#xA;&lt;p&gt;Something like: &lt;code&gt;helm ls --all --sort-by updated&lt;/code&gt;. Using &lt;code&gt;kubectl&lt;/code&gt; directly&#xA;would also be OK.&lt;/p&gt;&#xA;&lt;h2 id=&#34;solution&#34;&gt;&#xA;  Solution&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2024/09/helm-list-recent-chart-upgrades/#solution&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;The brute force way:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;helm ls --max 99999 -A -o json | jq -r &amp;#39;.[] | &amp;#34;\(.updated)\t\(.name)&amp;#34;&amp;#39; | sort | tail | column -t&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;-A&lt;/code&gt; for all namespaces&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;--max 99999&lt;/code&gt; to &amp;ldquo;disable&amp;rdquo; &lt;a href=&#34;https://github.com/helm/helm/issues/3322&#34;&gt;paging&lt;/a&gt; (there&amp;rsquo;s no better way as of 2024-09-24)&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;jq&lt;/code&gt; to filter out on the &amp;ldquo;updated&amp;rdquo; field&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Note that a sample json entry looks like the following:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;{&#xA;  &amp;#34;name&amp;#34;: &amp;#34;hoth-cb7f8a327&amp;#34;,&#xA;  &amp;#34;namespace&amp;#34;: &amp;#34;default&amp;#34;,&#xA;  &amp;#34;revision&amp;#34;: &amp;#34;2&amp;#34;,&#xA;  &amp;#34;updated&amp;#34;: &amp;#34;2024-06-14 15:02:16.775174131 &amp;#43;0000 UTC&amp;#34;,&#xA;  &amp;#34;status&amp;#34;: &amp;#34;deployed&amp;#34;,&#xA;  &amp;#34;chart&amp;#34;: &amp;#34;hoth-0.1.0&amp;#34;,&#xA;  &amp;#34;app_version&amp;#34;: &amp;#34;1.0.0&amp;#34;&#xA;}&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Sample output of the aforementioned command:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;2024-09-24  10:56:15.655674586  &amp;#43;0000  UTC  geonosis&#xA;2024-09-24  10:56:15.731630075  &amp;#43;0000  UTC  coruscant&#xA;2024-09-24  11:01:35.156973247  &amp;#43;0000  UTC  endor&#xA;2024-09-24  11:02:30.314014351  &amp;#43;0000  UTC  hoth&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: helm: list recent chart upgrades&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Helm: list all installed charts in the cluster
      </title>
      <link>https://perrotta.dev/2024/08/helm-list-all-installed-charts-in-the-cluster/</link>
      <pubDate>Mon, 26 Aug 2024 17:57:07 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2024/08/helm-list-all-installed-charts-in-the-cluster/</guid>
      <description>&lt;p&gt;♠ In theory this ought to be really easy:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;$ helm ls -h&#xA;[...]&#xA;  -a, --all                  show all releases without any filter applied&#xA;  -A, --all-namespaces       list releases across all namespaces&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;So&amp;hellip;one of these should work, right?&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;helm ls -a&#xA;helm ls -A&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;I have a &lt;code&gt;sms-service&lt;/code&gt; running in the cluster in the &lt;code&gt;default&lt;/code&gt; namespace. If I&#xA;&lt;code&gt;grep&lt;/code&gt; any of the previous commands to &lt;code&gt;sms&lt;/code&gt;, it does not show up though.&lt;/p&gt;&#xA;&lt;p&gt;Then&amp;hellip;maybe &lt;code&gt;helm ls -a -A&lt;/code&gt;? Nope.&lt;/p&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s what works:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;$ helm ls -a --max 9999 | grep sms&#xA;sms-service                  &#x9;default  &#x9;356     &#x9;2024-08-26 14:09:50.705277885 &amp;#43;0000 UTC&#x9;deployed&#x9;sms-v0.1.0&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Kinda ridiculous, eh?&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt; -m, --max int              maximum number of releases to fetch (default 256)&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;From &lt;a href=&#34;https://helm.sh/docs/helm/helm_list/&#34;&gt;https://helm.sh/docs/helm/helm_list/&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;By default, up to 256 items may be returned. To limit this, use the &amp;lsquo;&amp;ndash;max&amp;rsquo;&#xA;flag. Setting &amp;lsquo;&amp;ndash;max&amp;rsquo; to 0 will not return all results. Rather, it will return&#xA;the server&amp;rsquo;s default, which may be much higher than 256. Pairing the &amp;lsquo;&amp;ndash;max&amp;rsquo;&#xA;flag with the &amp;lsquo;&amp;ndash;offset&amp;rsquo; flag allows you to page through results.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Therefore I settle with this form:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;$ helm ls -a -A --max 9999 | grep sms&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Happy helming.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Helm: list all installed charts in the cluster&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>kubectl: print all secret values in plain text
      </title>
      <link>https://perrotta.dev/2024/08/kubectl-print-all-secret-values-in-plain-text/</link>
      <pubDate>Tue, 06 Aug 2024 13:37:31 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <guid>https://perrotta.dev/2024/08/kubectl-print-all-secret-values-in-plain-text/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Problem statement&lt;/strong&gt;: Given a &lt;a href=&#34;https://kubernetes.io/docs/concepts/configuration/secret/&#34;&gt;kubernetes&#xA;secret&lt;/a&gt; with more&#xA;than one key-value pair, print &lt;strong&gt;all&lt;/strong&gt; of them in plain text (i.e.&#xA;base64-decoded in this context).&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Edit(2024-08-08)&lt;/strong&gt;: It turns out I made a mistake. The effect of the post&#xA;below is akin to &lt;code&gt;kubectl get [...] -o yaml&lt;/code&gt;. I&amp;rsquo;ll keep the post for&#xA;bookkeeping purposes anyway.&lt;/p&gt;&#xA;&lt;p&gt;Basically, something like:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;kubectl cat secret -n infra-services my-cool-secret&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Except that there is no &lt;code&gt;kubectl cat&lt;/code&gt;, what a shame. It would be really great if&#xA;we had it, for consistency with &lt;code&gt;systemctl cat&lt;/code&gt;. Oh well.&lt;/p&gt;&#xA;&lt;p&gt;There is &lt;code&gt;kubectl edit&lt;/code&gt; though – amen, just like &lt;code&gt;systemctl edit&lt;/code&gt;. This will&#xA;open your &lt;code&gt;$EDITOR&lt;/code&gt;. It&amp;rsquo;s often inconvenient to copy text to the clipboard from&#xA;your terminal-based editor though, due to intricacies of different terminals,&#xA;shells, terminal multiplexers, and OSC-52, therefore I don&amp;rsquo;t deem this as an&#xA;acceptable solution.&lt;/p&gt;&#xA;&lt;p&gt;There is a simple trick though:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;EDITOR=cat kubectl edit secret -n infra-services my-cool-secret&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&amp;hellip;which works exactly as you would expect &lt;code&gt;kubectl cat&lt;/code&gt; to behave. It displays&#xA;a warning at the end:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;Edit cancelled, no changes made.&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&amp;hellip;however it can be duly ignored.&lt;/p&gt;&#xA;&lt;p&gt;If we didn&amp;rsquo;t have this trick, we could have:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;written a bloated tool in golang to pipe to e.g. &lt;a href=&#34;https://github.com/ashleyschuett/kubernetes-secret-decode&#34;&gt;https://github.com/ashleyschuett/kubernetes-secret-decode&lt;/a&gt; (&lt;a href=&#34;https://itnext.io/secrets-in-plain-text-13a98f54ef97&#34;&gt;via&lt;/a&gt;)&lt;/li&gt;&#xA;&lt;li&gt;used the &lt;code&gt;kubectl edit&lt;/code&gt; trick above, with a decent terminal and OSC-52 setup&lt;/li&gt;&#xA;&lt;li&gt;used &lt;code&gt;jq&lt;/code&gt; with its &lt;code&gt;map&lt;/code&gt; and &lt;code&gt;base64d&lt;/code&gt; constructs (c.f. &lt;a href=&#34;https://stackoverflow.com/questions/50286066/kubernetes-kubectl-print-all-secrets&#34;&gt;Stack&#xA;Overflow&lt;/a&gt;)&lt;/li&gt;&#xA;&lt;li&gt;used &lt;code&gt;kubectl describe&lt;/code&gt; with &lt;code&gt;-o jsonpath&lt;/code&gt;, specifying every single field, one&#xA;by one (super tedious) c.f. &lt;a href=&#34;https://perrotta.dev/2024/07/kubectl-get-secret-with-jsonpath-and-add-a-newline/&#34;&gt;this previous post&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: kubectl: print all secret values in plain text&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
