Skip to content

Commit 2c4c72e

Browse files
fix: support for external types with optional domain fields
In certain scenarios, domains must be empty, such as when scaffolding APIs for CertManager. In other cases, both group and domain must be specified to generate the correct scaffolds. This commit removes the check that enforces both fields to be non-empty, and properly documents and tests these scenarios in the testdata.
1 parent e451dfe commit 2c4c72e

File tree

23 files changed

+532
-61
lines changed

23 files changed

+532
-61
lines changed

docs/book/src/reference/using_an_external_resource.md

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,22 @@ kubebuilder create api --group <theirgroup> --version <theirversion> --kind <the
2828
For example, if you're managing Certificates from Cert Manager:
2929

3030
```shell
31-
kubebuilder create api --group certmanager --version v1 --kind Certificate --controller=true --resource=false --external-api-path=github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1 --external-api-domain=cert-manager.io
31+
kubebuilder create api --group cert-manager.io --version v1 --kind Certificate --controller=true --resource=false --external-api-path=github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1
3232
```
3333

34-
See the RBAC markers generated for this:
34+
See the RBAC [markers][markers-rbac] generated for this:
3535

3636
```go
37-
// +kubebuilder:rbac:groups=certmanager.cert-manager.io,resources=certificates,verbs=get;list;watch;create;update;patch;delete
38-
// +kubebuilder:rbac:groups=certmanager.cert-manager.io,resources=certificates/status,verbs=get;update;patch
39-
// +kubebuilder:rbac:groups=certmanager.cert-manager.io,resources=certificates/finalizers,verbs=update
37+
// +kubebuilder:rbac:groups=cert-manager.io,resources=certificates,verbs=get;list;watch;create;update;patch;delete
38+
// +kubebuilder:rbac:groups=cert-manager.io,resources=certificates/status,verbs=get;update;patch
39+
// +kubebuilder:rbac:groups=cert-manager.io,resources=certificates/finalizers,verbs=update
4040
```
4141

4242
Also, the RBAC role:
4343

4444
```ymal
4545
- apiGroups:
46-
- certmanager.cert-manager.io
46+
- cert-manager.io
4747
resources:
4848
- certificates
4949
verbs:
@@ -55,7 +55,7 @@ Also, the RBAC role:
5555
- update
5656
- watch
5757
- apiGroups:
58-
- certmanager.cert-manager.io
58+
- cert-manager.io
5959
resources:
6060
- certificates/finalizers
6161
verbs:
@@ -70,6 +70,50 @@ Also, the RBAC role:
7070
- update
7171
```
7272

73+
However, if we are scaffolding an API that has a domain defined, we need to explicitly set the domain using the `--external-api-domain`
74+
flag in the Kubebuilder command. For example, if we want to scaffold a controller for the ServiceMonitor
75+
API provided and defined in the [Prometheus Operator][prometheus-operator], we would scaffold it as follows:
76+
77+
```shell
78+
kubebuilder create api --group "monitoring" --version v1 --kind ServiceMonitor --controller=true --resource=false --make=false --external-api-path=github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1 --external-api-domain=coreos.com
79+
```
80+
81+
In this case:
82+
83+
- The **group** is `"monitoring"`.
84+
- The **domain** is `"coreos.com"`, as indicated by `--external-api-domain=coreos.com`.
85+
86+
This structure ensures that the API group and domain are correctly applied to generate the
87+
[markers][markers-rbac] and scaffolds accordingly.
88+
89+
In this scenario, the [markers][markers-rbac] scaffolded in the controller will look like this:
90+
91+
```go
92+
// +kubebuilder:rbac:groups=monitoring.coreos.com,resources=servicemonitors,verbs=get;list;watch;create;update;patch;delete
93+
// +kubebuilder:rbac:groups=monitoring.coreos.com,resources=servicemonitors/status,verbs=get;update;patch
94+
// +kubebuilder:rbac:groups=monitoring.coreos.com,resources=servicemonitors/finalizers,verbs=update
95+
96+
```
97+
98+
These [markers][markers-rbac] will generate the corresponding permissions under config/rbac/, such as:
99+
100+
```ymal
101+
- apiGroups:
102+
- monitoring.coreos.com
103+
resources:
104+
- servicemonitors/finalizers
105+
verbs:
106+
- update
107+
- apiGroups:
108+
- monitoring.coreos.com
109+
resources:
110+
- servicemonitors/status
111+
verbs:
112+
- get
113+
- patch
114+
- update
115+
```
116+
73117
This scaffolds a controller for the external type but skips creating new resource
74118
definitions since the type is defined in an external project.
75119

@@ -118,15 +162,15 @@ For instance, to create a controller to manage Deployment the command would be l
118162
create api --group apps --version v1 --kind Deployment --controller=true --resource=false
119163
```
120164

121-
See the RBAC markers generated for this:
165+
See the RBAC [markers][markers-rbac] generated for this:
122166

123167
```go
124168
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
125169
// +kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get;update;patch
126170
// +kubebuilder:rbac:groups=apps,resources=deployments/finalizers,verbs=update
127171
```
128172

129-
Also, the RBAC for the above markers:
173+
Also, the RBAC for the above [markers][markers-rbac]:
130174

131175
```yaml
132176
- apiGroups:
@@ -170,3 +214,5 @@ Webhook support for Core Types is not currently automated by the tool. However,
170214
</aside>
171215

172216
[webhook-for-core-types]: ./webhook-for-core-types.md
217+
[prometheus-operator]: https://github.com/prometheus-operator/prometheus-operator
218+
[markers-rbac]: ./markers/rbac.md

pkg/plugins/golang/v4/api.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -146,14 +146,6 @@ func (p *createAPISubcommand) InjectResource(res *resource.Resource) error {
146146
}
147147
}
148148

149-
// Ensure that if any external API flag is set, both must be provided.
150-
if len(p.options.ExternalAPIPath) != 0 || len(p.options.ExternalAPIDomain) != 0 {
151-
if len(p.options.ExternalAPIPath) == 0 || len(p.options.ExternalAPIDomain) == 0 {
152-
return errors.New("Both '--external-api-path' and '--external-api-domain' must be " +
153-
"specified together when referencing an external API.")
154-
}
155-
}
156-
157149
p.options.UpdateResource(p.resource, p.config)
158150

159151
if err := p.resource.Validate(); err != nil {

test/testdata/generate.sh

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ function scaffold_test_project {
4646
$kb create api --group crew --version v1 --kind Admiral --plural=admirales --controller=true --resource=true --namespaced=false --make=false
4747
$kb create webhook --group crew --version v1 --kind Admiral --plural=admirales --defaulting
4848
# Controller for External types
49-
$kb create api --group certmanager --version v1 --kind Certificate --controller=true --resource=false --make=false --external-api-path=github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1 --external-api-domain=cert-manager.io
49+
$kb create api --group "cert-manager.io" --version v1 --kind Certificate --controller=true --resource=false --make=false --external-api-path=github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1
50+
$kb create api --group "monitoring" --version v1 --kind ServiceMonitor --controller=true --resource=false --make=false --external-api-path=github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1 --external-api-domain=coreos.com
5051
fi
5152

5253
if [[ $project =~ multigroup ]]; then
@@ -72,7 +73,8 @@ function scaffold_test_project {
7273
$kb create api --group foo --version v1 --kind Bar --controller=true --resource=true --make=false
7374
$kb create api --group fiz --version v1 --kind Bar --controller=true --resource=true --make=false
7475
# Controller for External types
75-
$kb create api --group certmanager --version v1 --kind Certificate --controller=true --resource=false --make=false --external-api-path=github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1 --external-api-domain=cert-manager.io
76+
$kb create api --group "cert-manager.io" --version v1 --kind Certificate --controller=true --resource=false --make=false --external-api-path=github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1
77+
$kb create api --group "monitoring" --version v1 --kind ServiceMonitor --controller=true --resource=false --make=false --external-api-path=github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1 --external-api-domain=coreos.com
7678
fi
7779

7880
if [[ $project =~ multigroup ]] || [[ $project =~ with-plugins ]] ; then

testdata/project-v4-multigroup/PROJECT

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,18 @@ resources:
126126
path: sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/api/fiz/v1
127127
version: v1
128128
- controller: true
129-
domain: cert-manager.io
130129
external: true
131-
group: certmanager
130+
group: cert-manager.io
132131
kind: Certificate
133132
path: github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1
134133
version: v1
134+
- controller: true
135+
domain: coreos.com
136+
external: true
137+
group: monitoring
138+
kind: ServiceMonitor
139+
path: github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1
140+
version: v1
135141
- api:
136142
crdVersion: v1
137143
namespaced: true

testdata/project-v4-multigroup/cmd/main.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ import (
3535
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
3636
"sigs.k8s.io/controller-runtime/pkg/webhook"
3737

38-
certmanagerv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
38+
certmanageriov1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
39+
40+
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
3941

4042
crewv1 "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/api/crew/v1"
4143
examplecomv1alpha1 "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/api/example.com/v1alpha1"
@@ -48,12 +50,13 @@ import (
4850
shipv1beta1 "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/api/ship/v1beta1"
4951
shipv2alpha1 "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/api/ship/v2alpha1"
5052
appscontroller "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/internal/controller/apps"
51-
certmanagercontroller "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/internal/controller/certmanager"
53+
certmanageriocontroller "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/internal/controller/cert-manager.io"
5254
crewcontroller "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/internal/controller/crew"
5355
examplecomcontroller "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/internal/controller/example.com"
5456
fizcontroller "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/internal/controller/fiz"
5557
foocontroller "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/internal/controller/foo"
5658
foopolicycontroller "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/internal/controller/foo.policy"
59+
monitoringcontroller "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/internal/controller/monitoring"
5760
seacreaturescontroller "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/internal/controller/sea-creatures"
5861
shipcontroller "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/internal/controller/ship"
5962
webhookcrewv1 "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/internal/webhook/crew/v1"
@@ -81,7 +84,8 @@ func init() {
8184
utilruntime.Must(foopolicyv1.AddToScheme(scheme))
8285
utilruntime.Must(foov1.AddToScheme(scheme))
8386
utilruntime.Must(fizv1.AddToScheme(scheme))
84-
utilruntime.Must(certmanagerv1.AddToScheme(scheme))
87+
utilruntime.Must(certmanageriov1.AddToScheme(scheme))
88+
utilruntime.Must(monitoringv1.AddToScheme(scheme))
8589
utilruntime.Must(examplecomv1alpha1.AddToScheme(scheme))
8690
// +kubebuilder:scaffold:scheme
8791
}
@@ -276,13 +280,20 @@ func main() {
276280
setupLog.Error(err, "unable to create controller", "controller", "Bar")
277281
os.Exit(1)
278282
}
279-
if err = (&certmanagercontroller.CertificateReconciler{
283+
if err = (&certmanageriocontroller.CertificateReconciler{
280284
Client: mgr.GetClient(),
281285
Scheme: mgr.GetScheme(),
282286
}).SetupWithManager(mgr); err != nil {
283287
setupLog.Error(err, "unable to create controller", "controller", "Certificate")
284288
os.Exit(1)
285289
}
290+
if err = (&monitoringcontroller.ServiceMonitorReconciler{
291+
Client: mgr.GetClient(),
292+
Scheme: mgr.GetScheme(),
293+
}).SetupWithManager(mgr); err != nil {
294+
setupLog.Error(err, "unable to create controller", "controller", "ServiceMonitor")
295+
os.Exit(1)
296+
}
286297
if err = (&examplecomcontroller.MemcachedReconciler{
287298
Client: mgr.GetClient(),
288299
Scheme: mgr.GetScheme(),

testdata/project-v4-multigroup/config/rbac/role.yaml

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ rules:
4646
- patch
4747
- update
4848
- apiGroups:
49-
- certmanager.cert-manager.io
49+
- cert-manager.io
5050
resources:
5151
- certificates
5252
verbs:
@@ -58,13 +58,13 @@ rules:
5858
- update
5959
- watch
6060
- apiGroups:
61-
- certmanager.cert-manager.io
61+
- cert-manager.io
6262
resources:
6363
- certificates/finalizers
6464
verbs:
6565
- update
6666
- apiGroups:
67-
- certmanager.cert-manager.io
67+
- cert-manager.io
6868
resources:
6969
- certificates/status
7070
verbs:
@@ -181,6 +181,32 @@ rules:
181181
- get
182182
- patch
183183
- update
184+
- apiGroups:
185+
- monitoring.coreos.com
186+
resources:
187+
- servicemonitors
188+
verbs:
189+
- create
190+
- delete
191+
- get
192+
- list
193+
- patch
194+
- update
195+
- watch
196+
- apiGroups:
197+
- monitoring.coreos.com
198+
resources:
199+
- servicemonitors/finalizers
200+
verbs:
201+
- update
202+
- apiGroups:
203+
- monitoring.coreos.com
204+
resources:
205+
- servicemonitors/status
206+
verbs:
207+
- get
208+
- patch
209+
- update
184210
- apiGroups:
185211
- sea-creatures.testproject.org
186212
resources:

testdata/project-v4-multigroup/dist/install.yaml

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,7 +1177,7 @@ rules:
11771177
- patch
11781178
- update
11791179
- apiGroups:
1180-
- certmanager.cert-manager.io
1180+
- cert-manager.io
11811181
resources:
11821182
- certificates
11831183
verbs:
@@ -1189,13 +1189,13 @@ rules:
11891189
- update
11901190
- watch
11911191
- apiGroups:
1192-
- certmanager.cert-manager.io
1192+
- cert-manager.io
11931193
resources:
11941194
- certificates/finalizers
11951195
verbs:
11961196
- update
11971197
- apiGroups:
1198-
- certmanager.cert-manager.io
1198+
- cert-manager.io
11991199
resources:
12001200
- certificates/status
12011201
verbs:
@@ -1312,6 +1312,32 @@ rules:
13121312
- get
13131313
- patch
13141314
- update
1315+
- apiGroups:
1316+
- monitoring.coreos.com
1317+
resources:
1318+
- servicemonitors
1319+
verbs:
1320+
- create
1321+
- delete
1322+
- get
1323+
- list
1324+
- patch
1325+
- update
1326+
- watch
1327+
- apiGroups:
1328+
- monitoring.coreos.com
1329+
resources:
1330+
- servicemonitors/finalizers
1331+
verbs:
1332+
- update
1333+
- apiGroups:
1334+
- monitoring.coreos.com
1335+
resources:
1336+
- servicemonitors/status
1337+
verbs:
1338+
- get
1339+
- patch
1340+
- update
13151341
- apiGroups:
13161342
- sea-creatures.testproject.org
13171343
resources:

testdata/project-v4-multigroup/go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
module sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup
22

3-
go 1.22.0
3+
go 1.23
4+
5+
toolchain go1.23.2
46

57
require (
68
github.com/cert-manager/cert-manager v1.16.1
79
github.com/onsi/ginkgo/v2 v2.19.0
810
github.com/onsi/gomega v1.33.1
11+
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.77.1
912
k8s.io/api v0.31.1
1013
k8s.io/apimachinery v0.31.1
1114
k8s.io/client-go v0.31.1

testdata/project-v4-multigroup/internal/controller/certmanager/certificate_controller.go renamed to testdata/project-v4-multigroup/internal/controller/cert-manager.io/certificate_controller.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package certmanager
17+
package certmanagerio
1818

1919
import (
2020
"context"
2121

22-
certmanagerv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
22+
certmanageriov1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
2323
"k8s.io/apimachinery/pkg/runtime"
2424
ctrl "sigs.k8s.io/controller-runtime"
2525
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -32,9 +32,9 @@ type CertificateReconciler struct {
3232
Scheme *runtime.Scheme
3333
}
3434

35-
// +kubebuilder:rbac:groups=certmanager.cert-manager.io,resources=certificates,verbs=get;list;watch;create;update;patch;delete
36-
// +kubebuilder:rbac:groups=certmanager.cert-manager.io,resources=certificates/status,verbs=get;update;patch
37-
// +kubebuilder:rbac:groups=certmanager.cert-manager.io,resources=certificates/finalizers,verbs=update
35+
// +kubebuilder:rbac:groups=cert-manager.io,resources=certificates,verbs=get;list;watch;create;update;patch;delete
36+
// +kubebuilder:rbac:groups=cert-manager.io,resources=certificates/status,verbs=get;update;patch
37+
// +kubebuilder:rbac:groups=cert-manager.io,resources=certificates/finalizers,verbs=update
3838

3939
// Reconcile is part of the main kubernetes reconciliation loop which aims to
4040
// move the current state of the cluster closer to the desired state.
@@ -56,7 +56,7 @@ func (r *CertificateReconciler) Reconcile(ctx context.Context, req ctrl.Request)
5656
// SetupWithManager sets up the controller with the Manager.
5757
func (r *CertificateReconciler) SetupWithManager(mgr ctrl.Manager) error {
5858
return ctrl.NewControllerManagedBy(mgr).
59-
For(&certmanagerv1.Certificate{}).
60-
Named("certmanager-certificate").
59+
For(&certmanageriov1.Certificate{}).
60+
Named("cert-manager.io-certificate").
6161
Complete(r)
6262
}

0 commit comments

Comments
 (0)