docs/content/master/guides/implementing-safe-start.md

248 lines
6.6 KiB
Markdown

---
title: Implementing safe-start in Providers
weight: 90
state: alpha
alphaVersion: 2.0
description: 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.
{{</hint>}}
## 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:
```yaml
# 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](https://pkg.go.dev/github.com/crossplane/crossplane-runtime/v2)
for full API reference):
```go
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:
```go
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:
```go
// 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:
```go
// 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:
1. **Gate initialization**: Creates a gate that tracks CRD readiness
2. **Controller registration**: Controllers register with the gate, specifying
which CRDs they need
3. **CRD monitoring**: The `customresourcesgate` controller watches for CRD
creation/deletion
4. **Delayed startup**: Controllers only start when their required CRDs
become active
## Testing your implementation
Test safe-start behavior with this basic workflow:
```shell
# 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.
<!-- vale Google.WordList = NO -->
**Solution**: check that Crossplane activated MRDs and created CRDs:
<!-- vale Google.WordList = YES -->
```shell
kubectl get mrds -o wide
kubectl describe mrap <activation-policy-name>
```
<!-- vale Google.Headings = NO -->
### CRDs don't appear
<!-- vale Google.Headings = YES -->
<!-- vale Google.Colons = NO -->
**Cause**: MRDs might not activate or activation policy doesn't match.
<!-- vale Google.Colons = YES -->
**Solution**: verify activation policy patterns match MRD names:
```shell
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">}}).