I am struggling with how to take input for my gitops repo. I am providing a managed kubernetes configuration, that provides a developer experience.
Now the teams that need to utilize it, will enable the config with something like this:
# Consumer managed yaml, not part of my repo
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: asterix
namespace: argo
spec:
project: default
source:
repoURL: 'git@github.com:repo/devex-argo.git'
targetRevision: feat/feast
path: 'environments/devex-stable'
destination:
server: 'https://kubernetes.default.svc'
namespace: argo
syncPolicy:
automated:
prune: true
selfHeal: true
The team will not be capable of adding patches themselves, or maintaining overlays. Also, the scope is too big for me to add an overlay per cluster, and also in my mind defeats the purpose.
The environemnts/devex-stable looks like this:
# environemnts/devex-stable
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../services/feast/
and my feast sample looks like this:
# services/feast/kustomize
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace-feast.yaml
- argocd-feast.yaml
- ingress-feast.yaml
the argocd-feast is simply an argo/application that deploys the helm chart. Additionally i deploy an ingressroute like this:
# services/feast/ingress-feast.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: feast.DOMAIN
namespace: feast
annotations:
kubernetes.io/ingress.class: traefik
spec:
entryPoints:
- websecure
- web
routes:
- kind: Rule
match: Host(feast.DOMAIN) && PathPrefix(/)
services:
- name: feast-feature-server
port: 6566
tls:
certResolver: le
domains:
- main: feast.DOMAIN
and replace the DOMAIN string with the user supplied value.
I tried using kustomize, but seems to get stuck on it. My most promising soloution was a patch like this:
# services/feast/kustomize
...
replacements:
- source:
kind: IngressRoute
fieldPath: metadata.annotations.domain
targets:
- select:
kind: IngressRoute
fieldPaths:
- metadata.name
- spec.routes.0.match
- spec.tls.domains.0.main
options:
delimiter: '.'
index: 1
and adding this to the consumer yaml:
# consumer managed yaml
...
kustomize:
commonAnnotations:
domain: "MyCustomDomain.com" # Substitute this with the dynamic value as needed
commonAnnotationsEnvsubst: true
But it seems the patch from my own repo runs before the annotation is changed to the customer override.
I know i can achieve this with helm, but i would prefer not to create helm charts with only 1 ressource in them for the sake of templating (unless that is the only way of course).
Does anyone have any ideas on this?
So I finally made a solution that works, it took a while so I will document my findings in this answer
Kustomize itself does not support environment variables, so I made a simple argocd plugin that runs envsubst for all variables prefixed "ARGOCD_ENV" before it builds the kustomize projects.
For reference, when you set a env var as input from the user side, it will automatically get the ARGOCD_ENV prefix.
Argocd was deployed with these overrides:
#values.yaml
repoServer:
volumes:
- name: argocd-cmp-cm
configMap:
name: argocd-cmp-cm
- name: cmp-tmp
emptyDir: {}
extraContainers:
- name: envsubst
command:
- "/var/run/argocd/argocd-cmp-server"
image: bitnami/kubectl
securityContext:
runAsNonRoot: true
runAsUser: 999
volumeMounts:
- mountPath: /var/run/argocd
name: var-files
- mountPath: /home/argocd/cmp-server/plugins
name: plugins
# Remove this volumeMount if you've chosen to bake the config file into the sidecar image.
- mountPath: /home/argocd/cmp-server/config/plugin.yaml
subPath: envsubst.yaml
name: argocd-cmp-cm
# Starting with v2.4, do NOT mount the same tmp volume as the repo-server container. The filesystem separation helps
# mitigate path traversal attacks.
- mountPath: /tmp
name: cmp-tmp
configs:
cmp:
create: true
plugins:
envsubst:
init:
command: ["/bin/sh", "-c"]
args: [
"for file in $(find $(git rev-parse --show-toplevel) -type f); do envsubst $(compgen -v | grep ARGOCD_ENV | sed 's/^/\$/') < $file > $file.tmp && mv $file.tmp $file; done"
]
generate:
command: ["/bin/sh", "-c"]
args: [
"kubectl kustomize ."
]
Now in my kustomize files, i add my ingressroute as:
#ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: feast.${ARGOCD_ENV_DOMAIN}
namespace: feast
annotations:
kubernetes.io/ingress.class: traefik
spec:
entryPoints:
- websecure
- web
routes:
- kind: Rule
match: Host(feast.${ARGOCD_ENV_DOMAIN}) && PathPrefix(/)
services:
- name: feast-feature-server
port: 6566
tls:
certResolver: le
domains:
- main: feast.${ARGOCD_ENV_DOMAIN}
Finally, I deploy the repo to my cluster like this:
#argoApplication.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: asterix
namespace: argo
spec:
project: default
source:
repoURL: 'git@github.com:repo/devex-argo.git'
targetRevision: feat/feast
path: 'environments/devex-stable'
plugin:
name: envsubst
# my custom variables
env:
- name: DOMAIN
value: mydomain.com
destination:
server: 'https://kubernetes.default.svc'
namespace: argo
syncPolicy:
automated:
prune: true
selfHeal: true
I tried to do this natively with both helm and Kustomize, but hit a wall in both cases.
Kustomize is not meant for templating in its native form, though possible with plugins it seems: https://github.com/kubernetes-sigs/kustomize/issues/4120#issuecomment-912037164
Another approach is to deploy the cluster setup as a helm charts with sub charts. The issue I ran into here has to do with multi namespaces, as helm deploys all sub charts into the same namespace as the main chart. Some charts have the option to override the namespace, but there are no guarantees all helm charts has been written with that option. In my case, this is a dealbreaker as I deploy around 15-20 services, that should live in separate namespaces