mirror of https://github.com/crossplane/docs.git
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:
parent
704d5dfe38
commit
3fcc4473a9
|
|
@ -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" >}})
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -6,12 +6,13 @@ description: Learn what's new in the Crossplane v2 preview
|
||||||
**Crossplane v2 makes Crossplane more useful, more intuitive, and less
|
**Crossplane v2 makes Crossplane more useful, more intuitive, and less
|
||||||
opinionated.**
|
opinionated.**
|
||||||
|
|
||||||
Crossplane v2 makes four major changes:
|
Crossplane v2 makes five major changes:
|
||||||
|
|
||||||
* **Composite resources are now namespaced**
|
* **Composite resources are now namespaced**
|
||||||
* **Managed resources are now namespaced**
|
* **Managed resources are now namespaced**
|
||||||
* **Composition supports any Kubernetes resource**
|
* **Composition supports any Kubernetes resource**
|
||||||
* **Operations enable operational workflows**
|
* **Operations enable operational workflows**
|
||||||
|
* **Managed Resource Definitions provide selective resource activation**
|
||||||
|
|
||||||
**Crossplane v2 is better suited to building control planes for applications,
|
**Crossplane v2 is better suited to building control planes for applications,
|
||||||
not just infrastructure.** It removes the need for awkward abstractions like
|
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).
|
Read more about Crossplane v2's [backward compatibility](#backward-compatibility).
|
||||||
{{</hint>}}
|
{{</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
|
## Compose any resource
|
||||||
|
|
||||||
Crossplane v2 isn't opinionated about using composition together with managed
|
Crossplane v2 isn't opinionated about using composition together with managed
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,15 @@ initProvider
|
||||||
KCL
|
KCL
|
||||||
LateInitialize
|
LateInitialize
|
||||||
managementPolicies
|
managementPolicies
|
||||||
|
ManagedResourceActivationPolicy
|
||||||
|
ManagedResourceDefinition
|
||||||
|
ManagedResourceDefinitions
|
||||||
|
MRAP
|
||||||
|
MRAPs
|
||||||
MR
|
MR
|
||||||
|
MRD
|
||||||
|
MRD's
|
||||||
|
MRDs
|
||||||
MRs
|
MRs
|
||||||
Operation-specific
|
Operation-specific
|
||||||
PatchSet
|
PatchSet
|
||||||
|
|
@ -71,6 +79,8 @@ ProviderConfigs
|
||||||
ProviderRevision
|
ProviderRevision
|
||||||
RunFunctionRequest
|
RunFunctionRequest
|
||||||
RunFunctionResponse
|
RunFunctionResponse
|
||||||
|
SafeStart
|
||||||
|
SafeStart-capable
|
||||||
Sigstore
|
Sigstore
|
||||||
SSL
|
SSL
|
||||||
StoreConfig
|
StoreConfig
|
||||||
|
|
|
||||||
|
|
@ -84,3 +84,24 @@ v2
|
||||||
validators
|
validators
|
||||||
version-specific
|
version-specific
|
||||||
webhook-based
|
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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue