Skip to content

Commit 2353ce3

Browse files
authored
Merge pull request #182 from projectsyn/argocd-multitenancy
Add ArgoCD multi-tenancy documentation
2 parents 5ad9b51 + ccd93f9 commit 2353ce3

File tree

3 files changed

+185
-0
lines changed

3 files changed

+185
-0
lines changed

docs/modules/ROOT/nav.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ include::steward:ROOT:partial$nav-tutorials.adoc[]
4242
* xref:how-tos/change-parameter.adoc[Change a parameter]
4343
* xref:how-tos/prepare_for_component_sync.adoc[Prepare component for template updates]
4444
* xref:how-tos/tenant-version-pins.adoc[Renovate version pins]
45+
* xref:how-tos/component-multi-tenancy.adoc[]
4546
* Commodore
4647
+
4748
--
@@ -102,6 +103,7 @@ include::steward:ROOT:partial$nav-reference.adoc[]
102103
* xref:explanations/commodore-packages.adoc[Commodore Packages Best Practices]
103104
* xref:explanations/jsonnet.adoc[Jsonnet Best Practices]
104105
* xref:explanations/component_template_sync.adoc[Keep components in sync]
106+
* xref:explanations/argocd-multitenancy.adoc[]
105107
* Commodore
106108
+
107109
--
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
= ArgoCD multi-tenancy
2+
3+
Project Syn allows users to assign Commodore components installed on a cluster to multiple teams.
4+
The original design of this feature is documented in xref:SDDs:0030-argocd-multitenancy.adoc[].
5+
6+
== Configuration
7+
8+
The assignment of components to teams is controlled via inventory parameter `syn`.
9+
In particular, parameters `syn.owner` and `syn.teams` describe which team is responsible for which component.
10+
11+
Each component instance on a cluster must be assigned to exactly one team.
12+
To explicitly assign an instance to a team, the instance name is added to `syn.teams.<team>.instances`.
13+
Instances can be removed from `syn.teams.<team>.instances` by adding the instance name prefixed with `~` footnote:[The SDD requires that consumers of the instance list apply xref:commodore:ROOT:reference/commodore-libjsonnet.adoc#_renderarrayarr[`com.renderArray()`]] on the list.
14+
15+
NOTE: The https://github.com/projectsyn/jsonnet-libs/blob/main/syn-teams.libsonnet[`syn-teams.libsonnet`] Jsonnet library exposes fields which provide rendered views of the team to instance and instance to team mappings.
16+
17+
Any instance that's not explicitly assigned to a team is assigned to the cluster's owner team which is indicated by parameter `syn.owner`.
18+
19+
NOTE: To assign instance-aware components which deploy all instances through a single ArgoCD application to a non-owner team, the component name must be added to `syn.teams.<team>.instances` in order to assign the application to that team.
20+
21+
== Implementation
22+
23+
Project Syn implements ArgoCD multi-tenancy by deploying an independent ArgoCD project and root application footnote:[Project Syn uses the term "root application" for ArgoCD's https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#app-of-apps[app of apps] concept.] for each team that's responsible for at least one component present on the cluster.
24+
25+
Instances assigned to the cluster's owner team remain assigned to ArgoCD project `syn` and root application `root`.
26+
This project and root application are always present on a Project Syn-managed cluster regardless of whether `syn.owner` is set.
27+
28+
For each additional team that's responsible for one or more component instances on the cluster, a project called `<team-name>` and a root application called `root-<team-name>` are present on the cluster.
29+
30+
To ensure that each project and root application are fully independent, each root application reads ArgoCD application manifests from a different path in the cluster catalog.
31+
The default root application reads manifests from catalog path `apps/`.
32+
All other root applications read manifests from catalog path `apps-<team-name>/`.
33+
34+
Additional projects and root applications are bootstrapped by Steward based on the list of team names present in a config map (by default `additional-root-apps`).
35+
Steward reads this config map once a minute and ensures that an ArgoCD project and root app exist for each team.
36+
37+
The ArgoCD component dynamically sets `spec.project` of ArgoCD application manifests by treating `.metadata.name` of the manifest as a component instance name and looking up the responsible team for that instance through the `syn-teams.libsonnet` Jsonnet library.
38+
39+
NOTE: The ArgoCD component library assumes that each ArgoCD application's `metadata.name` is set to either a component name or a component instance name.
40+
41+
To ease the transition to the multi-tenancy model, Commodore components need to be explicitly marked as multi-tenant aware by setting the component parameter `_metadata.multi_tenant` to `true`.
42+
The ArgoCD commodore component will raise an error if a component which isn't marked as multi-tenant-aware is assigned to a team other than the cluster's owner team.
43+
44+
[IMPORTANT]
45+
====
46+
Components that set `_metadata.multi_tenant=true` are responsible for making sure that they write their ArgoCD application manifests into the appropriate catalog path.
47+
48+
The ArgoCD component ensures that `spec.project` of an application manifest generated by the `argocd.libjsonnet` component library will be set to the responsible team's ArgoCD project.
49+
As noted above, each non-owner team's ArgoCD project is called `<team-name>`.
50+
This allows components to make sure that the application manifests are written to the correct catalog path by setting `output_path: .` for the Kapitan compile step for `component/app.jsonnet` and by rendering each application's output key based on the application's `spec.project`.
51+
52+
.`class/component.yml`
53+
[source,yaml]
54+
----
55+
parameters:
56+
kapitan:
57+
compile:
58+
- input_paths:
59+
- ${_instance}/component/app.jsonnet
60+
input_type: jsonnet
61+
output_path: apps/
62+
output_path: .
63+
----
64+
65+
.`component/app.jsonnet`
66+
[source,jsonnet]
67+
----
68+
local kap = import 'lib/kapitan.libjsonnet';
69+
local kube = import 'lib/kube.libjsonnet';
70+
local inv = kap.inventory();
71+
local params = inv.parameters.<component>; <1>
72+
local argocd = import 'lib/argocd.libjsonnet';
73+
74+
local app = argocd.App(<component>, params.namespace, secrets=true); <1>
75+
76+
local appPath =
77+
local project = std.get(app, 'spec', { project: 'syn' }).project; <2>
78+
if project == 'syn' then 'apps' else 'apps-%s' % project;
79+
80+
{
81+
['%s/<component>' % appPath]: app, <1>
82+
}
83+
----
84+
<1> Replace `<component> with the component's name.
85+
<2> We use `std.get()` to read `spec.project` because `commodore component compile` currently uses a fake `argocd.libjsonnet` which doesn't generate a valid ArgoCD application manifest.
86+
====
87+
88+
In order to enable users to easily move ownership of existing component instances, Steward and the ArgoCD Commodore component ensure that resource pruning is disabled for each root application.
89+
90+
NOTE: Without this configuration, there's a chance that a component instance would be deleted and recreated when it's transferred to a different team.
91+
92+
== Usage in Commodore components
93+
94+
Commodore components that want to use the component team ownership information should use the `syn-teams.libsonnet` Jsonnet library which provides the ownership information in a variety of ways.
95+
96+
This library is available on https://github.com/projectsyn/jsonnet-libs[GitHub] and can be included as a regular component Jsonnet dependency:
97+
98+
.jsonnetfile.json
99+
[source,json]
100+
----
101+
{
102+
"version": 1,
103+
"dependencies": [
104+
{
105+
"source": {
106+
"git": {
107+
"remote": "https://github.com/projectsyn/jsonnet-libs",
108+
"subdir": ""
109+
}
110+
},
111+
"version": "main",
112+
"name": "syn"
113+
}
114+
],
115+
"legacyImports": true
116+
}
117+
----
118+
119+
Users should check the library for usage documentation.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
= Make a Commodore component multi-tenant aware
2+
3+
Project Syn supports xref:explanations/argocd-multitenancy.adoc[multi-tenancy in the Project Syn ArgoCD instance].
4+
5+
Currently components need to be marked as multi-tenant aware explicitly.
6+
This how-to provides the bare minimum configuration that's necessary to make an existing component multi-tenant aware.
7+
8+
[NOTE]
9+
====
10+
The default https://github.com/projectsyn/commodore-component-template[Commodore component template] is updated to generate multi-tenant aware components by default.
11+
12+
Components which receive template updates should already be updated to be multi-tenant aware.
13+
====
14+
15+
. Adjust the component's ArgoCD application manifest generation (usually in `component/app.jsonnet`)
16+
+
17+
.`component/app.jsonnet`
18+
[source,jsonnet]
19+
----
20+
local kap = import 'lib/kapitan.libjsonnet';
21+
local kube = import 'lib/kube.libjsonnet';
22+
local inv = kap.inventory();
23+
local params = inv.parameters.<component>; <1>
24+
local argocd = import 'lib/argocd.libjsonnet';
25+
26+
local app = argocd.App(<component>, params.namespace, secrets=true); <1>
27+
28+
local appPath =
29+
local project = std.get(app, 'spec', { project: 'syn' }).project; <2>
30+
if project == 'syn' then 'apps' else 'apps-%s' % project;
31+
32+
{
33+
['%s/<component>' % appPath]: app, <1>
34+
}
35+
----
36+
<1> Replace `<component> with the component's name.
37+
<2> We use `std.get()` here because `commodore component compile` generates an empty application manifest by default.
38+
39+
. Adjust the component's Kapitan compile step for the application manifests (in `class/<component-name>.yml`)
40+
+
41+
.`class/<component-name>.yml`
42+
[source,yaml]
43+
----
44+
parameters:
45+
kapitan:
46+
compile:
47+
- input_paths:
48+
- ${_instance}/component/app.jsonnet
49+
input_type: jsonnet
50+
output_path: apps/
51+
output_path: .
52+
----
53+
54+
. Mark the component as multi-tenant aware
55+
+
56+
[source,yaml]
57+
----
58+
parameters:
59+
<component_name>: <1>
60+
=_metadata: <2>
61+
multi_tenant: true
62+
----
63+
<1> Replace `component_name` with the component's parameter key.
64+
<2> We recommend making the component's metadata constant if it isn't already.

0 commit comments

Comments
 (0)