Config Management Plugins (CMPs) are Argo CD’s secret weapon for managing anything as Kubernetes manifests, not just Helm charts or Kustomize.
Here’s Argo CD in action, managing a simple Nginx deployment, but with a twist: we’re using a custom Go binary as a Config Management Plugin to generate the YAML.
# applications.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: nginx-custom-cmp
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/your-username/argo-cd-cmp-example.git
targetRevision: HEAD
path: nginx-app
plugin:
name: go-nginx-generator
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
The plugin block is where the magic happens. We tell Argo CD to use a plugin named go-nginx-generator. This name must match an entry in Argo CD’s plugin.yaml configuration.
# plugin.yaml (in Argo CD's config map)
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cm
namespace: argocd
data:
config.yaml: |
plugins:
- name: go-nginx-generator
generate:
# This is the command Argo CD will execute.
# It needs to output Kubernetes manifests to stdout.
command: ["/usr/local/bin/go-nginx-generator"]
# Arguments passed to the command.
# These come from the Application manifest's source.plugin.args.
args:
- "--replicas=3"
- "--image=nginx:1.21.6"
# Optional: If your plugin needs to detect the environment (e.g., diffing)
# diff:
# command: ["/usr/local/bin/go-nginx-generator"]
# args:
# - "--replicas=3"
# - "--image=nginx:1.21.6"
And here’s the Go program that go-nginx-generator points to. It simply prints a Kubernetes Deployment YAML to standard output.
// go-nginx-generator/main.go
package main
import (
"encoding/json"
"flag"
"fmt"
"os"
appv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func main() {
replicas := flag.Int("replicas", 1, "Number of replicas")
image := flag.String("image", "nginx:latest", "Container image")
flag.Parse()
deployment := appv1.Deployment{
TypeMeta: metav1.TypeMeta{
APIVersion: "apps/v1",
Kind: "Deployment",
},
ObjectMeta: metav1.ObjectMeta{
Name: "nginx-deployment",
},
Spec: appv1.DeploymentSpec{
Replicas: int32Ptr(*replicas),
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "nginx",
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": "nginx",
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "nginx",
Image: *image,
Ports: []corev1.ContainerPort{
{ContainerPort: 80},
},
},
},
},
},
},
}
// Argo CD expects JSON output for structured arguments, but YAML for manifests.
// We're just printing YAML here.
// For structured arguments, you'd typically encode a JSON object.
// Example:
// argsMap := make(map[string]string)
// for _, arg := range os.Args[1:] {
// parts := strings.SplitN(arg, "=", 2)
// if len(parts) == 2 {
// argsMap[parts[0]] = parts[1]
// }
// }
// json.NewEncoder(os.Stdout).Encode(argsMap)
// For manifest generation, output YAML.
// A real-world scenario might use a YAML marshaler.
// For simplicity, we'll just hardcode a basic YAML structure.
fmt.Printf(`apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: %d
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: %s
ports:
- containerPort: 80
`, *replicas, *image)
}
func int32Ptr(i int32) *int32 { return &i }
This Go program is compiled into an executable and placed in the Git repository alongside the applications.yaml and plugin.yaml. Argo CD will download the repository, find the specified plugin executable, and run it. The output of this executable is then treated by Argo CD as the Kubernetes manifests to be applied to the cluster.
The core problem CMPs solve is extending Argo CD’s understanding of "manifests" beyond its built-in support for Helm and Kustomize. If you have a custom templating engine, a DSL that compiles to YAML, or even a script that dynamically generates resources based on external data, CMPs let you integrate it seamlessly. The key is that your plugin must accept arguments (if any) and output valid Kubernetes YAML manifests to standard output.
You can pass arguments from your Application spec to your plugin. In the applications.yaml example, the source.plugin.args field is where you’d put them:
# ...
plugin:
name: go-nginx-generator
# Pass arguments to the plugin command
args:
- "--replicas=5"
- "--image=nginx:latest"
# ...
These arguments are then passed directly to your plugin’s command. Your plugin code needs to be written to parse these arguments (e.g., using flag in Go, argparse in Python, or getopts in shell).
The plugin.yaml configuration in the argocd-cm ConfigMap is crucial. It maps a logical plugin name (used in the Application spec) to the actual command Argo CD should execute. You can also specify args here that are always passed to the plugin, in addition to the ones from the Application spec.
The most surprising thing about Config Management Plugins is that they don’t just generate manifests; they can also be used for advanced diffing. By defining the diff section in plugin.yaml, you can provide a command that Argo CD runs during diff operations. This allows your custom tool to understand how to compare your generated resources, enabling accurate diffs even when your plugin produces complex or dynamically generated configurations.
The generate command is executed when Argo CD needs to render the manifests to be applied. The diff command is executed when Argo CD needs to show you the differences between the desired state in your repository and the current state in the cluster. If you only define generate, Argo CD will fall back to a default diffing mechanism, which might not accurately represent the changes made by your plugin.
When a plugin fails to execute (e.g., the command isn’t found, it exits with a non-zero status, or it produces invalid YAML), Argo CD will report an error. The error message will typically indicate which plugin failed and provide the exit code and stderr output from the plugin process. This is invaluable for debugging your plugin logic.
The next concept you’ll likely encounter is managing secrets with your CMPs, which often involves integrating with external secret management systems or using Argo CD’s native secret management capabilities.