Using CDK8S, YTT or Gomplate with ArgoCD Through "Config Management Plugins"
Tags: kubernetes, argocd, gomplate, cdk8s
Published: 2024-01-28 16:41:58

## Problem

Are you looking to use other yaml templating tools with Argocd, like [ytt](https://carvel.dev/ytt/), [cdk8s](https://cdk8s.io/) or [gomplate](https://docs.gomplate.ca/) to template your yaml files? Or do you need to customize Helm command that is used in ArgoCD? Perhaps with different parameters? (e.g.: passing `- post-renderer` to helm command)
While Argocd offers out-of-the-box support for Helm charts and Kustomize manifests, if you require more functionality, you need to somehow extend Argocd functionalities.

## Solution

Argocd offers a solution to this problem. And that is "[Config Management Plugins](https://argo-cd.readthedocs.io/en/stable/operator-manual/config-management-plugins/)". By writing plugins, you can install your own tool(s) and define your own command to build and template Kubernetes manifests. However, it's easier said than done.

So in this article, we are going to create our own plugin and use "gomplate" for rendering the manifests. Although you can of course use different tools.

## Installation

I'm using helm to install Argocd. So most of the configurations will be in the helm values.

First I create the namespace in which I'd like to deploy my ArgoCD resources:

```bash
kubectl create namespace argocd
```

This namespace will used for ArgoCD resources and your application will be deployed in its own namespace.

```bash
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update
helm install argocd argo/argo-cd \
    --namespace=argocd \
    --create-namespace \
    --version=5.46.7 \
    --dependency-update \
    --values=argo-cd/values.yaml
```

Here is the content of `values.yaml` file:

```yaml
configs:
  # -- Plugin yaml files to be added to argocd-cmp-cm
  cmp:
    create: true
    plugins:
      gomplate:
        generate:
          command: [sh, -c]
          args:
            # here we define our custom command to render the yaml file
            - cat ./* | gomplate -d ip=https://ipinfo.io

repoServer:
  extraContainers:
    - name: gomplate
      command:
        - "/var/run/argocd/argocd-cmp-server"
      image: alpine:3
      securityContext:
        runAsNonRoot: true
        runAsUser: 999
      volumeMounts:
        - mountPath: /usr/bin
          name: cmp-tmp
        - mountPath: /var/run/argocd
          name: var-files
        - mountPath: /home/argocd/cmp-server/plugins
          name: plugins
        - mountPath: /home/argocd/cmp-server/config/plugin.yaml
          name: argocd-cmp-cm
          subPath: gomplate.yaml
  initContainers:
    - name: install-gomplate
      image: alpine:3
      command: ["sh", "-c"]
      args:
        # here we install gomplate binary
        - apk add --no-cache gomplate
      volumeMounts:
        - mountPath: /usr/bin
          name: cmp-tmp

  # -- Additional volumes to the repo server pod
  volumes:
    # This is the config map all the CMPs are defined
    - name: argocd-cmp-cm
      configMap:
        name: argocd-cmp-cm
    - name: cmp-tmp
      emptyDir: {}
```

[ArgoCD CRDs](https://artifacthub.io/packages/helm/argo/argo-cd#custom-resource-definitions) will be installed out of the box. If you would like to install them separately, you can pass `--set crds.install=false` to the installation command.

Perfect! Now we have ArgoCD with our `Config Management Plugins` called `gomplate` installed.

## Usage

Now we can use this plugin in our `Application`/`Applicationset` manifest to render the yaml files. Here is an example:

```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-awesome-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/ali-de7/argocd-cmp.git
    targetRevision: main
    path: my-awesome-app
    plugin:
      name: gomplate
      env:
        - name: CUSTOM_ENV_VARIABLE # argocd prepends the env vars with ARGOCD_ENV_
          value: bar
  destination:
    server: https://kubernetes.default.svc
    namespace: my-awesome-app
```

We apply this manifest with the following command:

```bash
kubectl apply -f application.yaml
```

Now let's use Gomplate syntax in our awesome application and generate Kubernetes secrets through Gomplate datesets:

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: my-secret
  namespace: {{ getenv "ARGOCD_APP_NAMESPACE" "default" }}
type: Opaque
data:
  password: {{ (ds "ip").country | base64.Encode }}
  foo: {{ getenv "ARGOCD_ENV_CUSTOM_ENV_VARIABLE" | base64.Encode }} # argocd prepends the env vars with ARGOCD_ENV_
```

Super! Now we just need to sync our application in ArgoCD.

## Final notes

You can find the example in the following Github repo: 

{% github https://github.com/ali-de7/argocd-cmp %}

Fill free to check it out.