Skip to content

Commit

Permalink
Merge pull request #132 from vshn/add/non_converged_services
Browse files Browse the repository at this point in the history
Add information about non-converged setups
  • Loading branch information
Kidswiss authored Feb 5, 2025
2 parents 7903004 + ad9a692 commit eef7c56
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 0 deletions.
122 changes: 122 additions & 0 deletions docs/modules/ROOT/pages/framework/non-converged.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
= Implement Services non-converged compatible

This page contains information how to implement services, so that they are compatible with a non-coverged cluster.

While most of the things are handled automatically, there are some edgecases that need to be caught by the engineer implementing the service.

== Kube Objects

=== References

References in an `Object` will always be evaluated on the control plane where Crossplane is running.

It is theoretically possible to reference any arbitrary object in a kuberenetes cluster, the references should always point to a Crossplane Managed Resource, so we ensure that it actually exists on the control plane.

By default, if no `kind` and `apiVersion` is specified, references will default to provider-kubernetes' `Objects`.

.Example: Kube Object references
[source,go]
----
xRef := xkube.Reference{
DependsOn: &xkube.DependsOn{
Name: comp.GetName() + "-cluster",
},
}
// Is equal to this
xRef := xkube.Reference{
DependsOn: &xkube.DependsOn{
Kind: "Object",
APIVersion: "kubernetes.crossplane.io/v1alpha2",
Name: comp.GetName() + "-cluster",
},
}
// This won't work in a non-converged scenario, because the instance namespace doesn't exist on the control plane.
xRef := xkube.Reference{
DependsOn: &xkube.DependsOn{
Kind: "SGCluster",
APIVersion: "kubernetes.crossplane.io/v1alpha2",
Name: comp.GetName() + "-cluster",
Namespace: comp.GetInstanceNamespace(),
},
}
----

=== Connection Details

In contrast to references, the connection details are evaluated on the target service cluster. So it's still possible to expose any arbitrary fields as connection details. This can even be used instead of a dedicated observer object.

.Example: How to user connection details on a kube object
[source,go]
----
// Still works as always
obj.Spec.ConnectionDetails = []xkubev1.ConnectionDetail{
{
ToConnectionSecretKey: PostgresqlPassword,
ObjectReference: corev1.ObjectReference{
APIVersion: "v1",
Kind: "Secret",
Namespace: comp.GetInstanceNamespace(),
Name: comp.GetName(),
FieldPath: "data.superuser-password",
},
}
}
// Get the connection details
cd, err := svc.GetObservedComposedResourceConnectionDetails(comp.GetName()+"myobj")
if err != nil {
return ...
}
----

== Nested Composites

There's logic in the runtime that will automatically set the same providerconfig on all managed resources for any given managed resource in a composition.

However, this does not work for nested composites. Because composites don't have `providerConfigRefs` in their spec. So we need to ignore the providerconfig for the composite itself, but we have to ensure that the nested service knows about the `providerConfig` as well.

.Example: How to deploy a nested composite
[source,go]
----
// We have to ignore the provideconfig on the composite itself.
pg := &vshnv1.XVSHNPostgreSQL{
ObjectMeta: metav1.ObjectMeta{
Name: a.comp.GetName() + PgInstanceNameSuffix,
Labels: map[string]string{
runtime.ProviderConfigIgnoreLabel: "true",
},
},
Spec: vshnv1.XVSHNPostgreSQLSpec{
Parameters: *params,
ResourceSpec: xpv1.ResourceSpec{
WriteConnectionSecretToReference: &xpv1.SecretReference{
Name: PgSecretName,
Namespace: a.comp.GetInstanceNamespace(),
},
},
},
}
// But pass the parent's provider config properly to the instance.
if v, exists := a.comp.GetLabels()[runtime.ProviderConfigLabel]; exists {
pg.Labels[runtime.ProviderConfigLabel] = v
}
----

== ProviderConfig Handling

As mentioned in the previous chapter, there's logic in the function runtime that will automatically inject any given `providerConfig` set in the `appcat.vshn.io/provider-config` label on a composite.

In general to specify what `providerConfig` any given composite should follow, the `appcat.vshn.io/provider-config` label has to be set. Its value should be the name of any deployed `providerConfig` on the control plane.

Please note that `providerConfigs` need to exist for all relevant providers for any given composite, those are usually `provider-helm` and `provider-kubernetes`.

If the label is set, then the function runtime will inject that particular `providerConf` name into each managed resource in the composition.

There are however some special cases where an ignore label has to be set on the managed resource:

- User and Database management for PostgreSQL and MariaDB. The comp functions create adhoc `providerConfigs` based on the connection details for the `provider-sql`. Because they are bound to the specific instance.
- Nested Composites, as described above. Composistes themselves don't have a schema for the `providerConfig` so it's actually not possible to set it on a composite itself.
- Object Buckets, they usually connect to a globally available Object storage instance, so the default `provideConfig` should apply.
1 change: 1 addition & 0 deletions docs/modules/ROOT/partials/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
** xref:framework/ci-cd.adoc[]
** xref:framework/working_with_functions.adoc[]
** xref:framework/slareports.adoc[]
** xref:framework/non-converged.adoc[]
** Billing
*** xref:framework/cloud-usage-reporting.adoc[]
*** xref:framework/vshn-usage-reporting.adoc[]
Expand Down

0 comments on commit eef7c56

Please sign in to comment.