6.6 KiB
| title | weight | state | alphaVersion | description |
|---|---|---|---|---|
| Implementing safe-start in Providers | 90 | alpha | 2.0 | Guide for provider developers to add safe-start capability for selective resource activation |
This guide shows provider developers how to implement safe-start capability in their Crossplane providers. safe-start enables [disabling unused managed resources]({{<ref "disabling-unused-managed-resources">}}) through ManagedResourceDefinitions, improving performance and reducing resource overhead.
{{<hint "important">}} safe-start requires Crossplane v2.0+ and crossplane-runtime v2.0+. Implementing safe-start involves code changes that affect provider startup behavior. {{}}
What safe-start provides
safe-start changes how your provider handles CRD installation:
Without safe-start:
- Providers create all managed resource CRDs when installed
- Users get all resources even if they only need one or two
- Higher memory usage and API server load
With safe-start:
- Providers create ManagedResourceDefinitions but CRDs only when activated
- Users activate only needed resources through ManagedResourceActivationPolicies
- Significant reduction in cluster resource overhead
Prerequisites
Before implementing safe-start:
- Provider built with crossplane-runtime v2.0+
- Understanding of [ManagedResourceDefinitions]({{<ref "../managed-resources/managed-resource-definitions">}})
- Test environment with Crossplane v2.0+
Implementation steps
Step 1: Declare safe-start capability
Add safe-start to your provider package metadata:
# package/crossplane.yaml
apiVersion: meta.pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-example
spec:
capabilities:
- safe-start
Step 2: Add required imports
Update your main.go imports (see crossplane-runtime godoc for full API reference):
import (
// existing imports...
"k8s.io/apimachinery/pkg/runtime/schema"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"github.com/crossplane/crossplane-runtime/v2/pkg/controller"
"github.com/crossplane/crossplane-runtime/v2/pkg/gate"
"github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/customresourcesgate"
)
Step 3: Initialize the gate
Add gate initialization in your main function:
func main() {
// existing setup code...
o := controller.Options{
// existing options...
Gate: new(gate.Gate[schema.GroupVersionKind]),
}
// Add CustomResourceDefinition to scheme for gate controller
if err := apiextensionsv1.AddToScheme(mgr.GetScheme()); err != nil {
panic(err)
}
// Setup controllers
if err := yourprovider.Setup(mgr, o); err != nil {
panic(err)
}
// Setup the CRD gate controller
if err := customresourcesgate.Setup(mgr, o); err != nil {
panic(err)
}
// start manager...
}
Step 4: Use gated controller setup
Create a gated setup function for each managed resource controller:
// SetupGated registers controller setup with the gate, waiting for the
// required CRD
func SetupGated(mgr ctrl.Manager, o controller.Options) error {
o.Gate.Register(func() {
if err := Setup(mgr, o); err != nil {
panic(err)
}
}, v1alpha1.MyResourceGroupVersionKind)
return nil
}
// Setup is your existing controller setup function (unchanged)
func Setup(mgr ctrl.Manager, o controller.Options) error {
// existing controller setup code...
}
Step 5: Update controller registration
Change your controller setup to use the gated versions:
// internal/controller/controller.go
func Setup(mgr ctrl.Manager, o controller.Options) error {
for _, setup := range []func(ctrl.Manager, controller.Options) error{
myresource.SetupGated, // Changed from myresource.Setup
// other gated setups...
} {
if err := setup(mgr, o); err != nil {
return err
}
}
return nil
}
Implementation details
The safe-start implementation uses a "gate" pattern:
- Gate initialization: Creates a gate that tracks CRD readiness
- Controller registration: Controllers register with the gate, specifying which CRDs they need
- CRD monitoring: The
customresourcesgatecontroller watches for CRD creation/deletion - Delayed startup: Controllers only start when their required CRDs become active
Testing your implementation
Test safe-start behavior with this basic workflow:
# Install Crossplane v2.0+
helm install crossplane crossplane-stable/crossplane \
--namespace crossplane-system \
--set provider.defaultActivations={}
# Install your provider
kubectl apply -f provider.yaml
# Check that MRDs are created but inactive
kubectl get mrds
# All should show STATE: Inactive
# No CRDs should exist yet
kubectl get crds | grep yourprovider.m.crossplane.io
# Should return no results
# Create activation policy
kubectl apply -f - <<EOF
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: test-activation
spec:
activate:
- "myresource.yourprovider.m.crossplane.io"
EOF
# Verify activation worked
kubectl get mrd myresource.yourprovider.m.crossplane.io
# Should show STATE: Active
# CRD should now exist
kubectl get crd myresource.yourprovider.m.crossplane.io
Troubleshooting
Controllers never start
Cause: gate waits for CRDs that never become active.
Solution: check that Crossplane activated MRDs and created CRDs:
kubectl get mrds -o wide
kubectl describe mrap <activation-policy-name>
CRDs don't appear
Cause: MRDs might not activate or activation policy doesn't match.
Solution: verify activation policy patterns match MRD names:
kubectl get mrds
kubectl get mrap -o yaml
Migration considerations
When adding safe-start to existing providers:
- Existing installations: Continue working as expected (no CRD changes)
- New installations: Start with inactive MRDs, require activation policies
Next steps
- Test your safe-start implementation with different activation patterns
- Update provider documentation to explain activation requirements
- Consider the user experience for providers that now require activation policies
Learn more about the user experience in [disabling unused managed resources]({{<ref "disabling-unused-managed-resources">}}).