Skip to content

Commit b116112

Browse files
committed
Migrate Argo CD KCP API design doc to GitHub
1 parent 60f25e8 commit b116112

File tree

2 files changed

+851
-0
lines changed

2 files changed

+851
-0
lines changed
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
# Argo CD Application API suitability for a KCP-based Managed GitOps Service
2+
3+
### Written by
4+
- Jonathan West (@jgwest)
5+
- Originally written September 26th, 2022
6+
7+
One of the requirements we got from folks early on in the project was that we did not want to expose Argo CD-specific features. E.g. that we were building GitOpsaaS, not ArgoCDaaS.
8+
9+
That is, that the GitOps Service should be an abstraction over the underlying GitOps tool (including not exposing/supporting the Argo CD CLI/UI).
10+
11+
Thus a GitOps-agnostic API was the API [I proposed all the way back in October 2021](initial-api-design.md), and implemented in late 2021 and early 2022.
12+
13+
So with this requirement, and my knowledge of existing issues with the Argo CD API (see below), and the speed that Argo CD multitenancy improvements have taken in being merged by Argo CD principals, I didn’t put a ton of additional thought into whether to support Argo CD Application as our target API (and no concerns were raised).
14+
15+
However, this is a useful opportunity to codify some of my thoughts on why I don’t believe the Argo CD Application API is a good choice for a large-scale, public managed service (and especially one on KCP).
16+
17+
18+
## So here is my post facto case against directly using the Argo CD Application API:
19+
20+
### 1) Supporting the Argo CD Application API would mean locking us into that API: we can’t add new fields, for example, to support deployment to KCP workspace (or other KCP/Red Hat use cases).
21+
22+
For example, it would be great to be able to do this:
23+
24+
```yaml
25+
kind: Application
26+
spec:
27+
source:
28+
repoURL: (...)
29+
path: (...)
30+
31+
destination:
32+
# deploy to a child workspace:
33+
kcpWorkspace: staging
34+
35+
# or, deploy to an absolute path:
36+
kcpWorkspacePath: :root:users:jgwest:staging:staging1
37+
38+
namespace: my-app
39+
```
40+
41+
But, of course, we can’t add any fields to the Application API that aren’t upstream (and/or we REALLY SHOULDN’T). And while, the ‘.spec.destination’ field does include a name field, this refers to an opaque cluster secret, with its own fixed API.
42+
43+
In contrast, here is the GitOps Service version of this API, which has the desired property of making it easy to target a KCP workspace with Argo CD, while retaining the main Argo CD API fields:
44+
45+
```yaml
46+
kind: GitOpsDeployment
47+
spec:
48+
source:
49+
repoURL: (...)
50+
path: (...)
51+
52+
destination:
53+
environment: staging-kcp-workspace # reference to KCP workspace via managed environment
54+
namespace: my-app
55+
```
56+
57+
and
58+
59+
```yaml
60+
kind: ManagedEnvironment
61+
metadata:
62+
name: staging-kcp-workspace
63+
64+
spec:
65+
kcpWorkspace: staging
66+
# or
67+
kcpWorkspacePath: :root:users:jgwest:staging:staging1
68+
69+
kcpCredentials:
70+
# any additional credentials that are needed to connect to the KCP workspace, e.g. if the workspace is not a child workspace
71+
credentialsSecret: # (...)
72+
```
73+
74+
(ManagedEnvironment is the GitOps Service equivalent to an Argo CD cluster secret)
75+
76+
77+
### 2) It locks us into Argo CD as our GitOps technology of choice.
78+
79+
One of the requirements we got from folks early on in the project was that we did not want to expose Argo CD-specific features. That is, that the GitOps Service would be an abstraction over the underlying GitOps tool. This includes not exposing/supporting the Argo CD CLI/UI.
80+
81+
This has the advantage of allowing us to abstract the underlying GitOps technology: if we find that Argo CD is not a good choice for a large scale managed service (for example, is too expensive to scale), or we find the project direction is non-strategic.
82+
83+
84+
### 3) The AppProject concept is baked into the Application CR: while AppProject is one of the main mechanisms for multitenancy in Argo CD, it doesn’t fit well with a KCP-based approach
85+
86+
AppProject works well enough for Argo CD, where all the Argo CD Applications live within a single namespace, and a set of privileged administrators are responsible for configuring Argo CD on behalf of users.
87+
88+
In contrast, with the KCP model, since with KCP it’s so cheap to create virtual clusters (known in KCP as KCP workspaces), applications and users are more likely to get their own dedicated virtual clusters.
89+
90+
Argo CD namespace:
91+
92+
- target cluster 1 (cluster secret)
93+
94+
- target cluster 2
95+
96+
- target cluster 3
97+
98+
- gitops repo 1 (repo secret)
99+
100+
- gitops repo 2
101+
102+
- gitops repo 3
103+
104+
- Application appA: should be read from gitops repo 3 and write to target cluster 1
105+
106+
- Application appB: should be read from gitops repo 2 and write to target cluster 2
107+
108+
- Application appC: should be read from gitops repo 1 and write to target cluster 3
109+
110+
- AppProjects to enforce the above
111+
112+
In a KCP world, this is more likely to look like (with each line being a workspace, or a K8s resource within the workspace):
113+
114+
```
115+
.
116+
└── kcp-root
117+
└── applications
118+
├── appA (workspace)
119+
│ ├── appA-gitopsdeployment
120+
│ ├── git-repository-credential-1
121+
│ ├── dev (workspace)
122+
│ ├── prod (workspace)
123+
│ └── staging (workspace)
124+
├── appB (workspace)
125+
│ ├── appB-gitopsdeployment
126+
│ ├── git-repository-credential-2
127+
│ ├── dev (workspace)
128+
│ ├── prod (workspace)
129+
│ └── staging (workspace)
130+
└── appC (workspace)
131+
└── (...)
132+
```
133+
134+
e.g. each application is more likely to be segregated into its own virtual workspace, with only the Git credentials required to read that Application’s GitOps repository, and only the target clusters, associated with the GitOpsDeployment.
135+
136+
If a user only has access to the ‘:kcp-root:applications:appA’ workspace, they have no way to access the repository credentials for any of the other workspaces. Likewise, the GitOpsDeployment would only be able to deploy to child workspaces.
137+
138+
(Note: the specific pattern above is not prescribed anywhere: users are free to use GitOps Service and KCP together as they wish, with whatever workspace configuration they want. However I suspect a pattern like this would be a good initial best practice)
139+
140+
There may still be a use for an AppProject-like CR, but this is less useful outside the standard ‘All Argo CD Applications in a namespace’ model of Argo CD, or for the global-cluster scope of repository credentials/cluster credentials.
141+
142+
143+
### 4) If we expose the Argo CD API, but don’t support the Argo CD Application full feature set, this provides a bad API UX to users: they will see that we support the Argo CD Application CR and expect all of the .spec and .status fields to ‘just work’.
144+
145+
If a user sees that we support the Argo CD Application resource, they are going to assume that all of the fields of that Application CR are supported by the service. 
146+
147+
- They attempt to reference an AppProject
148+
149+
- They attempt to specify a user-provided plugin
150+
151+
- They attempt to use sync windows feature
152+
153+
- (etc)
154+
155+
There are a number of fields that the Argo CD Application CR includes, but are non-trivial for us to support in a managed service:
156+
157+
.spec.project / AppProjects in general:
158+
159+
- .spec.project makes sense when you have a single Argo CD namespace that contains all the Application CRs.
160+
161+
- In our case, we no longer have that restriction.
162+
163+
- We have no corresponding concept of AppProject, and it really doesn’t make sense in a multitenant environment.
164+
165+
.spec.source.plugin:
166+
167+
- We don’t have support for user-provided plugins.
168+
169+
.revisionHistoryLimit:
170+
171+
- Argo CD’s only preserving the previous X revisions has historically hampered us from implementing a longer-term record of deployments.
172+
173+
syncWindows
174+
175+
.status:
176+
177+
- Many of the .status fields
178+
179+
This forces us to have to tell users that while we support the Argo CD Application API, we don’t support all of the fields, which is a bad experience. In contrast, by using our own API, we can only expose these in our API once they become ready.
180+
181+
182+
### 5) Opinion: The Argo CD API design is out of step with Red Hat/OpenShift’s traditional API strategy: Red Hat has preferred to use CustomResources for configuration data (e.g. operands for operators), whereas the Argo CD has thus far preferred opaque Secrets/ConfigMaps
183+
184+
Or, said another way, Argo CD’s API is ergonomically challenging in many cases: opaque secrets/configmaps vs CRs, and this makes this part of the API difficult to consume.
185+
186+
An example of all various opaque Secret/ConfigMaps they use:
187+
188+
- <https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#atomic-configuration>
189+
190+
- <https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#clusters>
191+
192+
- <https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#repositories>
193+
194+
compare:
195+
196+
```yaml
197+
apiVersion: v1
198+
kind: Secret
199+
metadata:
200+
annotations:
201+
managed-by: argocd.argoproj.io
202+
labels:
203+
argocd.argoproj.io/secret-type: cluster
204+
name: cluster-api.ci-ln-c086nhk-72292.origin-ci-int-gce.dev.rhcloud.com-3625407592
205+
namespace: argocd
206+
data:
207+
# yes, this is a .json field in YAML
208+
config: |
209+
{
210+
"bearerToken": "(long string)",
211+
"tlsClientConfig": {
212+
"insecure": true
213+
}
214+
}
215+
name: default/api-ci-ln-c086nhk-72292-origin-ci-int-gce-dev-rhcloud-com:6443/kube:admin
216+
server: https://api.ci-ln-c086nhk-72292.origin-ci-int-gce.dev.rhcloud.com:6443
217+
type: Opaque
218+
```
219+
220+
to
221+
222+
```yaml
223+
apiVersion: managed-gitops.redhat.com/v1alpha1
224+
kind: GitOpsDeploymentManagedEnvironment
225+
metadata:
226+
name: my-managed-environment
227+
spec:
228+
apiURL: "https://api.ci-ln-c086nhk-72292.origin-ci-int-gce.dev.rhcloud.com:6443"
229+
allowInsecureHost: true
230+
231+
credentialsSecret: "my-managed-environment-secret" # bearer token in field of Secret
232+
```
233+
234+
235+
For instance:
236+
237+
- Repository secrets are an opaque Secret
238+
239+
- Cluster secrets are an opaque Secret
240+
241+
- Configuration secrets are opaque Secrets
242+
243+
In contrast:
244+
245+
- GitOps Service Repository credentials are a CR
246+
247+
- GitOps Service Cluster Secrets are a CR
248+
249+
Why has this API been historically preferred this? Probably because users just use the Argo CD CLI to generate these ConfigMaps/Secrets (‘argocd cluster add’), which OpenShift GitOps has not historically supported IIRC, and likewise with the GitOps Service.
250+
251+
252+
### 6) Not specific to the Application CR, but there's no way to trigger manual sync of an Argo CD Application without either using the CLI (not supported) or the Web UI (not supported).
253+
254+
So for the GitOps Service, we’ve introduced a new CR for this(GitOpsDeploymentSyncRun).
255+
256+
257+
## So what do I recommend?
258+
259+
Well the strategy we have used in this GitOps Service to date is to use our own resource group/version/kind (**GitOpsDeployment** in[ managed-gitops.redhat.com/v1alpha](http://managed-gitops.redhat.com/v1alpha)), BUT ensuring we still maintain close API compatibility with the Argo CD Application CR. 
260+
261+
This way we get the best of both worlds:
262+
263+
- Users can take their existing Argo CD Applications, replace the ‘kind:’ and ‘apiVersion:’ with ours, and in most cases be good to go.
264+
265+
- Existing knowledge of Argo CD API should allow them to get up and running quickly with our API
266+
267+
- If they use features we don’t support, their API will fail to validate, immediately informing them what they need to change.
268+
269+
- Besides Application CR, the other Argo CD APIs also have a corresponding GitOpsService equivalents: 
270+
271+
- Repository Secret -> GitOpsDeploymentRepositoryCredentials, 
272+
273+
- Cluster Secret -> GitOpsDeploymentManagedEnvironment.
274+
275+
- As above, unlike Argo CD, we use CRs instead of opaque Secrets
276+
277+
- With the added advantage that we can add our own fields to this API, without breaking the existing Argo CD API guarantees
278+
279+
- If/when we implement a new Argo CD Application feature in the GitOps Service, we can add the corresponding API to the GitopsDeployment resource (e.g. gradually expose Argo CD functionality, over time).
280+
281+
- For example, once we support sync windows, we could add that to the GitOpsDeployment API: but we wouldn’t add it to the GitOpsDeployment API until we support it.

0 commit comments

Comments
 (0)