Prevent ArgoCD from syncing a single ressource - kubernetes

I have an ArgoCD App which is generating symbols
apiVersion: v1
kind: Secret
metadata:
labels:
{{- include "myapp.labels" . | nindent 4 }}
annotations:
helm.sh/hook: pre-install,post-delete
name: {{ include "myapp.fullname" . }}
type: Opaque
data:
{{- if .Values.password }}
password: {{ .Values.password | b64enc | quote }}
{{- else }}
password: {{ randAlphaNum 10 | b64enc | quote }}
{{- end }}
A second service is adding additional values to the secrets later. I don't want this secrets in my chart
Now when something is changed on the app, the secrets are recreated.
How can i change this behaviour in ArgoCD?

Add annotation to that particular object and it should work
apiVersion: v1
kind: Secret
metadata:
labels:
{{- include "myapp.labels" . | nindent 4 }}
annotations:
helm.sh/hook: pre-install,post-delete
argocd.argoproj.io/sync-options: Prune=false
Some Sync Options can defined as annotations in a specific resource. Most of the Sync Options are configured in the Application resource spec.syncPolicy.syncOptions attribute. Multiple Sync Options which are configured with the argocd.argoproj.io/sync-options annotation can be concatenated with a , in the annotation value; white spaces will be trimmed.
no-prune-resources
Or if you don’t want to apply auto sync on this object then you can try ApplyOutOfSyncOnly=false
selective-sync

Related

Helm lookup always empty

While deploying a Kubernetes application, I want to check if a resource is already present. If so it shall not be rendered. To archive this behaviour the lookup function of helm is used. As it seems is always empty while deploying (no dry-run). Any ideas what I am doing wrong?
---
{{- if not (lookup "v1" "ServiceAccount" "my-namespace" "my-sa") }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .Chart.Name }}-{{ .Values.environment }}
namespace: {{ .Values.namespace }}
labels:
app: {{ $.Chart.Name }}
environment: {{ .Values.environment }}
annotations:
"helm.sh/resource-policy": keep
iam.gke.io/gcp-service-account: "{{ .Chart.Name }}-{{ .Values.environment }}#{{ .Values.gcpProjectId }}.iam.gserviceaccount.com"
{{- end }}
running the corresponding kubectl command return the expected service account
kubectl get ServiceAccount my-sa -n my-namespace lists the expected service account
helm version: 3.5.4
i think you cannot use this if-statement to validate what you want.
the lookup function returns a list of objects that were found by your lookup. so, if you want to validate that there are no serviceaccounts with the properties you specified, you should check if the returned list is empty.
test something like
---
{{ if eq (len (lookup "v1" "ServiceAccount" "my-namespace" "my-sa")) 0 }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .Chart.Name }}-{{ .Values.environment }}
namespace: {{ .Values.namespace }}
labels:
app: {{ $.Chart.Name }}
environment: {{ .Values.environment }}
annotations:
"helm.sh/resource-policy": keep
iam.gke.io/gcp-service-account: "{{ .Chart.Name }}-{{ .Values.environment }}#{{ .Values.gcpProjectId }}.iam.gserviceaccount.com"
{{- end }}
see: https://helm.sh/docs/chart_template_guide/functions_and_pipelines/#using-the-lookup-function

Helm create secret from env file

Kubectl provides a nice way to convert environment variable files into secrets using:
$ kubectl create secret generic my-env-list --from-env-file=envfile
Is there any way to achieve this in Helm? I tried the below snippet but the result was quite different:
kind: Secret
metadata:
name: my-env-list
data:
{{ .Files.Get "envfile" | b64enc }}
It appears kubectl just does the simple thing and only splits on a single = character so the Helm way would be to replicate that behavior (helm has regexSplit which will suffice for our purposes):
apiVersion: v1
kind: Secret
data:
{{ range .Files.Lines "envfile" }}
{{ if . }}
{{ $parts := regexSplit "=" . 2 }}
{{ index $parts 0 }}: {{ index $parts 1 | b64enc }}
{{ end }}
{{ end }}
that {{ if . }} is because .Files.Lines returned an empty string which of course doesn't comply with the pattern
Be aware that kubectl's version accepts barewords looked up from the environment which helm has no support for doing, so if your envfile is formatted like that, this specific implementation will fail
I would like to use env files but it seems to be helm doesn't support that yet.
Instead of using an env file you could use a yaml file.
I mean to convert from this env file
#envfile
MYENV1=VALUE1
MYENV2=VALUE2
to this yaml file (verify the yaml format, always it should be an empty space after the colon)
#envfile.yaml
MYENV1: VALUE1
MYENV2: VALUE2
After this, you should move the envfile.yaml generated in the root folder of your helm chart (same level of values yaml files)
You have to set up your secret.yaml in this way:
apiVersion: v1
kind: Secret
metadata:
name: my-secret
annotations:
checksum/config: {{ (tpl (.Files.Glob "envfile.yaml").AsSecrets . ) | sha256sum }}
type: Opaque
data:
{{- $v := $.Files.Get "envfile.yaml" | fromYaml }}
{{- range $key, $val := $v }}
{{ $key | indent 2 }}: {{ $val | b64enc }}
{{- end}}
We are iterating in the data property the envfile.yaml generated and encoding the value to base64. The result secret will be the next:
kubectl get secret my-secret -o yaml
apiVersion: v1
data:
MYENV1: VkFMVUUx
MYENV2: VkFMVUUy
kind: Secret
metadata:
annotations:
checksum/config: 8365925e9f9cf07b2a2b7f2ad8525ff79837d67eb0d41bb64c410a382bc3fcbc
creationTimestamp: "2022-07-09T10:25:16Z"
labels:
app.kubernetes.io/managed-by: Helm
name: my-secret
resourceVersion: "645673"
uid: fc2b3722-e5ef-435e-85e0-57c63725bd8b
type: Opaque
Also, I'm using checksum/config annotation to update the secret object every time a value is updated.

Using helm to install clusterRole.yaml

I'm currently using kubectl create -f clusterRole.yaml , I was wondering if I can use helm to install it automatically with my chart.
I was looking at the helm documentation, and it used kubectl create -f for the clusterRole file. Is there any reason that this can't be done through helm? Is it because this concerns with access privilege issues?
As already mentioned in the comments, you can install your RBAC roles using your helm chart. As a matter of fact many of the helm charts do configure roles/clusterRoles at install. Here's an example of Elasticsearch helm chart which does configure Role and RoleBinding at install level:
{{- if .Values.rbac.create -}}
{{- $fullName := include "elasticsearch.uname" . -}}
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ $fullName | quote }}
labels:
heritage: {{ .Release.Service | quote }}
release: {{ .Release.Name | quote }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
app: {{ $fullName | quote }}
rules:
- apiGroups:
- extensions
resources:
- podsecuritypolicies
resourceNames:
{{- if eq .Values.podSecurityPolicy.name "" }}
- {{ $fullName | quote }}
{{- else }}
- {{ .Values.podSecurityPolicy.name | quote }}
{{- end }}
verbs:
- use
{{- end -}}
Another example with clusterRole can be found here.
To sum up, if you context allow you to install desired rbac or any other stuff with kubectl then basically you will be able to do so with helm.

Helm require value without using it

Is it possible to have a required .Value without using it in the template.
For example in my case I want to require to write a password for a subchart of mongodb but I won't use it on my templates so can I have something like bellow in a template:
{{- required 'You must set a mongodb password' .Values.mongodb.mongodbPassword | noPrint -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "cloud.fullname" . }}
labels:
{{- include "cloud.labels" . | nindent 4 }}
app.kubernetes.io/component: cloud
spec:
replicas: {{ .Values.cloud.minReplicaCount }}
selector:
....
And the result would be something like:
apiVersion: apps/v1
kind: Deployment
metadata:
name: blablablabla
...
Possibly the most direct way is to use sprig's fail function.
{{- if not .Values.mongodb.mongodbPassword -}}
{{- fail "You must set a mongodb password" -}}
{{- end -}}
Assigning the required expression to a variable (that you never use) will probably also have the desired effect.
{{- $unused := required "You must set a mongodb password" .Values.mongodb.mongodbPassword -}}
Yes, it is possible. Let's consider the below Values.yaml file:
Values.yaml:
mongodb:
mongodbPassword: "AbDEX***"
So, you want to generate the deployment file only if the password is set. You can do by using if-block of go-templating. If the length of the password field is greater than zero, the deployment yaml will be generated otherwise not.
{{- if .Values.mongodb.mongodbPassword}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "cloud.fullname" . }}
labels:
{{- include "cloud.labels" . | nindent 4 }}
app.kubernetes.io/component: cloud
spec:
replicas: {{ .Values.cloud.minReplicaCount }}
selector:
....
{{- end }}
Reference:
{{if pipeline}} T1 {{end}}
If the value of the pipeline is empty, no output is generated;
otherwise, T1 is executed. The empty values are false, 0, any nil pointer or
interface value, and any array, slice, map, or string of length zero.
Dot is unaffected.

Helm - how to call helper functions in a loop?

I'm trying to define n StatefulSets where n is the number of nodes required, set in values.yaml as nodeCount. I get an error that looks to be scope related, but I can't seem to get the scope sorted out. Am I missing something here?
The relevant content in my StatefulSet .yaml file:
{{ range $k, $v := until ( .Values.nodeCount | int) }}
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: {{ $.Release.Name }}
labels:
app: {{ $.Release.Name }}
chart: {{ template "myapp-on-kube.chart" . }} #here's my call to _helpers
release: {{ $.Release.Name }}
heritage: {{ $.Release.Service }}
The relevant content in _helpers.tpl:
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "myapp-on-kube.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
The error I get:
Error: render error in "myapp-on-kube/templates/statefulset.yaml": template: myapp-on-kube/templates/_helpers.tpl:31:25: executing "myapp-on-kube.chart" at <.Chart.Name>: can't evaluate field Chart in type int
Several of the Go templating constructs change the meaning of . to be the thing that's being looped over, and you need to use $ to refer to the initial value. Most of your template correctly refers to e.g. $.Release.Name, but when you invoke the helper template, it's using the current context rather than the root value. Change:
chart: {{ template "myapp-on-kube.chart" $ }}
(Note that the template as you have it will declare several StatefulSets all with the same name, which won't go well. I might create just one StatefulSet with replicas: {{ .Values.nodeCount }}.)