Using ServerSideApply with ArgoCD

- By: Thomas Jungbauer ( Lastmod: 2024-02-03 ) - 3 min read

If it is not in GitOps, it does not exist“ - However, managing objects partially only by Gitops was always an issue, since ArgoCD would like to manage the whole object. For example, when you tried to work with node labels and would like to manage them via Gitops, you would need to put the whole node object into ArgoCD. This is impractical since the node object is very complex and typically managed by the cluster. There were 3rd party solutions (like the patch operator), that helped with this issue.

However, with the Kubernetes feature Server-Side Apply this problem is solved. Read further to see a working example of this feature.

What is Server-Side Apply (SSA)


Server-Side Apply helps users and controllers manage their resources through declarative configurations. Clients can create and modify their objects declaratively by sending their fully specified intent.

A fully specified intent is a partial object that only includes the fields and values for which the user has an opinion. That intent either creates a new object or is combined, by the server, with the existing object.


In other words: you can send a snippet of an object to the cluster and the cluster will eventually combine everything on the server and not validate on the client side first. All you need is a way to identify the object. Usually, the name and maybe the namespace too.

SSA and ArgoCD

When it comes to GitOps the implementation of SSA is quite new. However, it is important to note, that (managed field) conflicts are currently not handled by ArgoCD. Instead, ArgoCD forces a change and overrides everything, even if the field is managed by somebody else. This might be improved in the future. Nevertheless …​ let’s test the feature.

Prerequisites

The support of the Server-Side Apply feature is currently available in the latest version of ArgoCD. This means, that the channel of the openshift-gitops operator must be changed to "latest", which will deploy openshift-gitops version 1.6

A new stable version will arrive soon. :)

Node Labelling Chart

In this example, I would like to use a Helm chart that will try to set two different labels on 2 nodes. This is a very easy example to demonstrate the feature.

The values for this chart are straightforward: per node, a list of custom labels is defined.

helper-server-side-apply:
  nodes: (1)
    - name: ip-10-0-233-237.us-west-1.compute.internal (2)
      enabled: true
      custom_labels: (3)
        environment: 'Production'
        gpu: false
    - name: ip-10-0-193-67.us-west-1.compute.internal
      enabled: true
      custom_labels:
        environment: 'Test'
        gpu: true
1List of nodes
2Node name as OpenShift knows the node (oc get nodes)
3List of labels that should be added to the node: here environment and gpu
The Chart is using a sub-chart called helper-server-side-apply. The source can be found at the Helm Repository

The output of this Helm Chart will be the following:

# Source: node-labels/charts/helper-server-side-apply/templates/node.yaml
kind: Node
apiVersion: v1
metadata:
  name: "ip-10-0-233-237.us-west-1.compute.internal" (1)
  labels:
    gitops.ownedBy: openshift-gitops
    helm.sh/chart: helper-server-side-apply-1.0.3
    app.kubernetes.io/name: helper-server-side-apply
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/managed-by: Helm
    environment: "Production" (2)
    gpu: "false" (3)
---
# Source: node-labels/charts/helper-server-side-apply/templates/node.yaml
kind: Node
apiVersion: v1
metadata:
  name: "ip-10-0-193-67.us-west-1.compute.internal"
  labels:
    gitops.ownedBy: openshift-gitops
    helm.sh/chart: helper-server-side-apply-1.0.3
    app.kubernetes.io/name: helper-server-side-apply
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/managed-by: Helm
    environment: "Test"
    gpu: "true"
1The name of the node and our identifier
2The first label we set
3The second label we set
This is not a full definition of a Node object. The only things defined are the node name and the labels. (Besides the customer labels we would like to add, some default labels are added automatically.)

ArgoCD Application

So we have a Helm chart in Git. Perfect, but to automate everything with Gitops we need to create the object Application. For example the following:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: node-labelling
  namespace: openshift-gitops
spec:
  destination:
    namespace: default
    server: 'https://kubernetes.default.svc'
  info:
    - name: Description
      value: Deploy Node Labels
  project: default
  source:
    helm:
      valueFiles:
        - values.yaml
    path: clusters/management-cluster/node-configuration (1)
    repoURL: 'https://github.com/tjungbauer/openshift-clusterconfig-gitops'
    targetRevision: main
  syncPolicy:
    syncOptions:
      - ServerSideApply=true (2)
      - Validate=false (3)
1Path and URL of the node labelling Helm chart
2Must be set to true to enable SSA
3Must be set to false to skip schema validation
The two syncOptions are important to set. Since the yaml output might not pass the validation, the schema validation should be disabled.

This will create the following application in ArgoCD:

ApplicationSet
Figure 1. Argo CD: Application

Syncing the Application

When you now synchronize the ArgoCD application, ArgoCD will take the yaml and will tell Kubernetes (or OpenShift) to perform a Server-Side Apply. This will result in the following yaml for the node:

kind: Node
apiVersion: v1
metadata:
  name: ip-10-0-193-67.us-west-1.compute.internal
  labels:
    beta.kubernetes.io/os: linux
    app.kubernetes.io/instance: node-labelling
    [...]
    node-role.kubernetes.io/worker: ''
    gitops.ownedBy: openshift-gitops
    [...]
    environment: Test
    [...]

That’s it …​ all the magic is done.