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
.
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.
// 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.
// 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 theprovider-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.