@@ -12,89 +12,173 @@ In order to use a Kubernetes Custom Resource that has been defined in another pr
12
12
you will need to have several items of information.
13
13
* The Domain of the CR
14
14
* The Group under the Domain
15
- * The Go import path of the CR Type definition.
15
+ * The Go import path of the CR Type definition
16
+ * The Custom Resource Type you want to depend on.
16
17
17
18
The Domain and Group variables have been discussed in other parts of the documentation. The import path would be located in the project that installs the CR.
18
-
19
+ The Custom Resource Type is usually a Go Type of the same name as the CustomResourceDefinition in kubernetes, e.g. for a ` Pod ` there will be a type ` Pod ` in the ` v1 ` group.
20
+ For Kubernetes Core Types, the domain can be omitted.
21
+ ``
19
22
This document uses ` my ` and ` their ` prefixes as a naming convention for repos, groups, and types to clearly distinguish between your own project and the external one you are referencing.
20
- Note that by default, multigroup APIs are no longer included. To enable them again, see [ the guide on multigroup API migration] ( https://book.kubebuilder.io/migration/multi-group.html ) .
21
23
22
- Example external API Aggregation directory structure
24
+ In our example we will assume the following external API Type:
25
+
26
+ ` github.com/theiruser/theirproject ` is another kubebuilder project on whose CRD we want to depend and extend on.
27
+ Thus, it contains a ` go.mod ` in its repository root. The import path for the go types would be ` github.com/theiruser/theirproject/api/theirgroup/v1alpha1 ` .
28
+
29
+ The Domain of the CR is ` theirs.com ` , the Group is ` theirgroup ` and the kind and go type would be ` ExternalType ` .
30
+
31
+ If there is an interest to have multiple Controllers running in different Groups (e.g. because one is an owned CRD and one is an external Type), please first
32
+ reconfigure the Project to use a multi-group layout as described in the [ Multi-Group documentation] ( ../migration/multi-group.md ) .
33
+
34
+ ### Prerequisites
35
+
36
+ The following guide assumes that you have already created a project using ` kubebuilder init ` in a directory in the GOPATH. Please reference the [ Getting Started Guide] ( ../getting-started.md ) for more information.
37
+
38
+ Note that if you did not pass ` --domain ` to ` kubebuilder init ` you will need to modify it for the individual api types as the default is ` my.domain ` , not ` theirs.com ` .
39
+ Similarly, if you intend to use your own domain, please configure your own domain with ` kubebuilder init ` and do not use `theirs.com for the domain.
40
+
41
+ ### Add a controller for the external Type
42
+
43
+ We will now add a controller for the given type
44
+
45
+ ``` shell
46
+ kubebuilder create api --group theirgroup --version v1alpha1 --kind ExternalType --controller --resource=false
47
+ ```
48
+
49
+ Note that the ` resource ` argument is set to false, as we are not attempting to create our own CustomResourceDefinition,
50
+ but instead rely on an external one.
51
+
52
+ ### Adjusting the domain for an external type
53
+
54
+ In case of a domain mismatch of your project domain with the domain of the external type, you will need to adjust the domain for the external type:
55
+
56
+ #### Using a type not included in the Kubernetes Core Resources
57
+
58
+ file: PROJECT
23
59
```
24
- github.com
25
- ├── theiruser
26
- ├── theirproject
27
- ├── apis
28
- ├── theirgroup
29
- ├── doc.go`
30
- ├── install
31
- │ ├── install.go
32
- ├── v1alpha1
33
- │ ├── doc.go
34
- │ ├── register.go
35
- │ ├── types.go
36
- │ ├── zz_generated.deepcopy.go
60
+ domain: my.domain
61
+ layout:
62
+ - go.kubebuilder.io/v4
63
+ projectName: testkube
64
+ repo: github.com/jakobmoellerdev/testkube
65
+ resources:
66
+ - controller: true
67
+ domain: theirs.com
68
+ group: mygroup
69
+ kind: ExternalType
70
+ version: v1alpha1
71
+ version: "3"
37
72
```
38
73
39
- In the case above the import path would be ` github.com/theiruser/theirproject/apis/theirgroup/v1alpha1 `
74
+ Adjusting the generated RBAC manifests:
40
75
76
+ file: internal/controller/externaltype_controller.go
77
+ ``` go
78
+ // ExternalTypeReconciler reconciles a ExternalType object
79
+ type ExternalTypeReconciler struct {
80
+ client.Client
81
+ Scheme *runtime.Scheme
82
+ }
41
83
42
- ### Add a controller
84
+ // +kubebuilder:rbac:groups=theirgroup.theirs.com,resources=externaltypes,verbs=get;list;watch;create;update;patch;delete
85
+ // +kubebuilder:rbac:groups=theirgroup.theirs.com,resources=externaltypes/status,verbs=get;update;patch
86
+ // +kubebuilder:rbac:groups=theirgroup.theirs.com,resources=externaltypes/finalizers,verbs=update
43
87
44
- be sure to answer no when it asks if you would like to create an api? [ Y/n]
45
- ``` shell
46
- kubebuilder create api --group mygroup --version $APIVERSION --kind MyKind
47
88
```
48
89
49
- ## Edit the API files.
90
+ #### Using a type included in the Kubernetes Core Resources
91
+
92
+ file: PROJECT
93
+ ```
94
+ domain: my.domain
95
+ layout:
96
+ - go.kubebuilder.io/v4
97
+ projectName: testkube
98
+ repo: github.com/jakobmoellerdev/testkube
99
+ resources:
100
+ - controller: true
101
+ domain: ""
102
+ kind: Pod
103
+ version: v1alpha1
104
+ version: "3"
105
+ ```
106
+
107
+ Adjusting the generated RBAC manifests:
108
+
109
+ file: internal/controller/externaltype_controller.go
110
+ ``` go
111
+ // PodReconciler reconciles a Pod object
112
+ type ExternalTypeReconciler struct {
113
+ client.Client
114
+ Scheme *runtime.Scheme
115
+ }
116
+
117
+ // +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;create;update;patch;delete
118
+ // +kubebuilder:rbac:groups=core,resources=pods/status,verbs=get;update;patch
119
+ // +kubebuilder:rbac:groups=core,resources=pods/finalizers,verbs=update
120
+
121
+ ```
50
122
51
123
### Register your Types
52
124
53
- Edit the following file to the pkg/apis directory to append their ` AddToScheme ` to your ` AddToSchemes ` :
125
+ Edit the following lines to the main.go file to register the external types :
54
126
55
- file: pkg/apis/mytype_addtoscheme .go
127
+ file: cmd/main .go
56
128
``` go
57
129
package apis
58
130
59
131
import (
60
- mygroupv1alpha1 " github.com/myuser/myrepo/apis/mygroup/v1alpha1"
61
132
theirgroupv1alpha1 " github.com/theiruser/theirproject/apis/theirgroup/v1alpha1"
62
133
)
63
134
64
135
func init () {
65
- // Register the types with the Scheme so the components can map objects
66
- // to GroupVersionKinds and back
67
- AddToSchemes = append (
68
- AddToSchemes,
69
- mygroupv1alpha1.SchemeBuilder .AddToScheme ,
70
- theirgroupv1alpha1.SchemeBuilder .AddToScheme ,
71
- )
72
- }
73
-
136
+ utilruntime.Must (clientgoscheme.AddToScheme (scheme))
137
+ utilruntime.Must (theirgroupv1alpha1.AddToScheme (scheme)) // this contains the external API types
138
+ // +kubebuilder:scaffold:scheme
139
+ }
74
140
```
75
141
76
- ## Edit the Controller files
142
+ ## Edit the Controller `SetupWithManager function
77
143
78
- ### Use the correct imports for your API
144
+ ### Use the correct imports for your API and uncomment the controlled resource
79
145
80
- file: pkg /controllers/mytype_controller .go
146
+ file: internal /controllers/externaltype_controllers .go
81
147
``` go
82
148
package controllers
83
149
84
150
import (
85
- mygroupv1alpha1 " github.com/myuser/myrepo/apis/mygroup/v1alpha1"
86
151
theirgroupv1alpha1 " github.com/theiruser/theirproject/apis/theirgroup/v1alpha1"
87
152
)
153
+
154
+ // ...
155
+
156
+ // SetupWithManager sets up the controller with the Manager.
157
+ func (r *ExternalTypeReconciler ) SetupWithManager (mgr ctrl .Manager ) error {
158
+ return ctrl.NewControllerManagedBy (mgr).
159
+ For (&theirgroupv1alpha1.ExternalType {}).
160
+ Complete (r)
161
+ }
162
+
88
163
```
89
164
90
- Note that core resources may simply be imported by depending on the API's from upstream Kubernetes:
165
+ Note that core resources may simply be imported by depending on the API's from upstream Kubernetes and do not need additional ` AddToScheme ` registrations :
91
166
167
+ file: internal/controllers/externaltype_controllers.go
92
168
``` go
93
169
package controllers
94
170
// contains core resources like Deployment
95
171
import (
96
172
v1 " k8s.io/api/apps/v1"
97
173
)
174
+
175
+
176
+ // SetupWithManager sets up the controller with the Manager.
177
+ func (r *ExternalTypeReconciler ) SetupWithManager (mgr ctrl .Manager ) error {
178
+ return ctrl.NewControllerManagedBy (mgr).
179
+ For (&v1.Pod {}).
180
+ Complete (r)
181
+ }
98
182
```
99
183
100
184
### Update dependencies
@@ -103,47 +187,103 @@ import (
103
187
go mod tidy
104
188
```
105
189
190
+ ### Generate RBACs with updated Groups and Resources
106
191
107
- ### Verifying API Availability in the Cluster
108
-
109
- Since we are now using external types, it is best-practice to verify the existance of the API in the cluster.
110
- You can use the manager's client to verify API Existance before starting the controllers through the manager.
192
+ ```
193
+ make manifests
194
+ ```
111
195
112
196
## Prepare for testing
113
197
114
- #### Register your resource in the Scheme
198
+ ### Register your resource in the Scheme
115
199
116
- Edit the ` CRDDirectoryPaths ` in your test suite by appending the path to their CRDs :
200
+ Edit the ` CRDDirectoryPaths ` in your test suite and add the correct ` AddToScheme ` entry during suite initialization :
117
201
118
- file pkg /controllers/my_kind_controller_suite_test .go
202
+ file: internal /controllers/suite_test .go
119
203
``` go
204
+ package controller
205
+
206
+ import (
207
+ " fmt"
208
+ " path/filepath"
209
+ " runtime"
210
+ " testing"
211
+
212
+ . " github.com/onsi/ginkgo/v2"
213
+ . " github.com/onsi/gomega"
214
+
215
+ " k8s.io/client-go/kubernetes/scheme"
216
+ " k8s.io/client-go/rest"
217
+ " sigs.k8s.io/controller-runtime/pkg/client"
218
+ " sigs.k8s.io/controller-runtime/pkg/envtest"
219
+ logf " sigs.k8s.io/controller-runtime/pkg/log"
220
+ " sigs.k8s.io/controller-runtime/pkg/log/zap"
221
+ // +kubebuilder:scaffold:imports
222
+ theirgroupv1alpha1 " github.com/theiruser/theirproject/apis/theirgroup/v1alpha1"
223
+ )
224
+
120
225
var cfg *rest.Config
226
+ var k8sClient client.Client
227
+ var testEnv *envtest.Environment
121
228
122
- func TestMain (m *testing .M ) {
123
- // Get a config to talk to the apiserver
124
- t := &envtest.Environment {
125
- Config: cfg,
126
- CRDDirectoryPaths: []string {
127
- filepath.Join (" .." , " .." , " .." , " config" , " crds" ),
128
- filepath.Join (" .." , " .." , " .." , " vendor" , " github.com" , " theiruser" , " theirproject" , " config" , " crds" ),
129
- },
130
- UseExistingCluster: true ,
131
- }
229
+ func TestControllers (t *testing .T ) {
230
+ RegisterFailHandler (Fail)
132
231
133
- apis.AddToScheme (scheme.Scheme )
232
+ RunSpecs (t, " Controller Suite" )
233
+ }
134
234
135
- var err error
136
- if cfg, err = t.Start (); err != nil {
137
- log.Fatal (err)
235
+
236
+ var _ = BeforeSuite (func () {
237
+ // ...
238
+ By (" bootstrapping test environment" )
239
+ testEnv = &envtest.Environment {
240
+ CRDDirectoryPaths: []string {
241
+ // if you are using vendoring and rely on a kubebuilder based project, you can simply rely on the vendored config directory
242
+ filepath.Join (" .." , " .." , " .." , " vendor" , " github.com" , " theiruser" , " theirproject" , " config" , " crds" ),
243
+ // otherwise you can simply download the CRD from any source and place it within the config/crd/bases directory,
244
+ filepath.Join (" .." , " .." , " config" , " crd" , " bases" ),
245
+ },
246
+ ErrorIfCRDPathMissing: false ,
247
+
248
+ // The BinaryAssetsDirectory is only required if you want to run the tests directly
249
+ // without call the makefile target test. If not informed it will look for the
250
+ // default path defined in controller-runtime which is /usr/local/kubebuilder/.
251
+ // Note that you must have the required binaries setup under the bin directory to perform
252
+ // the tests directly. When we run make test it will be setup and used automatically.
253
+ BinaryAssetsDirectory: filepath.Join (" .." , " .." , " bin" , " k8s" ,
254
+ fmt.Sprintf (" 1.28.3-%s -%s " , runtime.GOOS , runtime.GOARCH )),
138
255
}
256
+
257
+ var err error
258
+ // cfg is defined in this file globally.
259
+ cfg, err = testEnv.Start ()
260
+ Expect (err).NotTo (HaveOccurred ())
261
+ Expect (cfg).NotTo (BeNil ())
139
262
140
- code := m.Run ()
141
- t.Stop ()
142
- os.Exit (code)
143
- }
263
+ // +kubebuilder:scaffold:scheme
264
+ Expect (theirgroupv1alpha1.AddToScheme (scheme.Scheme )).To (Succeed ())
265
+
266
+ k8sClient, err = client.New (cfg, client.Options {Scheme: scheme.Scheme })
267
+ Expect (err).NotTo (HaveOccurred ())
268
+ Expect (k8sClient).NotTo (BeNil ())
269
+
270
+
271
+ })
144
272
145
273
```
146
274
275
+ ### Verifying API Availability in the Cluster
276
+
277
+ Since we are now using external types, you will now have to rely on them being installed into the cluster.
278
+ If the APIs are not available at the time the manager starts, all informers listening to the non-available types
279
+ will fail, causing the manager to exit with an error similar to
280
+
281
+ ```
282
+ failed to get informer from cache {"error": "Timeout: failed waiting for *v1alpha1.ExternalType Informer to sync"}
283
+ ```
284
+
285
+ This will signal that the API Server is not yet ready to serve the external types.
286
+
147
287
## Helpful Tips
148
288
149
289
### Locate your domain and group variables
@@ -152,6 +292,6 @@ The following kubectl commands may be useful
152
292
153
293
``` shell
154
294
kubectl api-resources --verbs=list -o name
155
- kubectl api-resources --verbs=list -o name | grep mydomain.com
295
+ kubectl api-resources --verbs=list -o name | grep my.domain
156
296
```
157
297
0 commit comments