Skip to content

Commit

Permalink
further progressing
Browse files Browse the repository at this point in the history
  • Loading branch information
tobru committed Dec 2, 2024
1 parent 44479db commit 8fe0065
Show file tree
Hide file tree
Showing 2 changed files with 234 additions and 94 deletions.
210 changes: 152 additions & 58 deletions docs/modules/ROOT/pages/reference/arch-control-plane.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ This page discusses the architecture of the Application Marketplace Control Plan

image::universal-control-plane.drawio.svg[]

We inherit the https://kb.vshn.ch/appuio-cloud/references/architecture/control-api.html[APPUiO Control API^], extend it for VAMP and rebrand it into the VSHN Portal.
We inherit the https://kb.vshn.ch/appuio-cloud/references/architecture/control-api.html[APPUiO Control API^] and extend it for the Application Marketplace Control Plane.

For the user it doesn't matter where a service instance is created, it's always the same API.
For example `VSHNPostgreSQL` is the same object no matter if it's created on the Central Control Plane, on the CSP Control Plane or even on the local cluster.
The main difference lies in the Composition, hidden from the user.

== Terminology

Expand Down Expand Up @@ -36,41 +40,63 @@ Crossplane specific terms::
== Organization Origin

This enhances https://kb.vshn.ch/appuio-cloud/references/architecture/control-api-org.html[APPUiO Control API: Organization^] with the aspect of the origin of an organization.
The origin specifies from where an organization originates, either from VSHN or a CSP like Exoscale or cloudscale.
This origin is then used in further processes, like filtering of resources or access control.
The origin specifies from where an organization originates, either from VSHN, a CSP like Exoscale or cloudscale or any other entity taking part.

This origin is used in further processes, like filtering of resources or access control.

* Organizations are marked from where they are coming from
* E.g. we put a label or annotation on the Namespace and expose this in the virtual resource under `.spec.origin`
It's part of the `Organization` object, in `.spec.origin`.
A validation makes sure only known origins are allowed.
Known origins are specified in `OrganizationOriginConfig` objects (see <<Origin Metadata>>).

TODO: Expand on this concept
.Example Organization
[source,yaml]
----
apiVersion: organization.appuio.io/v1
kind: Organization
metadata:
name: acme-corp
spec:
displayName: Acme Corp.
origin: exoscale
----

.Example Organization Namespace
[source,yaml]
----
apiVersion: v1
kind: Namespace
metadata:
name: acme-corp
labels:
appuio.io/resource.type: organization
vshn.io/organization-origin: exoscale
annotations:
organization.appuio.io/display-name: Acme Corp.
----

=== Origin Metadata

An origin has a configuration which is used to parametrize further processes.
This is done in a CRD `OrganizationOriginConfig`.
This is done in a CRD called `OrganizationOriginConfig`.

It contains a configuration for label selectors of Compositions.
There can be more configuration if required in future features.

[source,yaml]
----
apiVersion: appcat.vshn.io/v1
apiVersion: vamp.vshn.io/v1
kind: OrganizationOriginConfig
metadata:
name: exoscale
spec:
compositionSelector: <1>
metadata.appcat.vshn.io/serviceprovider: "exoscale"
mandatoryExternalServiceRequest: "true" <2>
metadata.appcat.vshn.io/serviceprovider: exoscale
mandatoryProvidedService: "true" <2>
----
<1> Label selector to filter the available services in the organization origin
<2> Require `ExternalServiceRequest` to enable access to services
<2> Require `ProvidedService` to enable access to services

This configuration can be overwritten per organization namespace, for example to give access to "beta" services or additional zones.

== Crossplane ProviderConfig

TODO: Describe the use of `ProviderConfig` with labels to be selected automatically, e.g. for the Namespace reconciler (multiple CSPCPs at one CSP).
This configuration can be overwritten per Organization namespace, for example to give access to "beta" services or additional zones.

== Service Catalog

Expand All @@ -79,88 +105,135 @@ The Compositions define the exact characteristic of a service, exposing all the

The service catalog is manually defined on the CCP by adding the necessary configurations via a Project Syn Commodore Component.
The XRD for each service on the CCP is exactly the same as the one on the CSPCP, the differentiation lies in the https://docs.crossplane.io/latest/concepts/composite-resources/#composition-selection[`spec.compositionRef`^].
Depending on which Composition is selected, the service is instantiated at a different place.
This allows for true portability, the API spec per service is the same, no matter if the service is provisioned on the CCP or on a CSPCP.

Depending on which Composition is selected, the service is instantiated at a different place (e.g. service provider zone).
This allows for true portability, the API spec per service is the same, no matter if the service is provisioned on the CCP, on a CSPCP or directly in a cluster.

The Composition on the CCP wraps the Claim in an `Object` type from https://github.com/crossplane-contrib/provider-kubernetes[provider-kubernetes^].
It has the main `spec.providerConfigRef.name` set to the CSPCP and the objects inside the `Object` have the `providerConfigRef` set to the worker cluster where the service will be provisioned.
This information is transported via Composition Function inputs.

In the <<Origin Metadata>> we have a label selector for the Compositions, which is used to select the Compositions available to the organization.
In the <<Origin Metadata>> we have a label selector for the Compositions configured, which is used to select the Compositions available to the organization.
This automatically includes which XRDs are available.

Access to list the available XRDs and Compositions on cluster scope is denied to users of the CCP.
See <<Listing>> on how the service catalog is made available.

.TODO Example Composition
.Example Composition
[source,yaml]
----
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
labels:
metadata.appcat.vshn.io/serviceprovider: exoscale
metadata.appcat.vshn.io/serviceproviderzone: de-fra-1
metadata.appcat.vshn.io/zone: de-fra-1
metadata.appcat.vshn.io/servicename: VSHNPostgreSQL
metadata.appcat.vshn.io/description: PostgreSQL by VSHN
metadata.appcat.vshn.io/displayname: PostgreSQL by VSHN
metadata.appcat.vshn.io/end-user-docs-url: https://vs.hn/vshn-postgresql
metadata.appcat.vshn.io/product-description: https://products.docs.vshn.ch/products/appcat/postgresql.html
name: de-fra-1.exoscale.vshnpostgres.vshn.appcat.vshn.io
spec:
compositeTypeRef:
apiVersion: appcat.vshn.io/v1
apiVersion: vshn.appcat.vshn.io/v1
kind: XVSHNPostgreSQL
mode: Pipeline
pipeline:
- functionRef:
name: function-appcat
input:
apiVersion: v1
kind: ConfigMap
metadata:
name: xfn-config
labels:
name: xfn-config
data:
providerConfigRef: cspcp-a
providerConfigRefAtCSPCP: cspcp-zone-a
writeConnectionSecretsToNamespace: syn-crossplane
----

As we need several combinations of Compositions where only the CSPCP connection details are different they are generated via a Project Syn Commodore Component.

=== Listing

The available service catalog is subject to the organization, and specifically it's origin.
Therefore, the catalog is made available in the organization's context as a namespaced object called `catalog`.

A CRD `Catalog` is provided. Instances of this object are reconciled into the organization namespaces.
The catalog reconciler:

* Queries the <<Origin Metadata, origin configuration>> for catalog configuration
* If `mandatoryExternalServiceRequest` is `true`, gets the `ExternalServiceRequest` objects in the namespace
* Gets the matching `Compositions`, according to the label selector and `ExternalServiceRequests`
* For each matching composition, a `Catalog` object is created in the namespace with all the needed information.
This allows to query the available services using `kubectl -n myorg get catalog`.

The `ExternalServiceRequest` defines which service is available.
When the `ExternalServiceRequest` has a `spec.deletionTimestamp` set, the service is excluded from the listing.
The catalog reconciler reads the <<Origin Metadata, OrganizationOriginConfig>> for catalog configuration, according to the Organization origin found on the label of the Namespace.

This allows to query the available services using `kubectl -n myorg get catalog`.
.Pseudocode
[source]
----
compositionSelectors = []
if OrganizationOriginConfig.spec.mandatoryProvidedService = true:
foreach Namespace.ProvidedService:
compositionSelectors.add(ProvidedService.spec.compositionSelector)
else:
compositionSelector.add(OrganizationOriginConfig.spec.compositionSelector)
foreach compositionSelectors:
generate Catalog object from matching Coposition
----

.TODO Example Catalog
When `mandatoryProvidedService` in the origin configuration is enabled, the `ProvidedService` objects define which services are available.
When the `ProvidedService` has a `spec.deletionTimestamp` set, the service is excluded from the listing.

.Example Catalog
[source,yaml]
----
apiVersion: appcat.vshn.io/v1
kind: Catalog
metadata:
name: VSHNPostgreSQL
name: ExoscaleDEFRA1VSHNPostgreSQL
spec:
XrdGVK: vshn.appcat.vshn.io/v1/VSHNPostgreSQL
serviceProvider: exoscale
zones:
- de-fra-1
- ch-gva-2
zone: de-fra-1
displayName: PostgreSQL by VSHN at Exoscale DE-FRA-1
userDocs: https://vs.hn/vshn-postgresql
----

TODO: What do we need in the catalog object? Link to docs?

=== ExternalServiceRequest
=== ProvidedService

TODO: Describe this object
The optional namespaced `ProvidedService` object is used to filter available services to an Organization.

.Example
[source,yaml]
----
apiVersion: exoosb.vamp.vshn.ch/v1
kind: ExternalServiceRequest
apiVersion: appcat.vshn.io/v1
kind: ProvidedService
metadata:
name: servicename
namespace: organizationns
name: ExoscaleGVA1VSHNPostgreSQL
namespace: $organization
spec:
serviceName: VSHNPostgreSQL
compositionSelector: <1>
metadata.appcat.vshn.io/serviceprovider: exoscale
metadata.appcat.vshn.io/zone: de-fra-1
metadata.appcat.vshn.io/servicename: VSHNPostgreSQL
deletionTimestamp: "Mon, 02 Dec 2024 16:15:25 +0100" <2>
----
<1> Label selector to filter the available services in the organization origin
<2> Timestamp when the ProvidedService has been marked as deleted

The deletion timestamp can be used to mark a `ProvidedService` as deleted so that a cleanup of resources can happen for example after a deletion grace period.

Kubernetes RBAC rules only allows restricted access.
The user isn't allowed to create, edit or delete this object.

This object is also used in xref:reference/exoscale-osbapi.adoc[] to track the state in the OSB API.

== Service Provisioning

The CSPCP is an independent Control Plane which also could be used without the CCP.
Therefore, the CCP acts like "a user" of the CSPCP and therefore places Claims into an organization namespace.

The CCP acts like "a user" of the CSPCP and therefore places Claims into an organization namespace at the CSPCP.
This Claim is then reconciled into the service instance.

[mermaid]
Expand All @@ -173,44 +246,65 @@ sequenceDiagram
participant WRK as Worker Cluster / Zone
EU->>CCP: Create Claim
Note over EU, CCP: E.g. VSHNPostgreSQL
CCP->>CCP: Validate Claim
CCP->>CSPCP: Create Claim
Note over CCP, CSPCP: Wrapped in "Object"
CSPCP->>WRK: Create Instance
Note over CSPCP, WRK: Standard procedure
WRK->>CSPCP: Created
CSPCP->>CCP: Created
CCP->>EU: Created
....

.Example
[source,yaml]
----
apiVersion: vshn.appcat.vshn.io/v1
kind: VSHNPostgreSQL
metadata:
name: pgsql-app1-prod
namespace: prod-app
spec:
writeConnectionSecretToRef:
name: postgres-creds
compositionRef:
name: de-fra-1.exoscale.vshnpostgres.vshn.appcat.vshn.io
----

* TODO: How are credentials synchronized from the CSPCP to the CCP for the end-user to see?
* TODO: How to give end-user access to backup listing?
* TODO: Describe where the `spec.providerConfigRef.name` comes from

=== Validation
=== Service Access

When creating a Claim in the CCP, it needs additional validation according to the <<Origin Metadata>>:
As the service instances are running on Worker Clusters, services are exposed there and the user directly connects to the service on the Worker Cluster.
No access is provided to the CSPCP.

* Is the `spec.compositionRef` allowed?
=== Admission Control

=== Namespace Reconciliation
Addition validation is needed of every service instance, besides the normal service spec validation:

We need the organization namespace on the CSPCP to place Claims into it.
For that we use `provider-kubernetes` as we already have the configuration available.
* Is the service actually allowed (GVK of the service)?
* Is the the Composition in `spec.compositionRef` allowed?

This validation is basically the same procedure as in <<Listing>>, only services from the available catalog are allowed to be instantiated.

An XRD `RemoteNamespace` is provided which handles `Namespaces` on remote clusters.
The Composition then uses `provider-kubernetes` with the `spec.providerConfigRef.name` set to the CSPCP where the Namespace needs to be created at.
=== Organization Namespace on CSPCP

A controller watches Namespaces on the CCP which managed `RemoteNamespace` objects in the Organization Namespaces and configures them according to the Origin of the Organization.
An Organization namespace is required on each CSPCP where a service is provisioned, to place the Claim into it.

* TODO: Expand this concept with examples.
* TODO: Support multiple CSPCPs on the same CSP
For that we use `provider-kubernetes` as we already have the configuration available.
The Composition Function handles the creation of the Namespace and does observe only on it.

Removal of Organization namespaces is handled in a CronJob which checks for empty namespaces and removes them.
No Namespace removal is conducted with Crossplane.

== Service Deprovisioning

Service deprovisioning happens by deleting the Claim in the CCP.
It has the same deletion protection like on the CSPCP.

* TODO: What else do we need to write here?

== Control Plane Deployment

All control planes are running in a https://www.vcluster.com/[vcluster^].
Expand Down
Loading

0 comments on commit 8fe0065

Please sign in to comment.