@@ -59,96 +59,7 @@ Crossplane supports flexible capability matching. `safe-start`, `safestart`,
59
59
and `safe-start` are all recognized as the same capability.
60
60
{{< /hint >}}
61
61
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
152
63
153
64
safe-start providers need extra permissions to manage CRDs dynamically. Crossplane's RBAC manager automatically provides these permissions when you install safe-start providers.
154
65
@@ -162,10 +73,7 @@ Manual RBAC configuration is only required if you disable Crossplane's RBAC mana
162
73
# safe-start permissions
163
74
- apiGroups: ["apiextensions.k8s.io"]
164
75
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"]
169
77
` ` `
170
78
171
79
**Manual configuration (only if you disable RBAC manager):**
@@ -186,184 +94,10 @@ rules:
186
94
# safe-start permissions
187
95
- apiGroups: ["apiextensions.k8s.io"]
188
96
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"]
193
98
` ` `
194
99
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
367
101
368
102
Document connection details in your MRDs to help users understand resource
369
103
capabilities :
@@ -414,47 +148,6 @@ spec:
414
148
415
149
# # Testing safe-start implementation
416
150
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
-
458
151
# ## Integration testing
459
152
460
153
Test safe-start behavior in a real cluster :
@@ -480,8 +173,6 @@ metadata:
480
173
name: provider-example
481
174
spec:
482
175
package: registry.example.com/provider-example:latest
483
- capabilities:
484
- - safe-start
485
176
EOF
486
177
487
178
# Wait for provider installation
@@ -568,16 +259,6 @@ spec:
568
259
- "*.aws.example.io" # Activate all resources (legacy behavior)
569
260
` ` `
570
261
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
-
581
262
# # Documentation requirements
582
263
583
264
Update your provider documentation to include :
0 commit comments