first draft of MRD documentation based on the spec and PRs related to MRD/MRAP/SafeStart

Signed-off-by: Scott Nichols <n3wscott@upbound.io>
This commit is contained in:
Scott Nichols 2025-08-07 11:25:54 -07:00
parent 704d5dfe38
commit 3fcc4473a9
8 changed files with 2633 additions and 2 deletions

View File

@ -0,0 +1,530 @@
---
title: Get Started with Managed Resource Definitions
weight: 220
description: Learn how to use MRDs and activation policies to optimize your Crossplane installation
---
This guide shows how to use Managed Resource Definitions (MRDs) and activation
policies to control which managed resources are available in your cluster.
You install a provider, examine its MRDs, and use policies to activate only
the resources you need.
{{< hint "tip" >}}
This guide demonstrates the performance and discovery benefits of MRDs by
working with a subset of AWS resources rather than installing hundreds of CRDs.
{{< /hint >}}
By the end of this guide, you understand how to:
* Examine MRDs created by provider packages
* Use activation policies to control resource availability
* Discover connection details through MRD schemas
* Optimize cluster performance by activating only needed resources
## Prerequisites
This guide requires:
* A Kubernetes cluster with at least 2 GB of RAM
* Crossplane v2.0+ [installed on the cluster]({{< ref "install" >}})
* `kubectl` configured to access your cluster
## Understand the default activation policy
Before installing providers, it's important to understand Crossplane's default
activation behavior. Crossplane creates a default ManagedResourceActivationPolicy
that, by default, activates **all** managed resources with a `"*"` pattern.
{{< hint "important" >}}
The default `"*"` activation pattern defeats the performance benefits of
SafeStart by activating all resources. For this tutorial, we work with the
default behavior, but production setups should use more selective activation.
{{< /hint >}}
Check if you have a default activation policy:
```shell
kubectl get mrap crossplane-default-activation-policy -o yaml
```
You can modify the default activation policy directly:
{{< tabs >}}
{{< tab "Edit Existing Policy" >}}
```shell
# Permanently disable by using a non-matching pattern
kubectl patch mrap crossplane-default-activation-policy --type='merge' \
-p='{"spec":{"activations":["nonexistent.example.com"]}}'
# Or remove all activations entirely
kubectl patch mrap crossplane-default-activation-policy --type='merge' \
-p='{"spec":{"activations":[]}}'
```
{{< hint "note" >}}
Changes to the default policy are permanent. After the policy exists, Crossplane
won't modify it, even if you change Helm values.
{{< /hint >}}
{{< /tab >}}
{{< tab "Reset with Helm Values" >}}
```shell
# Delete the default policy and restart Crossplane to recreate from Helm values
kubectl delete mrap crossplane-default-activation-policy
helm upgrade crossplane crossplane-stable/crossplane \
--set provider.defaultActivations=null \
--namespace crossplane-system --reuse-values
kubectl rollout restart deployment/crossplane -n crossplane-system
```
This approach lets you use Helm chart values to control the default policy.
{{< /tab >}}
{{< /tabs >}}
{{< hint "tip" >}}
Learn more about configuring default activation policies during installation
and best practices in the [MRD activation policies guide]({{< ref "../guides/mrd-activation-policies#default-activation-policy" >}}).
{{< /hint >}}
## Install a SafeStart provider
Now install a provider that supports SafeStart. This provider creates MRDs
that activation policies control.
```yaml
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws
spec:
package: xpkg.upbound.io/crossplane-contrib/provider-aws:v0.45.0
```
Apply this configuration:
```shell
kubectl apply -f - <<EOF
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws
spec:
package: xpkg.upbound.io/crossplane-contrib/provider-aws:v0.45.0
EOF
```
Wait for the provider to become healthy:
```shell
kubectl get providers
```
```shell
NAME INSTALLED HEALTHY PACKAGE AGE
provider-aws True True xpkg.upbound.io/crossplane-contrib/provider-aws 2m
```
## Examine the MRDs
List the MRDs created by the provider:
```shell
kubectl get mrds
```
The MRD states depend on your default activation policy:
{{< tabs >}}
{{< tab "With Default Activation (default)" >}}
If you kept the default `"*"` activation pattern:
```shell
NAME STATE AGE
buckets.s3.aws.crossplane.io Active 2m
instances.ec2.aws.crossplane.io Active 2m
databases.rds.aws.crossplane.io Active 2m
clusters.eks.aws.crossplane.io Active 2m
# ... many more, all Active
```
The default policy activates all MRDs, so SafeStart providers behave like
traditional providers.
{{< /tab >}}
{{< tab "With Disabled Default Activation" >}}
If you disabled default activation:
```shell
NAME STATE AGE
buckets.s3.aws.crossplane.io Inactive 2m
instances.ec2.aws.crossplane.io Inactive 2m
databases.rds.aws.crossplane.io Inactive 2m
clusters.eks.aws.crossplane.io Inactive 2m
# ... many more, all Inactive
```
This demonstrates true SafeStart behavior where resources must be explicitly
activated.
{{< /tab >}}
{{< /tabs >}}
{{< hint "note" >}}
For the rest of this tutorial, we assume you have default activation
disabled to demonstrate selective activation. If you have default activation
enabled, the MRDs are already active.
{{< /hint >}}
Examine a specific MRD to understand its schema and connection details:
```shell
kubectl get mrd instances.ec2.aws.crossplane.io -o yaml
```
Look for the `connectionDetails` section:
```yaml
spec:
connectionDetails:
- description: The public IP address assigned to the instance
name: public_ip
type: string
- description: The private IP address assigned to the instance
name: private_ip
type: string
- description: The public DNS name assigned to the instance
name: public_dns
type: string
```
## Verify CRD creation behavior
The presence of CRDs depends on whether MRDs are active:
{{< tabs >}}
{{< tab "With Default Activation (default)" >}}
Because MRDs are active due to the default `"*"` policy, CRDs exist:
```shell
kubectl get crds | grep aws.crossplane.io | wc -l
```
This shows many CRDs (100+), demonstrating that active MRDs
create CRDs immediately.
{{< /tab >}}
{{< tab "With Disabled Default Activation" >}}
Because the MRDs are inactive, no CRDs should exist for AWS resources:
```shell
kubectl get crds | grep aws.crossplane.io
```
This should return no results, demonstrating that inactive MRDs don't create
CRDs in your cluster.
{{< /tab >}}
{{< /tabs >}}
## Create an activation policy
Create a ManagedResourceActivationPolicy to activate specific AWS resources:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: aws-demo-resources
spec:
activations:
- instances.ec2.aws.crossplane.io
- buckets.s3.aws.crossplane.io
- "*.rds.aws.crossplane.io"
```
Apply the policy:
```shell
kubectl apply -f - <<EOF
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: aws-demo-resources
spec:
activations:
- instances.ec2.aws.crossplane.io
- buckets.s3.aws.crossplane.io
- "*.rds.aws.crossplane.io"
EOF
```
This policy activates:
* EC2 instances (exact match)
* S3 buckets (exact match)
* All RDS resources (wildcard match)
## Verify activation
Check that the specified MRDs are now active:
```shell
kubectl get mrds instances.ec2.aws.crossplane.io buckets.s3.aws.crossplane.io
```
```shell
NAME STATE AGE
instances.ec2.aws.crossplane.io Active 5m
buckets.s3.aws.crossplane.io Active 5m
```
List all RDS MRDs to see they're also active:
```shell
kubectl get mrds | grep rds
```
```shell
NAME STATE AGE
clusters.rds.aws.crossplane.io Active 5m
databases.rds.aws.crossplane.io Active 5m
dbinstances.rds.aws.crossplane.io Active 5m
# ... other RDS resources
```
## Verify CRDs are created
Now that MRDs are active, CRDs should exist:
```shell
kubectl get crds | grep -E "(instances.ec2|buckets.s3|rds)" | head -5
```
```shell
buckets.s3.aws.crossplane.io 2024-01-15T10:30:00Z
clusters.rds.aws.crossplane.io 2024-01-15T10:30:00Z
databases.rds.aws.crossplane.io 2024-01-15T10:30:00Z
dbinstances.rds.aws.crossplane.io 2024-01-15T10:30:00Z
instances.ec2.aws.crossplane.io 2024-01-15T10:30:00Z
```
## Create a managed resource
Now you can create managed resources using the active MRDs. Create an S3 bucket:
```yaml
apiVersion: s3.aws.crossplane.io/v1alpha1
kind: Bucket
metadata:
name: my-demo-bucket
spec:
forProvider:
region: us-east-1
providerConfigRef:
name: default
```
{{< hint "note" >}}
This example assumes you have AWS credentials configured. See the
[AWS Provider documentation]({{< ref "../guides/aws-provider" >}}) for
authentication setup.
{{< /hint >}}
```shell
kubectl apply -f - <<EOF
apiVersion: s3.aws.crossplane.io/v1alpha1
kind: Bucket
metadata:
name: my-demo-bucket
spec:
forProvider:
region: us-east-1
providerConfigRef:
name: default
EOF
```
## Test inactive resources
Try to create a managed resource for an inactive MRD, like EKS clusters:
```shell
kubectl get mrd clusters.eks.aws.crossplane.io
```
```shell
NAME STATE AGE
clusters.eks.aws.crossplane.io Inactive 8m
```
Attempting to create an EKS cluster fails because the CRD doesn't exist:
```shell
kubectl apply -f - <<EOF
apiVersion: eks.aws.crossplane.io/v1alpha1
kind: Cluster
metadata:
name: test-cluster
spec:
forProvider:
region: us-east-1
version: "1.21"
EOF
```
```shell
error validating data: ValidationError(Cluster): unknown field "apiVersion" in io.k8s.api.core.v1.Cluster
```
## Expand activation with wildcards
Add more resources using wildcard patterns. Update your activation policy:
```shell
kubectl patch mrap aws-demo-resources --type merge -p '{
"spec": {
"activations": [
"instances.ec2.aws.crossplane.io",
"buckets.s3.aws.crossplane.io",
"*.rds.aws.crossplane.io",
"*.eks.aws.crossplane.io"
]
}
}'
```
Verify EKS resources are now active:
```shell
kubectl get mrds | grep eks
```
```shell
NAME STATE AGE
clusters.eks.aws.crossplane.io Active 10m
nodegroups.eks.aws.crossplane.io Active 10m
```
## Examine activation policy status
Check which MRDs your policy has activated:
```shell
kubectl get mrap aws-demo-resources -o yaml
```
Look for the `status.activated` field:
```yaml
status:
activated:
- buckets.s3.aws.crossplane.io
- instances.ec2.aws.crossplane.io
- clusters.rds.aws.crossplane.io
- databases.rds.aws.crossplane.io
- clusters.eks.aws.crossplane.io
- nodegroups.eks.aws.crossplane.io
# ... other activated MRDs
```
## Performance comparison
Compare the resource usage with traditional provider installation:
**Without MRDs (traditional):**
- All ~200 AWS CRDs created immediately
- Higher memory usage in kube-apiserver
- Longer provider installation time
**With MRDs and selective activation:**
- Only activated CRDs created (~10-20 in this example)
- Lower memory footprint
- Faster resource discovery and management
Check the number of AWS CRDs currently in your cluster:
```shell
kubectl get crds | grep aws.crossplane.io | wc -l
```
This should be much smaller than the total number of MRDs.
## Multiple activation policies
You can create multiple MRAPs for different use cases. Create a second policy
for development environments:
```shell
kubectl apply -f - <<EOF
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: aws-dev-resources
spec:
activations:
- "*.ec2.aws.crossplane.io"
- "*.iam.aws.crossplane.io"
EOF
```
The activations from both policies are combined, giving you fine-grained
control over resource availability.
## Clean up
Remove the demo resources:
```shell
kubectl delete bucket my-demo-bucket
kubectl delete mrap aws-demo-resources aws-dev-resources
kubectl delete provider provider-aws
```
## Production recommendations
For production Crossplane deployments, follow these best practices:
### 1. Disable default activation
Install Crossplane with selective activation:
```shell
helm install crossplane crossplane-stable/crossplane \
--set provider.defaultActivations=null \
--namespace crossplane-system
```
### 2. Use targeted activation policies
Create provider-specific policies rather than using the default `"*"` pattern:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: production-aws-resources
spec:
activations:
- "instances.ec2.aws.crossplane.io"
- "*.rds.aws.crossplane.io"
- "buckets.s3.aws.crossplane.io"
- "*.iam.aws.crossplane.io"
```
### 3. Environment-specific policies
Use different activation strategies per environment:
* **Development**: Broad activation for experimentation
* **Staging**: Subset of production resources for testing
* **Production**: Minimal, specific activation for performance
## Next steps
Now that you understand MRDs and activation policies, you can:
* **Optimize cluster performance** by using selective activation
* **Improve resource discovery** through MRD connection details documentation
* **Implement environment-specific policies** for different deployment stages
* **Plan provider adoption** using SafeStart-capable providers
Learn more about:
* [MRD activation policies best practices]({{< ref "../guides/mrd-activation-policies" >}}) - Comprehensive guide including default policy configuration
* [Managed Resource Definitions concepts]({{< ref "managed-resource-definitions" >}})
* [Provider capabilities and SafeStart]({{< ref "../packages/provider-capabilities" >}})

View File

@ -0,0 +1,714 @@
---
title: Implementing SafeStart in Providers
weight: 160
description: Guide for provider developers to implement SafeStart capability
---
This guide shows provider developers how to implement SafeStart capability in
their Crossplane providers. SafeStart enables selective resource activation
through Managed Resource Definitions (MRDs), improving performance and resource
management.
{{< hint "important" >}}
SafeStart requires Crossplane v2.0+ and involves significant provider changes.
Plan for breaking changes and thorough testing before implementing.
{{< /hint >}}
## What SafeStart provides
SafeStart transforms how your provider handles resource installation:
**Without SafeStart:**
- All managed resources become CRDs immediately when provider installs
- Users get all ~200 AWS resources even if they need only 5
- Higher memory usage and slower API server responses
**With SafeStart:**
- All managed resources become inactive MRDs when provider installs
- Users activate only needed resources through policies
- Lower resource overhead and better performance
## Prerequisites
Before implementing SafeStart, ensure you have:
* Provider built with Crossplane v2.0+ runtime
* Understanding of [MRDs and activation policies]({{< ref "mrd-activation-policies" >}})
* Test environment with Crossplane v2.0+
* CI/CD pipeline that can build and test provider changes
## Implementation steps
### Step 1: Update provider metadata
Declare SafeStart capability in your provider package metadata:
```yaml
apiVersion: meta.pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-example
spec:
package: registry.example.com/provider-example:v1.0.0
capabilities:
- name: SafeStart
```
{{< hint "tip" >}}
Crossplane supports flexible capability matching. `SafeStart`, `safestart`,
and `safe-start` are all recognized as the same capability.
{{< /hint >}}
### Step 2: Enhance MRD generation
Update your MRD generation to include connection details documentation:
{{< tabs >}}
{{< tab "Go Controller Runtime" >}}
```go
// In your MRD generation code
type ManagedResourceDefinition struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ManagedResourceDefinitionSpec `json:"spec"`
Status ManagedResourceDefinitionStatus `json:"status,omitempty"`
}
type ManagedResourceDefinitionSpec struct {
// Standard CRD fields
Group string `json:"group"`
Names Names `json:"names"`
Scope string `json:"scope"`
// SafeStart-specific fields
ConnectionDetails []ConnectionDetail `json:"connectionDetails,omitempty"`
State ResourceState `json:"state,omitempty"`
}
type ConnectionDetail struct {
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
FromConnectionSecretKey string `json:"fromConnectionSecretKey,omitempty"`
}
```
{{< /tab >}}
{{< tab "Terrajet/Upjet Provider" >}}
```go
// In your provider configuration
func GetProvider() *ujconfig.Provider {
pc := ujconfig.NewProvider([]byte(providerSchema), resourcePrefix, modulePath,
ujconfig.WithIncludeList(ExternalNameConfigured()),
ujconfig.WithDefaultResourceOptions(
ExternalNameConfigurations(),
SafeStartConfiguration(), // Add SafeStart config
))
// Configure SafeStart for specific resources
for _, configure := range []func(provider *ujconfig.Provider){
configureConnectionDetails,
configureMRDDocumentation,
} {
configure(pc)
}
return pc
}
func configureConnectionDetails(p *ujconfig.Provider) {
// Example: RDS Instance connection details
p.AddResourceConfigurator("aws_db_instance", func(r *ujconfig.Resource) {
r.ConnectionDetails = map[string]ujconfig.ConnectionDetail{
"endpoint": {
Description: "The RDS instance endpoint",
Type: "string",
FromConnectionSecretKey: "endpoint",
},
"port": {
Description: "The port on which the DB accepts connections",
Type: "integer",
FromConnectionSecretKey: "port",
},
"username": {
Description: "The master username for the database",
Type: "string",
FromConnectionSecretKey: "username",
},
"password": {
Description: "The master password for the database",
Type: "string",
FromConnectionSecretKey: "password",
},
}
})
}
```
{{< /tab >}}
{{< /tabs >}}
### Step 3: Handle namespaced resources
SafeStart works best with namespaced managed resources. Update your resources
to support both cluster and namespaced scopes:
```go
// Update resource definitions to support namespacing
type Database struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec DatabaseSpec `json:"spec"`
Status DatabaseStatus `json:"status,omitempty"`
}
// Update your CRD generation
//+kubebuilder:resource:scope=Namespaced
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
type Database struct {
// ... resource definition
}
// Optionally create cluster-scoped variants
//+kubebuilder:resource:scope=Cluster
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
type ClusterDatabase struct {
// ... same spec but cluster scoped
}
```
### Step 4: Update RBAC permissions
SafeStart providers need additional permissions to manage CRDs dynamically:
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: provider-example-system
rules:
# Existing provider permissions
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
- apiGroups: ["example.crossplane.io"]
resources: ["*"]
verbs: ["*"]
# Additional SafeStart permissions
- apiGroups: ["apiextensions.k8s.io"]
resources: ["customresourcedefinitions"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["apiextensions.crossplane.io"]
resources: ["managedresourcedefinitions"]
verbs: ["get", "list", "watch", "update", "patch"]
```
### Step 5: Implement MRD controller logic
Add controller logic to handle MRD activation and CRD lifecycle:
```go
package controller
import (
"context"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
xpv1alpha1 "github.com/crossplane/crossplane/apis/apiextensions/v1alpha1"
)
// MRDReconciler handles MRD activation
type MRDReconciler struct {
client.Client
Scheme *runtime.Scheme
}
func (r *MRDReconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) {
mrd := &xpv1alpha1.ManagedResourceDefinition{}
if err := r.Get(ctx, req.NamespacedName, mrd); err != nil {
return reconcile.Result{}, client.IgnoreNotFound(err)
}
// Check if MRD should be active
if mrd.Spec.State != nil && *mrd.Spec.State == xpv1alpha1.ResourceStateActive {
return r.ensureCRDExists(ctx, mrd)
}
// If inactive, ensure CRD is removed
return r.ensureCRDRemoved(ctx, mrd)
}
func (r *MRDReconciler) ensureCRDExists(ctx context.Context, mrd *xpv1alpha1.ManagedResourceDefinition) (reconcile.Result, error) {
crd := &apiextv1.CustomResourceDefinition{}
crdName := mrd.Spec.Names.Plural + "." + mrd.Spec.Group
err := r.Get(ctx, types.NamespacedName{Name: crdName}, crd)
if client.IgnoreNotFound(err) != nil {
return reconcile.Result{}, err
}
if err != nil { // CRD doesn't exist
return r.createCRD(ctx, mrd)
}
// CRD exists, ensure it's up to date
return r.updateCRD(ctx, mrd, crd)
}
func (r *MRDReconciler) createCRD(ctx context.Context, mrd *xpv1alpha1.ManagedResourceDefinition) (reconcile.Result, error) {
crd := &apiextv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: mrd.Spec.Names.Plural + "." + mrd.Spec.Group,
OwnerReferences: []metav1.OwnerReference{{
APIVersion: mrd.APIVersion,
Kind: mrd.Kind,
Name: mrd.Name,
UID: mrd.UID,
Controller: pointer.Bool(true),
}},
},
Spec: mrd.Spec.CustomResourceDefinitionSpec,
}
return reconcile.Result{}, r.Create(ctx, crd)
}
```
### Step 6: Update build and CI processes
Update your build process to generate MRDs alongside CRDs:
{{< tabs >}}
{{< tab "Makefile" >}}
```makefile
# Update your Makefile to generate both CRDs and MRDs
.PHONY: generate
generate: controller-gen
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
$(CONTROLLER_GEN) crd:allowDangerousTypes=true paths="./..." output:crd:artifacts:config=package/crds
$(CONTROLLER_GEN) mrd:allowDangerousTypes=true paths="./..." output:mrd:artifacts:config=package/mrds
# Add MRD generation tool
MRD_GEN = $(shell pwd)/bin/mrd-gen
.PHONY: mrd-gen
mrd-gen: ## Download mrd-gen locally if necessary.
$(call go-get-tool,$(MRD_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.13.0)
# Update package generation to include MRDs
.PHONY: build-package
build-package: generate
mkdir -p package/
cp package/crds/*.yaml package/
cp package/mrds/*.yaml package/
echo "# Package metadata with SafeStart capability" > package/provider.yaml
echo "apiVersion: meta.pkg.crossplane.io/v1" >> package/provider.yaml
echo "kind: Provider" >> package/provider.yaml
echo "spec:" >> package/provider.yaml
echo " capabilities:" >> package/provider.yaml
echo " - name: SafeStart" >> package/provider.yaml
```
{{< /tab >}}
{{< tab "GitHub Actions" >}}
```yaml
name: Build and Test SafeStart Provider
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test-safestart:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Run Tests
run: make test
- name: Generate MRDs
run: make generate
- name: Verify MRD Generation
run: |
if [ ! -d "package/mrds" ]; then
echo "MRD generation failed"
exit 1
fi
echo "Generated MRDs:"
ls -la package/mrds/
- name: Test SafeStart Integration
run: |
# Start local cluster
make kind-up
make install-crossplane-v2
# Install provider with SafeStart
make install-provider
# Verify MRDs created but inactive
kubectl get mrds
kubectl get mrds -o jsonpath='{.items[*].spec.state}' | grep -q "Inactive"
# Test activation policy
kubectl apply -f examples/activation-policy.yaml
# Verify resources activate
sleep 30
kubectl get mrds -o jsonpath='{.items[*].spec.state}' | grep -q "Active"
# Test resource creation
kubectl apply -f examples/example-resource.yaml
kubectl wait --for=condition=Ready --timeout=300s -f examples/example-resource.yaml
```
{{< /tab >}}
{{< /tabs >}}
### Step 7: Add connection details documentation
Document connection details in your MRDs to help users understand resource
capabilities:
```yaml
# Example generated MRD with connection details
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceDefinition
metadata:
name: databases.rds.aws.example.io
spec:
group: rds.aws.example.io
names:
kind: Database
plural: databases
scope: Namespaced
# SafeStart-specific fields
connectionDetails:
- name: endpoint
description: "The RDS instance connection endpoint"
type: string
fromConnectionSecretKey: endpoint
- name: port
description: "The port number for database connections"
type: integer
fromConnectionSecretKey: port
- name: username
description: "The master username for the database"
type: string
fromConnectionSecretKey: username
- name: password
description: "The master password for the database"
type: string
fromConnectionSecretKey: password
- name: ca_certificate
description: "The CA certificate for SSL connections"
type: string
fromConnectionSecretKey: ca_certificate
# Standard CRD specification
versions:
- name: v1alpha1
served: true
storage: true
# ... rest of CRD spec
```
## Testing SafeStart implementation
### Unit testing
Test your MRD generation and controller logic:
```go
func TestMRDGeneration(t *testing.T) {
// Test that MRDs are generated with correct connection details
mrd := generateMRDForResource("Database")
assert.Equal(t, "databases.rds.aws.example.io", mrd.Name)
assert.NotEmpty(t, mrd.Spec.ConnectionDetails)
// Verify specific connection details
endpointDetail := findConnectionDetail(mrd, "endpoint")
assert.NotNil(t, endpointDetail)
assert.Equal(t, "string", endpointDetail.Type)
assert.Contains(t, endpointDetail.Description, "endpoint")
}
func TestMRDActivation(t *testing.T) {
// Test MRD activation creates CRD
ctx := context.Background()
mrd := &v1alpha1.ManagedResourceDefinition{
Spec: v1alpha1.ManagedResourceDefinitionSpec{
State: &[]v1alpha1.ResourceState{v1alpha1.ResourceStateActive}[0],
},
}
reconciler := &MRDReconciler{Client: fakeClient}
result, err := reconciler.Reconcile(ctx, reconcile.Request{})
assert.NoError(t, err)
assert.False(t, result.Requeue)
// Verify CRD was created
crd := &apiextv1.CustomResourceDefinition{}
err = fakeClient.Get(ctx, types.NamespacedName{Name: "databases.rds.aws.example.io"}, crd)
assert.NoError(t, err)
}
```
### Integration testing
Test SafeStart behavior in a real cluster:
```bash
#!/bin/bash
set -e
echo "Starting SafeStart integration test..."
# Install Crossplane v2.0
kubectl create namespace crossplane-system
helm install crossplane crossplane-stable/crossplane \
--namespace crossplane-system \
--version v2.0.0 \
--wait
# Install provider with SafeStart
kubectl apply -f - <<EOF
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-example
spec:
package: registry.example.com/provider-example:latest
capabilities:
- name: SafeStart
EOF
# Wait for provider installation
kubectl wait --for=condition=Healthy provider/provider-example --timeout=300s
# Verify MRDs created but inactive
echo "Checking MRD states..."
MRD_COUNT=$(kubectl get mrds --no-headers | wc -l)
INACTIVE_COUNT=$(kubectl get mrds -o jsonpath='{.items[*].spec.state}' | grep -o "Inactive" | wc -l)
if [ "$MRD_COUNT" -eq "$INACTIVE_COUNT" ]; then
echo "✓ All MRDs are inactive as expected"
else
echo "✗ Some MRDs are unexpectedly active"
exit 1
fi
# Test activation policy
kubectl apply -f - <<EOF
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: test-policy
spec:
activations:
- "databases.rds.aws.example.io"
EOF
# Wait for activation
sleep 10
# Verify activation worked
ACTIVE_COUNT=$(kubectl get mrd databases.rds.aws.example.io -o jsonpath='{.spec.state}' | grep -o "Active" | wc -l)
if [ "$ACTIVE_COUNT" -eq "1" ]; then
echo "✓ MRD activation successful"
else
echo "✗ MRD activation failed"
exit 1
fi
# Test resource creation
kubectl apply -f - <<EOF
apiVersion: rds.aws.example.io/v1alpha1
kind: Database
metadata:
name: test-db
namespace: default
spec:
forProvider:
engine: postgres
region: us-east-1
EOF
# Verify resource creation
kubectl wait --for=condition=Ready database/test-db --timeout=300s --namespace default
echo "✓ SafeStart integration test passed"
```
## Migration considerations
### For existing users
When you add SafeStart to an existing provider:
**Breaking change considerations:**
- Existing installations will continue working (backward compatibility)
- New installations will have inactive MRDs by default
- Users need activation policies for new installations
**Migration strategy:**
```yaml
# Provide migration documentation like:
# For users upgrading to SafeStart-enabled provider v2.0:
# 1. Existing resources continue working unchanged
# 2. For new installations, create activation policy:
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: legacy-compatibility
spec:
activations:
- "*.aws.example.io" # Activate all resources (legacy behavior)
```
### Version compatibility matrix
Document version compatibility clearly:
| Provider Version | Crossplane Version | SafeStart Support | Notes |
|------------------|-------------------|------------------|-------|
| v1.x | v1.x - v2.x | No | Legacy CRD-only mode |
| v2.0 | v2.0+ | Yes | Full SafeStart support |
| v2.1 | v2.0+ | Yes | Enhanced MRD features |
## Documentation requirements
Update your provider documentation to include:
### README updates
```markdown
# Provider Example
## SafeStart Support
This provider supports SafeStart capability, which provides:
- Selective resource activation
- Improved performance for large providers
- Connection details documentation
### Quick Start with SafeStart
1. Install the provider:
```yaml
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-example
spec:
package: registry.example.com/provider-example:v2.0.0
```
2. Create activation policy:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: example-resources
spec:
activations:
- "databases.rds.aws.example.io"
- "*.s3.aws.example.io"
```
3. Create resources normally - only activated resources work.
```
### Connection details documentation
Document what connection details each resource provides:
```markdown
## Connection Details Reference
### Database (`databases.rds.aws.example.io`)
- `endpoint` (string): RDS instance connection endpoint
- `port` (integer): Database connection port
- `username` (string): Master database username
- `password` (string): Master database password
- `ca_certificate` (string): CA certificate for SSL connections
### Storage Bucket (`buckets.s3.aws.example.io`)
- `bucket_name` (string): The S3 bucket name
- `region` (string): AWS region where bucket is located
- `arn` (string): Full ARN of the S3 bucket
```
## Troubleshooting
### Common issues
**MRDs not activating:**
```bash
# Check activation policy exists and matches
kubectl get mrap
kubectl describe mrap my-policy
# Verify MRD exists
kubectl get mrd my-resource.provider.example.io
kubectl describe mrd my-resource.provider.example.io
```
**CRDs not created:**
```bash
# Check MRD controller logs
kubectl logs -n crossplane-system deployment/provider-example
# Verify RBAC permissions
kubectl auth can-i create customresourcedefinitions --as=system:serviceaccount:crossplane-system:provider-example
```
**Resource creation fails:**
```bash
# Verify MRD is active
kubectl get mrd my-resource.provider.example.io -o jsonpath='{.spec.state}'
# Check if CRD exists
kubectl get crd my-resource.provider.example.io
# Look for controller errors
kubectl describe my-resource my-instance
```
## Best practices
### Performance optimization
- Start with inactive MRDs for providers with >20 resources
- Document recommended activation patterns for common use cases
- Provide environment-specific activation policy examples
### User experience
- Include helpful error messages when resources aren't activated
- Provide clear migration guides for existing users
- Document connection details thoroughly
### Testing strategy
- Test both with and without SafeStart in CI
- Verify activation/deactivation cycles work correctly
- Test resource creation after activation
SafeStart provides significant value for large providers and improves the
overall Crossplane user experience. Following this guide helps ensure your
implementation is robust, well-documented, and user-friendly.

View File

@ -0,0 +1,691 @@
---
title: Writing MRD Activation Policies
weight: 150
description: Learn how to create effective activation policies for managed resources
---
ManagedResourceActivationPolicy (MRAP) provides powerful pattern-based control
over which Managed Resource Definitions (MRDs) become active in your cluster.
This guide shows how to write effective activation policies for different
scenarios.
## Default activation policy
Crossplane automatically creates a default ManagedResourceActivationPolicy when
installed. Understanding and configuring this default policy is crucial for
effective MRD management.
### What is the default MRAP?
The default MRAP is automatically created by Crossplane and activates managed
resources according to configurable patterns. By default, it uses a wildcard
pattern that activates **all** managed resources:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: crossplane-default-activation-policy
spec:
activations:
- "*" # Activates all managed resources
```
{{< hint "important" >}}
The default `"*"` pattern means SafeStart providers will still create all CRDs,
defeating the performance benefits. Most users should customize this behavior.
{{< /hint >}}
### Configuring default activation with Helm
Configure the default activation policy during Crossplane installation:
```yaml
# values.yaml for Crossplane Helm installation
provider:
defaultActivations:
- "*" # Default: activate everything (not recommended for large providers)
```
**Recommended configurations:**
{{< tabs >}}
{{< tab "Production (Selective)" >}}
```yaml
# Recommended: Disable default activation, use targeted policies
provider:
defaultActivations: null # or []
# Then create provider-specific MRAPs
# This provides better control and performance
```
{{< /tab >}}
{{< tab "Development (Permissive)" >}}
```yaml
# For development environments where you want broad access
provider:
defaultActivations:
- "*.aws.crossplane.io"
- "*.gcp.crossplane.io"
- "*.azure.crossplane.io"
```
{{< /tab >}}
{{< tab "Service-Specific" >}}
```yaml
# Activate only specific service categories
provider:
defaultActivations:
- "*.rds.aws.crossplane.io" # All RDS resources
- "*.s3.aws.crossplane.io" # All S3 resources
- "instances.ec2.aws.crossplane.io" # Only EC2 instances
```
{{< /tab >}}
{{< /tabs >}}
### Modifying the default policy after installation
The default activation policy can be modified directly and changes will persist:
```shell
# View current default policy
kubectl get mrap crossplane-default-activation-policy -o yaml
# Permanently modify to disable default activation
kubectl patch mrap crossplane-default-activation-policy --type='merge' \
-p='{"spec":{"activations":["nonexistent.example.com"]}}'
# Or remove all activations
kubectl patch mrap crossplane-default-activation-policy --type='merge' \
-p='{"spec":{"activations":[]}}'
# Or delete the default policy entirely
kubectl delete mrap crossplane-default-activation-policy
```
{{< hint "note" >}}
**Changes to the default policy are permanent.** Once the default MRAP exists,
Crossplane will not modify it. The Helm chart `provider.defaultActivations`
value is only used when creating the policy if it doesn't already exist.
{{< /hint >}}
### Controlling default activation for new installations
The Helm chart value only affects the **initial creation** of the default policy:
```shell
# This only matters for NEW installations or when the default policy doesn't exist
helm upgrade crossplane crossplane-stable/crossplane \
--set provider.defaultActivations=null \
--namespace crossplane-system --reuse-values
```
If you want to reset the default policy to match new Helm values:
```shell
# Delete existing policy so Crossplane recreates it from Helm values
kubectl delete mrap crossplane-default-activation-policy
# Restart Crossplane to recreate the policy with current Helm values
kubectl rollout restart deployment/crossplane -n crossplane-system
```
### Best practices for default activation
**Recommended approach:**
1. **Disable default activation** by setting `provider.defaultActivations: null`
2. **Create targeted MRAPs** for each provider or service category
3. **Use specific patterns** rather than wildcards when possible
```shell
# 1. Install Crossplane with no default activation
helm install crossplane crossplane-stable/crossplane \
--set provider.defaultActivations=null
# 2. Create provider-specific activation policies
kubectl apply -f - <<EOF
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: aws-core-services
spec:
activations:
- "instances.ec2.aws.crossplane.io"
- "*.rds.aws.crossplane.io"
- "buckets.s3.aws.crossplane.io"
EOF
```
**Why this approach is better:**
- **Performance**: Only activates resources you actually use
- **Security**: Principle of least privilege for resource access
- **Clarity**: Explicit about which resources are available
- **Maintainability**: Easier to understand and modify activation patterns
## Policy basics
Beyond the default policy, you can create custom activation policies that
specify which MRDs should be activated using pattern matching:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: my-policy
spec:
activations:
- "exact-match.provider.example.com"
- "*.wildcard.example.com"
- "prefix-*.example.com"
```
{{< hint "note" >}}
Multiple activation policies can exist simultaneously. Their activations are
combined, so any MRD matched by any policy becomes active.
{{< /hint >}}
## Activation patterns
### Exact matching
Activate specific MRDs by their full name:
```yaml
spec:
activations:
- instances.ec2.aws.crossplane.io
- buckets.s3.aws.crossplane.io
- databases.rds.aws.crossplane.io
```
Use exact matching when:
* You know exactly which resources you need
* You want fine-grained control over individual resources
* Security policies require explicit resource approval
### Wildcard matching
Use wildcards to activate groups of related resources:
```yaml
spec:
activations:
- "*.rds.aws.crossplane.io" # All RDS resources
- "*.storage.gcp.crossplane.io" # All GCP storage resources
- "*.compute.azure.crossplane.io" # All Azure compute resources
```
{{< hint "tip" >}}
Wildcard patterns only support prefix matching. The `*` must be at the
beginning of the pattern and match one or more DNS label components.
{{< /hint >}}
### Provider-wide activation
Activate all resources from a specific provider:
```yaml
spec:
activations:
- "*.aws.crossplane.io" # All AWS resources
- "*.gcp.crossplane.io" # All GCP resources
- "*.azure.crossplane.io" # All Azure resources
```
Use provider-wide activation when:
* You're migrating from non-SafeStart providers
* Your applications use diverse resources from a single provider
* Development environments need broad resource access
## Environment-based policies
### Development environment
Activate minimal resources for development:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: development-resources
namespace: development
spec:
activations:
# Basic compute
- instances.ec2.aws.crossplane.io
- "*.compute.gcp.crossplane.io"
# Storage
- buckets.s3.aws.crossplane.io
- "*.storage.azure.crossplane.io"
# Databases
- "*.rds.aws.crossplane.io"
- instances.sql.gcp.crossplane.io
```
### Staging environment
Include additional resources for integration testing:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: staging-resources
namespace: staging
spec:
activations:
# Everything from development
- instances.ec2.aws.crossplane.io
- "*.compute.gcp.crossplane.io"
- buckets.s3.aws.crossplane.io
- "*.storage.azure.crossplane.io"
- "*.rds.aws.crossplane.io"
- instances.sql.gcp.crossplane.io
# Additional staging needs
- "*.networking.aws.crossplane.io"
- "*.iam.aws.crossplane.io"
- clusters.eks.aws.crossplane.io
- "*.monitoring.gcp.crossplane.io"
```
### Production environment
Activate all necessary resources:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: production-resources
namespace: production
spec:
activations:
# Broad activation for production flexibility
- "*.aws.crossplane.io"
- "*.gcp.crossplane.io"
- "*.azure.crossplane.io"
```
## Service-based policies
### Database services
Create policies focused on specific service categories:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: database-services
spec:
activations:
# Relational databases
- "*.rds.aws.crossplane.io"
- instances.sql.gcp.crossplane.io
- servers.postgresql.azure.crossplane.io
- servers.mysql.azure.crossplane.io
# NoSQL databases
- tables.dynamodb.aws.crossplane.io
- instances.spanner.gcp.crossplane.io
- accounts.cosmosdb.azure.crossplane.io
# Caching
- clusters.elasticache.aws.crossplane.io
- instances.memorystore.gcp.crossplane.io
- caches.redis.azure.crossplane.io
```
### Networking services
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: networking-services
spec:
activations:
# Core networking
- vpcs.ec2.aws.crossplane.io
- subnets.ec2.aws.crossplane.io
- networks.compute.gcp.crossplane.io
- subnetworks.compute.gcp.crossplane.io
- virtualnetworks.network.azure.crossplane.io
# Load balancing
- "*.elbv2.aws.crossplane.io"
- "*.compute.gcp.crossplane.io"
- loadbalancers.network.azure.crossplane.io
# DNS and routing
- "*.route53.aws.crossplane.io"
- "*.dns.gcp.crossplane.io"
- zones.dns.azure.crossplane.io
```
## Team-based policies
### Platform team
Broad access for platform engineering:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: platform-team-resources
spec:
activations:
# Infrastructure management
- "*.iam.aws.crossplane.io"
- "*.iam.gcp.crossplane.io"
- "*.authorization.azure.crossplane.io"
# Networking and security
- "*.ec2.aws.crossplane.io"
- "*.compute.gcp.crossplane.io"
- "*.network.azure.crossplane.io"
# Monitoring and logging
- "*.cloudwatch.aws.crossplane.io"
- "*.monitoring.gcp.crossplane.io"
- "*.insights.azure.crossplane.io"
```
### Application team
Resources needed by application developers:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: app-team-resources
spec:
activations:
# Compute resources
- instances.ec2.aws.crossplane.io
- clusters.eks.aws.crossplane.io
- clusters.gke.gcp.crossplane.io
# Storage
- buckets.s3.aws.crossplane.io
- buckets.storage.gcp.crossplane.io
- accounts.storage.azure.crossplane.io
# Databases (read-only access through compositions)
- "*.rds.aws.crossplane.io"
- instances.sql.gcp.crossplane.io
```
## Dynamic activation patterns
### Conditional activation
Use multiple policies to create conditional resource activation:
```yaml
# Base resources always active
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: base-resources
spec:
activations:
- instances.ec2.aws.crossplane.io
- buckets.s3.aws.crossplane.io
---
# Optional resources for advanced features
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: advanced-resources
spec:
activations:
- clusters.eks.aws.crossplane.io
- "*.lambda.aws.crossplane.io"
```
You can delete the `advanced-resources` policy to quickly deactivate optional
resources while keeping base functionality.
### Feature flag patterns
Use labels and naming to create feature flag-like behavior:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: feature-ml-services
labels:
feature: machine-learning
environment: production
spec:
activations:
- "*.sagemaker.aws.crossplane.io"
- "*.ml.gcp.crossplane.io"
- "*.cognitiveservices.azure.crossplane.io"
```
## Validation and testing
### Check activation status
Verify your policies are working correctly:
```shell
# List all activation policies
kubectl get mrap
# Check specific policy status
kubectl describe mrap my-policy
# See which MRDs are currently active
kubectl get mrds --field-selector spec.state=Active
# Count active MRDs by provider
kubectl get mrds -l crossplane.io/provider=provider-aws --field-selector spec.state=Active | wc -l
```
### Test resource creation
Verify activated resources work correctly:
```shell
# Try creating a managed resource
kubectl apply -f - <<EOF
apiVersion: s3.aws.crossplane.io/v1alpha1
kind: Bucket
metadata:
name: test-activation
spec:
forProvider:
region: us-east-1
EOF
# Check if the resource was created successfully
kubectl get bucket test-activation
kubectl describe bucket test-activation
```
### Policy debugging
Common issues and solutions:
```shell
# Check if MRD exists but isn't active
kubectl get mrd my-resource.provider.example.com
kubectl describe mrd my-resource.provider.example.com
# Verify policy is matching correctly
kubectl get mrap my-policy -o yaml | grep -A20 status
# Look for controller errors
kubectl logs -n crossplane-system deployment/crossplane | grep -i mrd
```
## Performance considerations
### Activation overhead
Each activation creates a CRD, which has resource overhead:
```yaml
# Efficient - activates exactly what's needed
spec:
activations:
- instances.ec2.aws.crossplane.io
- buckets.s3.aws.crossplane.io
# Less efficient - activates everything
spec:
activations:
- "*.aws.crossplane.io" # Could be 200+ resources
```
### Policy consolidation
Multiple small policies vs. few large policies:
{{< tabs >}}
{{< tab "Multiple Small Policies (Recommended)" >}}
```yaml
# Easier to manage and understand
---
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: compute-resources
spec:
activations:
- "*.ec2.aws.crossplane.io"
- "*.compute.gcp.crossplane.io"
---
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: storage-resources
spec:
activations:
- "*.s3.aws.crossplane.io"
- "*.storage.gcp.crossplane.io"
```
{{< /tab >}}
{{< tab "Single Large Policy" >}}
```yaml
# Harder to manage but fewer resources
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: all-resources
spec:
activations:
- "*.ec2.aws.crossplane.io"
- "*.compute.gcp.crossplane.io"
- "*.s3.aws.crossplane.io"
- "*.storage.gcp.crossplane.io"
# ... many more activations
```
{{< /tab >}}
{{< /tabs >}}
## Policy lifecycle management
### GitOps workflow
Store activation policies in Git for proper change management:
```yaml
# clusters/production/activation-policies/
production-compute.yaml
production-storage.yaml
production-networking.yaml
# clusters/staging/activation-policies/
staging-core.yaml
staging-experimental.yaml
# clusters/development/activation-policies/
development-minimal.yaml
```
### Versioning strategies
Use metadata to track policy versions:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: production-resources
labels:
version: "v2.1.0"
environment: production
team: platform-engineering
annotations:
policy.crossplane.io/description: "Production resource activation policy"
policy.crossplane.io/last-updated: "2024-01-15"
policy.crossplane.io/approved-by: "platform-team"
spec:
activations:
- "*.aws.crossplane.io"
```
## Integration with compositions
### Composition compatibility
Compositions work with both active and inactive MRDs, but resource creation
only succeeds when MRDs are active:
```yaml
# This composition can exist regardless of MRD state
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: webapp-stack
spec:
compositeTypeRef:
apiVersion: platform.example.com/v1alpha1
kind: WebApp
resources:
- name: database
base:
apiVersion: rds.aws.crossplane.io/v1alpha1
kind: DBInstance
# This only works if RDS MRDs are active
- name: storage
base:
apiVersion: s3.aws.crossplane.io/v1alpha1
kind: Bucket
# This only works if S3 MRDs are active
```
### Activation dependencies
Document activation requirements in compositions:
```yaml
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: ml-pipeline
annotations:
composition.crossplane.io/required-mrds: |
- "*.sagemaker.aws.crossplane.io"
- "*.s3.aws.crossplane.io"
- "*.iam.aws.crossplane.io"
spec:
# ... composition definition
```
This helps operators understand which activation policies are needed for
specific compositions to work correctly.

View File

@ -0,0 +1,237 @@
---
title: Managed Resource Definitions
weight: 10
description: Understand Managed Resource Definitions (MRDs) and selective resource activation
---
Managed Resource Definitions (MRDs) provide a lightweight abstraction over
Kubernetes Custom Resource Definitions (CRDs) that enables selective
installation and better documentation of managed resources.
{{< hint "note" >}}
MRDs are available in Crossplane v2.0+ as an alpha feature.
{{< /hint >}}
## What are managed resource definitions?
A Managed Resource Definition (MRD) is essentially a CRD with additional
metadata that provides:
* **Connection details schema** - Documents what connection details the
managed resource provides
* **Activation control** - Controls whether the underlying CRD gets installed
in your cluster
* **Resource discovery** - Makes it easier to understand what resources are
available
**Every managed resource in a provider package has an associated MRD.** The MRD
contains the same schema as the CRD, plus additional Crossplane-specific
metadata.
```mermaid
flowchart LR
provider(Provider Package)
mrd(MRD)
crd(CRD)
mr(Managed Resource)
provider --> mrd
mrd --"when active"--> crd
crd --> mr
style mrd fill:#e1f5fe
style crd fill:#f3e5f5
```
## Why use managed resource definitions?
MRDs solve several challenges with traditional provider packages:
### Performance optimization
Installing a provider creates CRDs for _every_ managed resource the provider
supports. For large providers like AWS, this can mean hundreds of CRDs that
you may never use.
**MRDs let you install only the CRDs you need**, reducing Kubernetes API
server overhead and improving cluster performance.
### Connection details discovery
Understanding what connection details a managed resource provides requires
reading provider source code or trial-and-error testing.
**MRDs document connection details in the schema**, making it clear what
credentials and endpoints each resource provides.
### Selective installation
Different environments may need different subsets of managed resources. A
development environment might only need basic resources, while production
needs the full set.
**MRDs enable environment-specific resource activation** through policies.
## How MRDs work
When you install a provider package, Crossplane creates:
1. **MRDs for all resources** - Every managed resource gets an MRD
2. **CRDs only when activated** - Crossplane creates CRDs only for active MRDs
3. **Activation policies** - ManagedResourceActivationPolicy controls which
MRDs become active
### MRD lifecycle
```mermaid
flowchart TD
install[Install Provider]
createMRD[Create MRDs]
checkPolicy{Activation Policy<br/>Matches?}
activate[Activate MRD]
createCRD[Create CRD]
inactive[MRD Inactive]
install --> createMRD
createMRD --> checkPolicy
checkPolicy -->|Yes| activate
checkPolicy -->|No| inactive
activate --> createCRD
style activate fill:#c8e6c9
style inactive fill:#ffcdd2
```
### MRD states
MRDs can be in one of two states:
* **Active** - The underlying CRD exists and you can create managed resources
* **Inactive** - No CRD exists, managed resource creation fails
You can change an MRD's state by:
* Editing the MRD directly (`spec.state: Active`)
* Using a ManagedResourceActivationPolicy
* Provider package capabilities (SafeStart)
## Connection details schema
MRDs document the connection details that managed resources provide. This makes
it easier to understand what credentials and endpoints you get when creating
resources.
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceDefinition
metadata:
name: instances.ec2.aws.crossplane.io
spec:
connectionDetails:
- name: endpoint
description: The connection endpoint for the database
type: string
- name: port
description: The port number for connections
type: integer
- name: username
description: The master username for the database
type: string
- name: password
description: The master password for the database
type: string
fromConnectionSecretKey: password
```
The `connectionDetails` field documents:
* **Connection detail names** - What keys appear in connection secrets
* **Descriptions** - What each connection detail contains
* **Types** - The data type of each detail
* **Source keys** - How details map from provider responses
## Managed Resource Activation Policy
ManagedResourceActivationPolicy (MRAP) provides pattern-based control over
which MRDs become active. This is more scalable than manually activating
individual MRDs.
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: aws-core-resources
spec:
activations:
- "instances.ec2.aws.crossplane.io" # Specific MRD
- "*.rds.aws.crossplane.io" # All RDS resources
- "buckets.s3.aws.crossplane.io" # S3 buckets
```
### Activation patterns
MRAP supports several activation patterns:
* **Exact match**: `instances.ec2.aws.crossplane.io`
* **Wildcard prefix**: `*.rds.aws.crossplane.io` (all RDS resources)
* **Provider wildcard**: `*.aws.crossplane.io` (all AWS resources)
Multiple MRAPs can exist, and their activations are combined.
## Provider capabilities
Providers can declare capabilities that affect MRD behavior:
### SafeStart capability
Providers with the `SafeStart` capability start with all MRDs inactive by
default. This prevents performance issues when installing large providers.
Without SafeStart, all MRDs are active by default for backward compatibility.
```yaml
# In provider package metadata
spec:
capabilities:
- name: SafeStart
```
{{< hint "note" >}}
Implementing SafeStart requires significant provider code changes. Provider
developers should follow the
[SafeStart implementation guide]({{< ref "../guides/implementing-safestart" >}})
for detailed technical requirements and examples.
{{< /hint >}}
### SafeStart implementation examples
The Crossplane community has implemented SafeStart in several providers:
* **provider-nop** - [Reference implementation](https://github.com/crossplane-contrib/provider-nop/pull/24)
showing SafeStart integration with both namespaced and cluster-scoped resources
* **provider-aws** - Large provider demonstrating SafeStart performance benefits
These implementations provide real-world examples of:
- MRD controller integration
- Build process modifications
- Testing strategies for SafeStart behavior
- Migration approaches for existing users
## Key concepts
Understanding these terms helps when working with MRDs:
* **MRD** - The definition that may or may not have an active CRD
* **MRAP** - Policy that controls which MRDs become active
* **Active state** - MRD has an underlying CRD, resources can be created
* **Inactive state** - No CRD exists, resource creation fails
* **SafeStart** - Provider capability that defaults MRDs to inactive
* **Connection details schema** - Documentation of what connection details
a managed resource provides
## Relationship to other Crossplane features
MRDs integrate with existing Crossplane concepts:
* **Providers** - Create MRDs when installed
* **Managed resources** - Can only be created when their MRD is active
* **Compositions** - Can reference both active and inactive managed resources
(composition validation occurs at render time)
* **Claims** - Work normally once the underlying managed resources are active
MRDs are backward compatible. Existing providers and compositions continue to
work without modification.

View File

@ -0,0 +1,372 @@
---
title: Provider Capabilities
weight: 20
description: Understand provider capabilities and how they affect resource behavior
---
Provider capabilities are declarative features that providers can implement to
modify their behavior and integration with Crossplane. Capabilities enable
providers to opt into new features while maintaining backward compatibility.
## What are provider capabilities?
Provider capabilities are metadata declarations in provider packages that tell
Crossplane how the provider should behave. They're similar to feature flags
but are declared at the package level.
```yaml
# In provider package metadata
apiVersion: meta.pkg.crossplane.io/v1alpha1
kind: Provider
metadata:
name: provider-aws
spec:
capabilities:
- name: SafeStart
- name: CustomCapability
```
Crossplane reads these capabilities and modifies its behavior when installing
and managing the provider.
## Available capabilities
### SafeStart
The `SafeStart` capability changes how Managed Resource Definitions (MRDs) are
activated when the provider is installed.
**Without SafeStart:**
- All MRDs are automatically activated
- All corresponding CRDs are created immediately
- Compatible with legacy providers and existing workflows
**With SafeStart:**
- All MRDs start in `Inactive` state
- No CRDs are created until MRDs are explicitly activated
- Reduces initial resource overhead and improves performance
```yaml
spec:
capabilities:
- name: SafeStart
```
{{< hint "tip" >}}
SafeStart is particularly valuable for large providers like AWS that define
hundreds of managed resources. It prevents performance issues by avoiding the
creation of unused CRDs.
{{< /hint >}}
#### When to use SafeStart
Use SafeStart when:
* Your provider defines many managed resources (>50)
* Users typically need only a subset of available resources
* Installation performance and resource usage are concerns
* You want to provide better resource discovery through MRDs
Don't use SafeStart when:
* Your provider has few managed resources (<20)
* Most users need all available resources
* Backward compatibility with existing installations is critical
* Your users aren't ready to manage resource activation
## Capability matching
Crossplane supports flexible matching for capability names:
* **Exact match**: `SafeStart`
* **Case variations**: `safestart`, `safe-start`, `SafeStart`
* **Fuzzy matching**: Handles common spelling variations
This flexibility prevents issues when providers use different naming conventions.
## How capabilities affect installation
The provider installation process changes based on declared capabilities:
```mermaid
flowchart TD
install[Install Provider Package]
readCaps[Read Capabilities]
checkSafe{Has SafeStart?}
activateAll[Activate All MRDs]
keepInactive[Keep MRDs Inactive]
createCRDs[Create All CRDs]
waitPolicy[Wait for Activation Policy]
install --> readCaps
readCaps --> checkSafe
checkSafe -->|No| activateAll
checkSafe -->|Yes| keepInactive
activateAll --> createCRDs
keepInactive --> waitPolicy
style keepInactive fill:#c8e6c9
style waitPolicy fill:#fff3e0
```
## Provider compatibility
### Legacy providers
Providers without any capabilities work exactly as before:
* All MRDs are active by default
* All CRDs are created immediately
* No changes required for existing compositions or configurations
### Modern providers
Providers with SafeStart capability require additional setup:
* Create ManagedResourceActivationPolicy to activate needed resources
* Verify required CRDs exist before creating managed resources
* Use MRD connection details documentation for resource planning
## Implementing SafeStart in providers
SafeStart implementation requires several technical changes to provider code and
build processes. This section provides an overview - see the
[complete SafeStart implementation guide]({{< ref "../guides/implementing-safestart" >}})
for detailed instructions.
### Key implementation requirements
**Code changes:**
- Add MRD controller logic to handle activation/deactivation
- Support both namespaced and cluster-scoped resources
- Generate MRDs with connection details documentation
- Implement CRD lifecycle management
**Build process changes:**
- Update Makefile to generate MRDs alongside CRDs
- Modify CI/CD to test SafeStart behavior
- Include MRDs in provider package artifacts
**RBAC updates:**
SafeStart providers need additional permissions to manage CRDs dynamically:
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: my-provider-system
rules:
# Standard provider permissions
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
# Additional SafeStart permissions
- apiGroups: ["apiextensions.k8s.io"]
resources: ["customresourcedefinitions"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["apiextensions.crossplane.io"]
resources: ["managedresourcedefinitions"]
verbs: ["get", "list", "watch", "update", "patch"]
```
### Provider package metadata
Declare SafeStart capability in your provider package:
```yaml
apiVersion: meta.pkg.crossplane.io/v1
kind: Provider
metadata:
name: my-provider
spec:
package: registry.example.com/my-provider:v2.0.0
capabilities:
- name: SafeStart
```
### MRD generation with connection details
Generate MRDs that document connection details for better user experience:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceDefinition
metadata:
name: databases.rds.aws.example.io
spec:
group: rds.aws.example.io
names:
kind: Database
plural: databases
scope: Namespaced
# Connection details documentation
connectionDetails:
- name: endpoint
description: "The RDS instance connection endpoint"
type: string
fromConnectionSecretKey: endpoint
- name: port
description: "The port number for database connections"
type: integer
fromConnectionSecretKey: port
- name: username
description: "The master username for the database"
type: string
fromConnectionSecretKey: username
- name: password
description: "The master password for the database"
type: string
fromConnectionSecretKey: password
```
### Implementation examples
The [provider-nop SafeStart implementation](https://github.com/crossplane-contrib/provider-nop/pull/24)
demonstrates:
- Adding both namespaced and cluster-scoped resource variants
- MRD controller integration
- Build process updates for SafeStart support
- Testing strategies for SafeStart behavior
{{< hint "tip" >}}
See the [complete SafeStart implementation guide]({{< ref "../guides/implementing-safestart" >}})
for step-by-step instructions, code examples, and testing strategies.
{{< /hint >}}
### Migration considerations
When adding SafeStart to existing providers:
**Backward compatibility:**
- Existing provider installations continue working unchanged
- New installations start with inactive MRDs
- Provide migration documentation for users
**Version strategy:**
```yaml
# Document version compatibility clearly
# Provider v1.x: Traditional CRD installation
# Provider v2.0+: SafeStart support with MRDs
```
## Best practices
### For provider developers
**Do use SafeStart when:**
* Your provider has >50 managed resources
* Resource activation patterns vary by environment
* Performance optimization is important
**Document capabilities clearly:**
* Explain what each capability does
* Provide migration guides for existing users
* Include examples of activation policies
**Test compatibility:**
* Verify behavior with and without capabilities
* Test with different Crossplane versions
* Validate RBAC permissions
### For platform operators
**Plan activation policies:**
```yaml
# Development environment - minimal resources
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: dev-resources
spec:
activations:
- "databases.*.example.com"
- "buckets.*.example.com"
---
# Production environment - comprehensive resources
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: prod-resources
spec:
activations:
- "*.example.com" # Activate all resources
```
**Monitor activation status:**
```shell
# Check which MRDs are active
kubectl get mrds -l provider=my-provider
# Verify activation policies
kubectl get mrap -o wide
# Monitor resource usage
kubectl top nodes
```
## Future capabilities
The capability system is extensible. Future capabilities might include:
* **ResourceQuotas** - Automatic resource limit management
* **NetworkPolicies** - Provider-specific network isolation
* **CustomValidation** - Enhanced resource validation
* **TelemetryOpt** - Telemetry and observability features
## Troubleshooting capabilities
### Common issues
**MRDs not activating:**
```shell
# Check if provider has SafeStart capability
kubectl get provider my-provider -o yaml | grep -A5 capabilities
# Verify activation policy exists and matches
kubectl get mrap
kubectl describe mrap my-policy
```
**CRDs not created:**
```shell
# Check MRD activation status
kubectl get mrd my-resource.example.com
# Look for controller errors
kubectl logs -n crossplane-system deployment/crossplane
```
**Provider installation fails:**
```shell
# Check provider conditions
kubectl describe provider my-provider
# Look for RBAC issues
kubectl get events --field-selector reason=FailedCreate
```
### Debug commands
```shell
# List all providers and their capabilities
kubectl get providers -o jsonpath='{range .items[*]}{.metadata.name}: {.spec.capabilities[*].name}{"\n"}{end}'
# Check MRD activation across providers
kubectl get mrds --show-labels
# Verify CRD creation matches activation
kubectl get crds | grep example.com | wc -l
kubectl get mrds -l state=Active | wc -l
```
## Relationship to other features
Provider capabilities integrate with:
* **MRDs** - SafeStart controls default activation state
* **Activation policies** - Work together to control resource availability
* **Package manager** - Capabilities are read during package installation
* **RBAC** - Some capabilities require additional permissions
* **Compositions** - May need updates when capabilities change resource availability
Capabilities provide a foundation for evolving provider behavior while
maintaining compatibility with existing Crossplane installations.

View File

@ -6,12 +6,13 @@ description: Learn what's new in the Crossplane v2 preview
**Crossplane v2 makes Crossplane more useful, more intuitive, and less
opinionated.**
Crossplane v2 makes four major changes:
Crossplane v2 makes five major changes:
* **Composite resources are now namespaced**
* **Managed resources are now namespaced**
* **Managed resources are now namespaced**
* **Composition supports any Kubernetes resource**
* **Operations enable operational workflows**
* **Managed Resource Definitions provide selective resource activation**
**Crossplane v2 is better suited to building control planes for applications,
not just infrastructure.** It removes the need for awkward abstractions like
@ -172,6 +173,61 @@ deprecate and remove cluster scoped MRs at a future date.
Read more about Crossplane v2's [backward compatibility](#backward-compatibility).
{{</hint>}}
## Managed Resource Definitions
Crossplane v2 introduces Managed Resource Definitions (MRDs) that provide
selective resource activation and improved resource discovery.
**MRDs solve performance and usability challenges with large providers.**
Installing a provider like AWS traditionally creates hundreds of CRDs, even if
you only need a few resources. MRDs let you activate only the resources you
actually use.
```mermaid
flowchart LR
provider(Provider Package)
mrd1(S3 Bucket MRD)
mrd2(EC2 Instance MRD)
mrd3(RDS DB MRD)
crd1(S3 Bucket CRD)
crd2(EC2 Instance CRD)
policy(Activation Policy)
provider --> mrd1
provider --> mrd2
provider --> mrd3
policy -.->|activates| mrd1
policy -.->|activates| mrd2
mrd3 -.->|inactive|
mrd1 --> crd1
mrd2 --> crd2
style mrd1 fill:#c8e6c9
style mrd2 fill:#c8e6c9
style mrd3 fill:#ffcdd2
style crd1 fill:#e8f5e8
style crd2 fill:#e8f5e8
```
### Key MRD benefits
**Performance optimization** - Only create CRDs for resources you need, reducing
cluster overhead and improving provider installation speed.
**Connection details discovery** - MRDs document what connection details each
managed resource provides, making it easier to understand resource capabilities.
**Policy-based activation** - Use ManagedResourceActivationPolicy (MRAP) to
activate resources with flexible pattern matching.
**SafeStart providers** - Providers can opt into SafeStart capability, starting
with all MRDs inactive for better performance.
Learn more about [Managed Resource Definitions]({{<ref "../managed-resources/managed-resource-definitions">}})
and [get started with MRDs]({{<ref "../get-started/get-started-with-mrds">}}).
## Compose any resource
Crossplane v2 isn't opinionated about using composition together with managed

View File

@ -61,7 +61,15 @@ initProvider
KCL
LateInitialize
managementPolicies
ManagedResourceActivationPolicy
ManagedResourceDefinition
ManagedResourceDefinitions
MRAP
MRAPs
MR
MRD
MRD's
MRDs
MRs
Operation-specific
PatchSet
@ -71,6 +79,8 @@ ProviderConfigs
ProviderRevision
RunFunctionRequest
RunFunctionResponse
SafeStart
SafeStart-capable
Sigstore
SSL
StoreConfig

View File

@ -84,3 +84,24 @@ v2
validators
version-specific
webhook-based
well-documented
user-friendly
provider-nop
provider-specific
pattern-based
fine-grained
environment-specific
real-world
trial-and-error
CRD-only
flag-like
Team-based
Service-based
Provider-wide
Environment-based
Policy-based
Crossplane-specific
non-SafeStart
GitOps
Makefile
backporting