From a8c534192257a251980b0a025d65a69c943b29a0 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Mon, 23 Dec 2024 15:42:54 +0100 Subject: [PATCH 1/2] Add explanation page for ArgoCD multi-tenancy --- docs/modules/ROOT/nav.adoc | 1 + .../explanations/argocd-multitenancy.adoc | 119 ++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 docs/modules/ROOT/pages/explanations/argocd-multitenancy.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index ae1a1df..883dfd9 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -102,6 +102,7 @@ include::steward:ROOT:partial$nav-reference.adoc[] * xref:explanations/commodore-packages.adoc[Commodore Packages Best Practices] * xref:explanations/jsonnet.adoc[Jsonnet Best Practices] * xref:explanations/component_template_sync.adoc[Keep components in sync] +* xref:explanations/argocd-multitenancy.adoc[] * Commodore + -- diff --git a/docs/modules/ROOT/pages/explanations/argocd-multitenancy.adoc b/docs/modules/ROOT/pages/explanations/argocd-multitenancy.adoc new file mode 100644 index 0000000..4516f02 --- /dev/null +++ b/docs/modules/ROOT/pages/explanations/argocd-multitenancy.adoc @@ -0,0 +1,119 @@ += ArgoCD multi-tenancy + +Project Syn allows users to assign Commodore components installed on a cluster to multiple teams. +The original design of this feature is documented in xref:SDDs:0030-argocd-multitenancy.adoc[]. + +== Configuration + +The assignment of components to teams is controlled via inventory parameter `syn`. +In particular, parameters `syn.owner` and `syn.teams` describe which team is responsible for which component. + +Each component instance on a cluster must be assigned to exactly one team. +To explicitly assign an instance to a team, the instance name is added to `syn.teams..instances`. +Instances can be removed from `syn.teams..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. + +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. + +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`. + +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..instances` in order to assign the application to that team. + +== Implementation + +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. + +Instances assigned to the cluster's owner team remain assigned to ArgoCD project `syn` and root application `root`. +This project and root application are always present on a Project Syn-managed cluster regardless of whether `syn.owner` is set. + +For each additional team that's responsible for one or more component instances on the cluster, a project called `` and a root application called `root-` are present on the cluster. + +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. +The default root application reads manifests from catalog path `apps/`. +All other root applications read manifests from catalog path `apps-/`. + +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`). +Steward reads this config map once a minute and ensures that an ArgoCD project and root app exist for each team. + +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. + +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. + +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`. +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. + +[IMPORTANT] +==== +Components that set `_metadata.multi_tenant=true` are responsible for making sure that they write their ArgoCD application manifests into the appropriate catalog path. + +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. +As noted above, each non-owner team's ArgoCD project is called ``. +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`. + +.`class/component.yml` +[source,yaml] +---- +parameters: + kapitan: + compile: + - input_paths: + - ${_instance}/component/app.jsonnet + input_type: jsonnet + output_path: apps/ + output_path: . +---- + +.`component/app.jsonnet` +[source,jsonnet] +---- +local kap = import 'lib/kapitan.libjsonnet'; +local kube = import 'lib/kube.libjsonnet'; +local inv = kap.inventory(); +local params = inv.parameters.; <1> +local argocd = import 'lib/argocd.libjsonnet'; + +local app = argocd.App(, params.namespace, secrets=true); <1> + +local appPath = + local project = std.get(app, 'spec', { project: 'syn' }).project; <2> + if project == 'syn' then 'apps' else 'apps-%s' % project; + +{ + ['%s/' % appPath]: app, <1> +} +---- +<1> Replace ` with the component's name. +<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. +==== + +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. + +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. + +== Usage in Commodore components + +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. + +This library is available on https://github.com/projectsyn/jsonnet-libs[GitHub] and can be included as a regular component Jsonnet dependency: + +.jsonnetfile.json +[source,json] +---- +{ + "version": 1, + "dependencies": [ + { + "source": { + "git": { + "remote": "https://github.com/projectsyn/jsonnet-libs", + "subdir": "" + } + }, + "version": "main", + "name": "syn" + } + ], + "legacyImports": true +} +---- + +Users should check the library for usage documentation. From ccd93f9e460d37bdcf9e6e93a9b07e9ef3b0743c Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Mon, 23 Dec 2024 15:53:08 +0100 Subject: [PATCH 2/2] Add how-to for making components multi-tenant aware --- docs/modules/ROOT/nav.adoc | 1 + .../how-tos/component-multi-tenancy.adoc | 64 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 docs/modules/ROOT/pages/how-tos/component-multi-tenancy.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 883dfd9..7584cd0 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -42,6 +42,7 @@ include::steward:ROOT:partial$nav-tutorials.adoc[] * xref:how-tos/change-parameter.adoc[Change a parameter] * xref:how-tos/prepare_for_component_sync.adoc[Prepare component for template updates] * xref:how-tos/tenant-version-pins.adoc[Renovate version pins] +* xref:how-tos/component-multi-tenancy.adoc[] * Commodore + -- diff --git a/docs/modules/ROOT/pages/how-tos/component-multi-tenancy.adoc b/docs/modules/ROOT/pages/how-tos/component-multi-tenancy.adoc new file mode 100644 index 0000000..8fd606b --- /dev/null +++ b/docs/modules/ROOT/pages/how-tos/component-multi-tenancy.adoc @@ -0,0 +1,64 @@ += Make a Commodore component multi-tenant aware + +Project Syn supports xref:explanations/argocd-multitenancy.adoc[multi-tenancy in the Project Syn ArgoCD instance]. + +Currently components need to be marked as multi-tenant aware explicitly. +This how-to provides the bare minimum configuration that's necessary to make an existing component multi-tenant aware. + +[NOTE] +==== +The default https://github.com/projectsyn/commodore-component-template[Commodore component template] is updated to generate multi-tenant aware components by default. + +Components which receive template updates should already be updated to be multi-tenant aware. +==== + +. Adjust the component's ArgoCD application manifest generation (usually in `component/app.jsonnet`) ++ +.`component/app.jsonnet` +[source,jsonnet] +---- +local kap = import 'lib/kapitan.libjsonnet'; +local kube = import 'lib/kube.libjsonnet'; +local inv = kap.inventory(); +local params = inv.parameters.; <1> +local argocd = import 'lib/argocd.libjsonnet'; + +local app = argocd.App(, params.namespace, secrets=true); <1> + +local appPath = + local project = std.get(app, 'spec', { project: 'syn' }).project; <2> + if project == 'syn' then 'apps' else 'apps-%s' % project; + +{ + ['%s/' % appPath]: app, <1> +} +---- +<1> Replace ` with the component's name. +<2> We use `std.get()` here because `commodore component compile` generates an empty application manifest by default. + +. Adjust the component's Kapitan compile step for the application manifests (in `class/.yml`) ++ +.`class/.yml` +[source,yaml] +---- +parameters: + kapitan: + compile: + - input_paths: + - ${_instance}/component/app.jsonnet + input_type: jsonnet + output_path: apps/ + output_path: . +---- + +. Mark the component as multi-tenant aware ++ +[source,yaml] +---- +parameters: + : <1> + =_metadata: <2> + multi_tenant: true +---- +<1> Replace `component_name` with the component's parameter key. +<2> We recommend making the component's metadata constant if it isn't already.