Skip to content

Commit b1f70b3

Browse files
committed
trim the implementing safestart guide
Signed-off-by: Scott Nichols <[email protected]>
1 parent b765ea6 commit b1f70b3

File tree

1 file changed

+4
-323
lines changed

1 file changed

+4
-323
lines changed

content/master/guides/implementing-safestart.md

Lines changed: 4 additions & 323 deletions
Original file line numberDiff line numberDiff line change
@@ -59,96 +59,7 @@ Crossplane supports flexible capability matching. `safe-start`, `safestart`,
5959
and `safe-start` are all recognized as the same capability.
6060
{{< /hint >}}
6161

62-
### Step 2: Enhance managed resource definition generation
63-
64-
Update your MRD generation to include connection details documentation:
65-
66-
{{< tabs >}}
67-
{{< tab "Go Controller Runtime" >}}
68-
```go
69-
// In your MRD generation code
70-
type ManagedResourceDefinition struct {
71-
metav1.TypeMeta `json:",inline"`
72-
metav1.ObjectMeta `json:"metadata,omitempty"`
73-
74-
Spec ManagedResourceDefinitionSpec `json:"spec"`
75-
Status ManagedResourceDefinitionStatus `json:"status,omitempty"`
76-
}
77-
78-
type ManagedResourceDefinitionSpec struct {
79-
// Standard CRD fields
80-
Group string `json:"group"`
81-
Names Names `json:"names"`
82-
Scope string `json:"scope"`
83-
84-
// safe-start-specific fields
85-
ConnectionDetails []ConnectionDetail `json:"connectionDetails,omitempty"`
86-
State ResourceState `json:"state,omitempty"`
87-
}
88-
89-
type ConnectionDetail struct {
90-
Name string `json:"name"`
91-
Description string `json:"description"`
92-
Type string `json:"type"`
93-
FromConnectionSecretKey string `json:"fromConnectionSecretKey,omitempty"`
94-
}
95-
```
96-
{{< /tab >}}
97-
98-
{{< tab "Terrajet/Upjet Provider" >}}
99-
```go
100-
// In your provider configuration
101-
func GetProvider() *ujconfig.Provider {
102-
pc := ujconfig.NewProvider([]byte(providerSchema), resourcePrefix, modulePath,
103-
ujconfig.WithIncludeList(ExternalNameConfigured()),
104-
ujconfig.WithDefaultResourceOptions(
105-
ExternalNameConfigurations(),
106-
safe-startConfiguration(), // Add safe-start config
107-
))
108-
109-
// Configure safe-start for specific resources
110-
for _, configure := range []func(provider *ujconfig.Provider){
111-
configureConnectionDetails,
112-
configureMRDDocumentation,
113-
} {
114-
configure(pc)
115-
}
116-
117-
return pc
118-
}
119-
120-
func configureConnectionDetails(p *ujconfig.Provider) {
121-
// Example: RDS Instance connection details
122-
p.AddResourceConfigurator("aws_db_instance", func(r *ujconfig.Resource) {
123-
r.ConnectionDetails = map[string]ujconfig.ConnectionDetail{
124-
"endpoint": {
125-
Description: "The RDS instance endpoint",
126-
Type: "string",
127-
FromConnectionSecretKey: "endpoint",
128-
},
129-
"port": {
130-
Description: "The port on which the DB accepts connections",
131-
Type: "integer",
132-
FromConnectionSecretKey: "port",
133-
},
134-
"username": {
135-
Description: "The master username for the database",
136-
Type: "string",
137-
FromConnectionSecretKey: "username",
138-
},
139-
"password": {
140-
Description: "The master password for the database",
141-
Type: "string",
142-
FromConnectionSecretKey: "password",
143-
},
144-
}
145-
})
146-
}
147-
```
148-
{{< /tab >}}
149-
{{< /tabs >}}
150-
151-
### Step 3: Update RBAC Permissions
62+
### Step 2: Update RBAC Permissions
15263

15364
safe-start providers need extra permissions to manage CRDs dynamically. Crossplane's RBAC manager automatically provides these permissions when you install safe-start providers.
15465

@@ -162,10 +73,7 @@ Manual RBAC configuration is only required if you disable Crossplane's RBAC mana
16273
# safe-start permissions
16374
- apiGroups: ["apiextensions.k8s.io"]
16475
resources: ["customresourcedefinitions"]
165-
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
166-
- apiGroups: ["apiextensions.crossplane.io"]
167-
resources: ["managedresourcedefinitions"]
168-
verbs: ["get", "list", "watch", "update", "patch"]
76+
verbs: ["get", "list", "watch"]
16977
```
17078

17179
**Manual configuration (only if you disable RBAC manager):**
@@ -186,184 +94,10 @@ rules:
18694
# safe-start permissions
18795
- apiGroups: ["apiextensions.k8s.io"]
18896
resources: ["customresourcedefinitions"]
189-
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
190-
- apiGroups: ["apiextensions.crossplane.io"]
191-
resources: ["managedresourcedefinitions"]
192-
verbs: ["get", "list", "watch", "update", "patch"]
97+
verbs: ["get", "list", "watch"]
19398
```
19499

195-
### Step 4: Implement managed resource definition controller logic
196-
197-
Add controller logic to handle MRD activation and CRD lifecycle:
198-
199-
```go
200-
package controller
201-
202-
import (
203-
"context"
204-
205-
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
206-
"sigs.k8s.io/controller-runtime/pkg/reconcile"
207-
208-
xpv1alpha1 "github.com/crossplane/crossplane/apis/apiextensions/v1alpha1"
209-
)
210-
211-
// MRDReconciler handles MRD activation
212-
type MRDReconciler struct {
213-
client.Client
214-
Scheme *runtime.Scheme
215-
}
216-
217-
func (r *MRDReconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) {
218-
mrd := &xpv1alpha1.ManagedResourceDefinition{}
219-
if err := r.Get(ctx, req.NamespacedName, mrd); err != nil {
220-
return reconcile.Result{}, client.IgnoreNotFound(err)
221-
}
222-
223-
// Check if MRD should be active
224-
if mrd.Spec.State != nil && *mrd.Spec.State == xpv1alpha1.ResourceStateActive {
225-
return r.ensureCRDExists(ctx, mrd)
226-
}
227-
228-
// If inactive, ensure CRD is removed
229-
return r.ensureCRDRemoved(ctx, mrd)
230-
}
231-
232-
func (r *MRDReconciler) ensureCRDExists(ctx context.Context, mrd *xpv1alpha1.ManagedResourceDefinition) (reconcile.Result, error) {
233-
crd := &apiextv1.CustomResourceDefinition{}
234-
crdName := mrd.Spec.Names.Plural + "." + mrd.Spec.Group
235-
236-
err := r.Get(ctx, types.NamespacedName{Name: crdName}, crd)
237-
if client.IgnoreNotFound(err) != nil {
238-
return reconcile.Result{}, err
239-
}
240-
241-
if err != nil { // CRD doesn't exist
242-
return r.createCRD(ctx, mrd)
243-
}
244-
245-
// CRD exists, ensure it's up to date
246-
return r.updateCRD(ctx, mrd, crd)
247-
}
248-
249-
func (r *MRDReconciler) createCRD(ctx context.Context, mrd *xpv1alpha1.ManagedResourceDefinition) (reconcile.Result, error) {
250-
crd := &apiextv1.CustomResourceDefinition{
251-
ObjectMeta: metav1.ObjectMeta{
252-
Name: mrd.Spec.Names.Plural + "." + mrd.Spec.Group,
253-
OwnerReferences: []metav1.OwnerReference{{
254-
APIVersion: mrd.APIVersion,
255-
Kind: mrd.Kind,
256-
Name: mrd.Name,
257-
UID: mrd.UID,
258-
Controller: pointer.Bool(true),
259-
}},
260-
},
261-
Spec: mrd.Spec.CustomResourceDefinitionSpec,
262-
}
263-
264-
return reconcile.Result{}, r.Create(ctx, crd)
265-
}
266-
```
267-
268-
### Step 5: Update build and continuous integration processes
269-
270-
Update your build process to generate MRDs alongside CRDs:
271-
272-
{{< tabs >}}
273-
{{< tab "Makefile" >}}
274-
```makefile
275-
# Update your Makefile to generate both CRDs and MRDs
276-
.PHONY: generate
277-
generate: controller-gen
278-
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
279-
$(CONTROLLER_GEN) crd:allowDangerousTypes=true paths="./..." output:crd:artifacts:config=package/crds
280-
$(CONTROLLER_GEN) mrd:allowDangerousTypes=true paths="./..." output:mrd:artifacts:config=package/mrds
281-
282-
# Add MRD generation tool
283-
MRD_GEN = $(shell pwd)/bin/mrd-gen
284-
.PHONY: mrd-gen
285-
mrd-gen: ## Download mrd-gen locally if necessary.
286-
$(call go-get-tool,$(MRD_GEN),sigs.k8s.io/controller-tools/cmd/[email protected])
287-
288-
# Update package generation to include MRDs
289-
.PHONY: build-package
290-
build-package: generate
291-
mkdir -p package/
292-
cp package/crds/*.yaml package/
293-
cp package/mrds/*.yaml package/
294-
echo "# Package metadata with safe-start capability" > package/provider.yaml
295-
echo "apiVersion: meta.pkg.crossplane.io/v1" >> package/provider.yaml
296-
echo "kind: Provider" >> package/provider.yaml
297-
echo "spec:" >> package/provider.yaml
298-
echo " capabilities:" >> package/provider.yaml
299-
echo " - safe-start" >> package/provider.yaml
300-
```
301-
{{< /tab >}}
302-
303-
{{< tab "GitHub Actions" >}}
304-
```yaml
305-
name: Build and Test safe-start Provider
306-
307-
on:
308-
push:
309-
branches: [ main ]
310-
pull_request:
311-
branches: [ main ]
312-
313-
jobs:
314-
test-safestart:
315-
runs-on: ubuntu-latest
316-
steps:
317-
- uses: actions/checkout@v4
318-
319-
- name: Setup Go
320-
uses: actions/setup-go@v4
321-
with:
322-
go-version: '1.21'
323-
324-
- name: Run Tests
325-
run: make test
326-
327-
- name: Generate MRDs
328-
run: make generate
329-
330-
- name: Verify MRD Generation
331-
run: |
332-
if [ ! -d "package/mrds" ]; then
333-
echo "MRD generation failed"
334-
exit 1
335-
fi
336-
echo "Generated MRDs:"
337-
ls -la package/mrds/
338-
339-
- name: Test safe-start Integration
340-
run: |
341-
# Start local cluster
342-
make kind-up
343-
make install-crossplane-v2
344-
345-
# Install provider with safe-start
346-
make install-provider
347-
348-
# Verify MRDs created but inactive
349-
kubectl get mrds
350-
kubectl get mrds -o jsonpath='{.items[*].spec.state}' | grep -q "Inactive"
351-
352-
# Test activation policy
353-
kubectl apply -f examples/activation-policy.yaml
354-
355-
# Verify resources activate
356-
sleep 30
357-
kubectl get mrds -o jsonpath='{.items[*].spec.state}' | grep -q "Active"
358-
359-
# Test resource creation
360-
kubectl apply -f examples/example-resource.yaml
361-
kubectl wait --for=condition=Ready --timeout=300s -f examples/example-resource.yaml
362-
```
363-
{{< /tab >}}
364-
{{< /tabs >}}
365-
366-
### Step 6: Add connection details documentation
100+
### Step 3: Add connection details documentation
367101

368102
Document connection details in your MRDs to help users understand resource
369103
capabilities:
@@ -414,47 +148,6 @@ spec:
414148

415149
## Testing safe-start implementation
416150

417-
### Unit testing
418-
419-
Test your MRD generation and controller logic:
420-
421-
```go
422-
func TestMRDGeneration(t *testing.T) {
423-
// Test MRD generation with correct connection details
424-
mrd := generateMRDForResource("Database")
425-
426-
assert.Equal(t, "databases.rds.aws.example.io", mrd.Name)
427-
assert.NotEmpty(t, mrd.Spec.ConnectionDetails)
428-
429-
// Verify specific connection details
430-
endpointDetail := findConnectionDetail(mrd, "endpoint")
431-
assert.NotNil(t, endpointDetail)
432-
assert.Equal(t, "string", endpointDetail.Type)
433-
assert.Contains(t, endpointDetail.Description, "endpoint")
434-
}
435-
436-
func TestMRDActivation(t *testing.T) {
437-
// Test MRD activation creates CRD
438-
ctx := context.Background()
439-
mrd := &v1alpha1.ManagedResourceDefinition{
440-
Spec: v1alpha1.ManagedResourceDefinitionSpec{
441-
State: &[]v1alpha1.ResourceState{v1alpha1.ResourceStateActive}[0],
442-
},
443-
}
444-
445-
reconciler := &MRDReconciler{Client: fakeClient}
446-
result, err := reconciler.Reconcile(ctx, reconcile.Request{})
447-
448-
assert.NoError(t, err)
449-
assert.False(t, result.Requeue)
450-
451-
// Verify CRD creation
452-
crd := &apiextv1.CustomResourceDefinition{}
453-
err = fakeClient.Get(ctx, types.NamespacedName{Name: "databases.rds.aws.example.io"}, crd)
454-
assert.NoError(t, err)
455-
}
456-
```
457-
458151
### Integration testing
459152

460153
Test safe-start behavior in a real cluster:
@@ -480,8 +173,6 @@ metadata:
480173
name: provider-example
481174
spec:
482175
package: registry.example.com/provider-example:latest
483-
capabilities:
484-
- safe-start
485176
EOF
486177
487178
# Wait for provider installation
@@ -568,16 +259,6 @@ spec:
568259
- "*.aws.example.io" # Activate all resources (legacy behavior)
569260
```
570261

571-
### Version compatibility matrix
572-
573-
Document version compatibility:
574-
575-
| Provider Version | Crossplane Version | safe-start Support | Notes |
576-
|------------------|-------------------|------------------|-------|
577-
| v1.x | v1.x - v2.x | No | Legacy CRD-only mode |
578-
| v2.0 | v2.0+ | Yes | Full safe-start support |
579-
| v2.1 | v2.0+ | Yes | Enhanced MRD features |
580-
581262
## Documentation requirements
582263

583264
Update your provider documentation to include:

0 commit comments

Comments
 (0)