Configuring Crossplane with Argo CD

Argo CD and Crossplane are a great combination. Argo CD provides GitOps while Crossplane turns any Kubernetes cluster into a Universal Control Plane for all of your resources. There are configuration details required in order for the two to work together properly. This doc will help you understand these requirements. It is recommended to use Argo CD version 2.4.8 or later with Crossplane.

Argo CD synchronizes Kubernetes resource manifests stored in a Git repository with those running in a Kubernetes cluster (GitOps). There are different ways to configure how Argo CD tracks resources. With Crossplane, you need to configure Argo CD to use Annotation based resource tracking. See the Argo CD docs for additional detail.

Configuring Argo CD with Crossplane

Set Resource Tracking Method

In oder for Argo CD to correctly track an Application resources that contain Crossplane related object it needs to be configured to use the annotation mechanism.

To configure it, edit the argocd-cm ConfigMap in the argocd Namespace as such:

1apiVersion: v1
2kind: ConfigMap
3data:
4  application.resourceTrackingMethod: annotation

Set Health Status

Argo CD has a built-in health assessment for Kubernetes resources. Some checks are supported by the community directly in Argo’s repository. For example the Provider from pkg.crossplane.io has already been declared which means there no further configuration needed.

Argo CD also enable customising these checks per instance, and that’s the mechanism used to provide support of Provider’s CRDs

To configure it, edit the argocd-cm ConfigMap in the argocd Namespace as such:

 1apiVersion: v1
 2kind: ConfigMap
 3data:
 4  application.resourceTrackingMethod: annotation
 5  resource.customizations: |
 6    "*.upbound.io/*":
 7      health.lua: |
 8        health_status = {
 9          status = "Progressing",
10          message = "Provisioning ..."
11        }
12
13        if obj.status == nil or obj.status.conditions == nil then
14          return health_status
15        end
16
17        for i, condition in ipairs(obj.status.conditions) do
18          if condition.type == "LastAsyncOperation" then
19            if condition.status == "False" then
20              health_status.status = "Degraded"
21              health_status.message = condition.message
22              return health_status
23            end
24          end
25
26          if condition.type == "Synced" then
27            if condition.status == "False" then
28              health_status.status = "Degraded"
29              health_status.message = condition.message
30              return health_status
31            end
32          end
33
34          if condition.type == "Ready" then
35            if condition.status == "True" then
36              health_status.status = "Healthy"
37              health_status.message = "Resource is up-to-date."
38              return health_status
39            end
40          end
41        end
42
43        return health_status
44
45    "*.crossplane.io/*":
46      health.lua: |
47        health_status = {
48          status = "Progressing",
49          message = "Provisioning ..."
50        }
51
52        if obj.status == nil or obj.status.conditions == nil then
53          return health_status
54        end
55
56        for i, condition in ipairs(obj.status.conditions) do
57          if condition.type == "LastAsyncOperation" then
58            if condition.status == "False" then
59              health_status.status = "Degraded"
60              health_status.message = condition.message
61              return health_status
62            end
63          end
64
65          if condition.type == "Synced" then
66            if condition.status == "False" then
67              health_status.status = "Degraded"
68              health_status.message = condition.message
69              return health_status
70            end
71          end
72
73          if condition.type == "Ready" then
74            if condition.status == "True" then
75              health_status.status = "Healthy"
76              health_status.message = "Resource is up-to-date."
77              return health_status
78            end
79          end
80        end
81
82        return health_status    

Set Resource Exclusion

Crossplane providers generates a ProviderConfigUsage for each of the managed resource (MR) it handles. This resource enable representing the relationship between MR and a ProviderConfig so that controller can use it as finalizer when a ProviderConfig is deleted. End-user of Crossplane are not expected to interact with this resource.

Argo CD UI reactivity can be impacted as the number of resource and types grow. To help keep this number low we recommend hiding all ProviderConfigUsage resources from Argo CD UI.

To configure resource exclusion edit the argocd-cm ConfigMap in the argocd Namespace as such:

1apiVersion: v1
2kind: ConfigMap
3data:
4    resource.exclusions: |
5      - apiGroups:
6        - "*"
7        kinds:
8        - ProviderConfigUsage      

The use of "*" as apiGroups will enable the mechanism for all Crossplane Providers.

Increase K8s Client QPS

As the number of CRDs grow on a control plane it will increase the amount of queries Argo CD Application Controller needs to send to the Kubernetes API. If this is the case you can increase the rate limits of the Argo CD Kubernetes client.

Set the environment variable ARGOCD_K8S_CLIENT_QPS to 300 for improved compatibility with a large number of CRDs.

The default value of ARGOCD_K8S_CLIENT_QPS is 50, modifying the value will also update ARGOCD_K8S_CLIENT_BURST as it is default to ARGOCD_K8S_CLIENT_QPS x 2.