Ever wish you could pass some label to an ArgoCD application and have every child resource pick up that label? This is probably of most interest to cluster operators who may not directly control the contents of manifests deployed to their clusters via ArgoCD, but need a way to enforce some standard labeling scheme.
The solution? A simple ArgoCD plugin, easier to setup than you might think.
An ArgoCD plugin needs to be configured as a sidecar container of the argocd-repo-server deployment, and the container needs to have a plugin.yaml file that describes what it should do.
apiVersion: argoproj.io/v1alpha1
kind: ConfigManagementPlugin
metadata:
  # This is the name by which the plugin will be registered to ArgoCD and must be unique within the Argo instance.
  name: argocd-labeler
spec:
  generate:
    command: [sh, c]
    args:
      - |
        if [[ ! -f "kustomization.yaml" && ! -f "kustomization.yml" ]]; then
          kustomize create --autodetect --recursive
        fi
        kustomize edit add label "my_app_id:$ARGOCD_ENV_MY_APP_ID"
        kustomize buildThis is a very simple plugin config. The full spec is explained in the docs.
Basically, we're running this plugin using an image that already contains kustomize, and then we're making use of kustomize's commonLabels to add a key:value pair, where the value comes from the environment.
Additional details:
- When this plugin gets invoked, the context is the repo/location which contains the application manifests (basically the spec.sourceof the Application). Think of this as the working directory where we're calling kustomize.
- The $ARGOCD_ENV_MY_APP_IDvariable is going to come from an input on the Application manifest.
- After making sure kustomizeis properly set up, we callkustomize buildto write the updated manifests to stdout.
- If you don't want to include things like Service and Deployment selectors getting labeled, you can add the --without-selectorflag to thekustomize edit add labelcommand.
Use this as a kustomize strategic merge patch to add the plugin sidecar container to the argocd-repo-server:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: argocd-repo-server
spec:
  template:
    spec:
      containers:
        - name: argocd-labeler
          image: registry.k8s.io/kustomize/kustomize:v5.4.3
          command:
            - /var/run/argocd/argocd-cmp/server # starts a gRPC server that will receive calls to the plugin and execute the command defined in the plugin.yaml
          securityContext:
            runAsNonRoot: true
            runAsUser: 999 # must use 999 for the UID
          volumeMounts:
            - mountPath: /var/run/argocd
              name: var-files # this one should already be defined as a volume by a standard argocd-repo-server deployment
            - mountPath: /home/argocd/cmp-server/plugins
              name: plugins
            - mountPath: /home/argocd/cmp-server/config/plugin.yaml # must be mounted here and must be named plugin.yaml
              subPath: plugin.yaml
              name: argocd-labeler-plugin
            - mountPath: /tmp
              name: cmp-tmp
        volumes:
          - configMap:
              name: argocd-labeler-plugin
            name: argocd-labeler-plugin
          - emptyDir: {}
            name: cmp-tmpNote
This assumes you have put the plugin.yaml in a ConfigMap named argocd-labeler-plugin in the ArgoCD namespace, and that ConfigMap has a data key named plugin.yaml, e.g.
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-labeler-plugin
data:
  plugin.yaml: |
    # contents from aboveapiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: foo-app
  namespace: argocd
spec:
  destination:
    namespace: default
    server: https://kubernetes.default.svc
  project: default
  source:
    repoURL: [email protected]:org/my-gitops-repo.git
    path: foo
    plugin:
      name: argocd-labeler
      env:
        - name: MY_APP_ID
          value: "foo-123"In this simple application we call our argocd-labeler plugin and pass a value for an environment variable.  Remember the $ARGOCD_ENV_MY_APP_ID variable we referenced in the plugin.yaml?  This is where it comes from.  ArgoCD will set the variables you define in your app's spec.source.plugin.env in the plugin environment, prefixed with ARGOCD_ENV_.
Now when my app syncs, all child resources will have a new label added like my_app_id:foo-123 ✅.