Skip to content

Commit efe33fd

Browse files
Add Hub and Spoke for conversion webhooks
Introduces the required code to add the code implementation for the interfaces.
1 parent 8b7e17d commit efe33fd

File tree

36 files changed

+564
-374
lines changed

36 files changed

+564
-374
lines changed

docs/book/src/multiversion-tutorial/testdata/project/PROJECT

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ resources:
1818
path: tutorial.kubebuilder.io/project/api/v1
1919
version: v1
2020
webhooks:
21+
conversion: true
2122
defaulting: true
2223
validation: true
2324
webhookVersion: v1
@@ -30,7 +31,6 @@ resources:
3031
path: tutorial.kubebuilder.io/project/api/v2
3132
version: v2
3233
webhooks:
33-
conversion: true
3434
defaulting: true
3535
validation: true
3636
webhookVersion: v1

docs/book/src/multiversion-tutorial/testdata/project/api/v1/cronjob_conversion.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
/*
2+
Copyright 2024 The Kubernetes authors.
3+
24
Licensed under the Apache License, Version 2.0 (the "License");
35
you may not use this file except in compliance with the License.
46
You may obtain a copy of the License at

docs/book/src/multiversion-tutorial/testdata/project/api/v1/cronjob_types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ type CronJobStatus struct {
127127
*/
128128

129129
// +kubebuilder:object:root=true
130+
// +kubebuilder:storageversion
131+
// +kubebuilder:conversion:hub
130132
// +kubebuilder:subresource:status
131133
// +versionName=v1
132134
// +kubebuilder:storageversion

docs/book/src/multiversion-tutorial/testdata/project/api/v2/cronjob_conversion.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
/*
2+
Copyright 2024 The Kubernetes authors.
3+
24
Licensed under the Apache License, Version 2.0 (the "License");
35
you may not use this file except in compliance with the License.
46
You may obtain a copy of the License at
@@ -21,16 +23,17 @@ For imports, we'll need the controller-runtime
2123
package, plus the API version for our hub type (v1), and finally some of the
2224
standard packages.
2325
*/
26+
2427
import (
2528
"fmt"
2629
"strings"
2730

28-
"sigs.k8s.io/controller-runtime/pkg/conversion"
31+
"log"
2932

30-
v1 "tutorial.kubebuilder.io/project/api/v1"
31-
)
33+
"sigs.k8s.io/controller-runtime/pkg/conversion"
3234

33-
// +kubebuilder:docs-gen:collapse=Imports
35+
batchv1 "tutorial.kubebuilder.io/project/api/v1"
36+
) // +kubebuilder:docs-gen:collapse=Imports
3437

3538
/*
3639
Our "spoke" versions need to implement the
@@ -43,9 +46,11 @@ methods to convert to/from the hub version.
4346
ConvertTo is expected to modify its argument to contain the converted object.
4447
Most of the conversion is straightforward copying, except for converting our changed field.
4548
*/
49+
4650
// ConvertTo converts this CronJob to the Hub version (v1).
4751
func (src *CronJob) ConvertTo(dstRaw conversion.Hub) error {
48-
dst := dstRaw.(*v1.CronJob)
52+
dst := dstRaw.(*batchv1.CronJob)
53+
log.Printf("Converting from %T to %T", dst.APIVersion, src.APIVersion)
4954

5055
sched := src.Spec.Schedule
5156
scheduleParts := []string{"*", "*", "*", "*", "*"}
@@ -74,7 +79,7 @@ func (src *CronJob) ConvertTo(dstRaw conversion.Hub) error {
7479

7580
// Spec
7681
dst.Spec.StartingDeadlineSeconds = src.Spec.StartingDeadlineSeconds
77-
dst.Spec.ConcurrencyPolicy = v1.ConcurrencyPolicy(src.Spec.ConcurrencyPolicy)
82+
dst.Spec.ConcurrencyPolicy = batchv1.ConcurrencyPolicy(src.Spec.ConcurrencyPolicy)
7883
dst.Spec.Suspend = src.Spec.Suspend
7984
dst.Spec.JobTemplate = src.Spec.JobTemplate
8085
dst.Spec.SuccessfulJobsHistoryLimit = src.Spec.SuccessfulJobsHistoryLimit
@@ -85,6 +90,7 @@ func (src *CronJob) ConvertTo(dstRaw conversion.Hub) error {
8590
dst.Status.LastScheduleTime = src.Status.LastScheduleTime
8691

8792
// +kubebuilder:docs-gen:collapse=rote conversion
93+
8894
return nil
8995
}
9096

@@ -93,9 +99,10 @@ ConvertFrom is expected to modify its receiver to contain the converted object.
9399
Most of the conversion is straightforward copying, except for converting our changed field.
94100
*/
95101

96-
// ConvertFrom converts from the Hub version (v1) to this version.
102+
// ConvertFrom converts the Hub version (v1) to this CronJob (v2).
97103
func (dst *CronJob) ConvertFrom(srcRaw conversion.Hub) error {
98-
src := srcRaw.(*v1.CronJob)
104+
src := srcRaw.(*batchv1.CronJob)
105+
log.Printf("Converting from %T to %T", src.APIVersion, dst.APIVersion)
99106

100107
schedParts := strings.Split(src.Spec.Schedule, " ")
101108
if len(schedParts) != 5 {
@@ -133,5 +140,6 @@ func (dst *CronJob) ConvertFrom(srcRaw conversion.Hub) error {
133140
dst.Status.LastScheduleTime = src.Status.LastScheduleTime
134141

135142
// +kubebuilder:docs-gen:collapse=rote conversion
143+
136144
return nil
137145
}

docs/book/src/multiversion-tutorial/testdata/project/config/crd/kustomization.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ patches:
1010
# patches here are for enabling the conversion webhook for each CRD
1111
- path: patches/webhook_in_cronjobs.yaml
1212
- path: patches/webhook_in_cronjobs.yaml
13+
- path: patches/webhook_in_cronjobs.yaml
1314
# +kubebuilder:scaffold:crdkustomizewebhookpatch
1415

1516
# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix.

docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,7 @@ Next, we'll setup a logger for the webhooks.
4444
var cronjoblog = logf.Log.WithName("cronjob-resource")
4545

4646
/*
47-
This setup doubles as setup for our conversion webhooks: as long as our
48-
types implement the
49-
[Hub](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/conversion?tab=doc#Hub) and
50-
[Convertible](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/conversion?tab=doc#Convertible)
51-
interfaces, a conversion webhook will be registered.
47+
Then, we set up the webhook with the manager.
5248
*/
5349

5450
// SetupCronJobWebhookWithManager registers the webhook for CronJob in the manager.

docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v2/cronjob_webhook_test.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,4 @@ var _ = Describe("CronJob Webhook", func() {
8484
// })
8585
})
8686

87-
Context("When creating CronJob under Conversion Webhook", func() {
88-
// TODO (user): Add logic to convert the object to the desired version and verify the conversion
89-
// Example:
90-
// It("Should convert the object correctly", func() {
91-
// convertedObj := &batchv2.CronJob{}
92-
// Expect(obj.ConvertTo(convertedObj)).To(Succeed())
93-
// Expect(convertedObj).ToNot(BeNil())
94-
// })
95-
})
96-
9787
})

hack/docs/internal/multiversion-tutorial/generate_multiversion.go

Lines changed: 78 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ limitations under the License.
1717
package multiversion
1818

1919
import (
20-
"os"
2120
"os/exec"
2221
"path/filepath"
2322

@@ -57,14 +56,23 @@ func (sp *Sample) GenerateSampleProject() {
5756
)
5857
hackutils.CheckError("Creating the v2 API without controller", err)
5958

59+
log.Infof("Creating conversion webhook for v1")
60+
err = sp.ctx.CreateWebhook(
61+
"--group", "batch",
62+
"--version", "v1",
63+
"--kind", "CronJob",
64+
"--conversion",
65+
"--spoke", "v2",
66+
)
67+
hackutils.CheckError("Creating conversion webhook for v1", err)
68+
6069
log.Infof("Creating defaulting and validation webhook for v2")
6170
err = sp.ctx.CreateWebhook(
6271
"--group", "batch",
6372
"--version", "v2",
6473
"--kind", "CronJob",
6574
"--defaulting",
6675
"--programmatic-validation",
67-
"--conversion",
6876
)
6977
hackutils.CheckError("Creating defaulting and validation webhook for v2", err)
7078
}
@@ -75,25 +83,12 @@ func (sp *Sample) UpdateTutorial() {
7583
// Update files according to the multiversion
7684
sp.updateApiV1()
7785
sp.updateApiV2()
78-
sp.updateWebhookV1()
7986
sp.updateWebhookV2()
80-
sp.createHubFiles()
87+
sp.updateConversionFiles()
8188
sp.updateSampleV2()
8289
sp.updateMain()
8390
}
8491

85-
func (sp *Sample) updateWebhookV1() {
86-
err := pluginutil.ReplaceInFile(
87-
filepath.Join(sp.ctx.Dir, "internal/webhook/v1/cronjob_webhook.go"),
88-
"Then, we set up the webhook with the manager.",
89-
`This setup doubles as setup for our conversion webhooks: as long as our
90-
types implement the
91-
[Hub](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/conversion?tab=doc#Hub) and
92-
[Convertible](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/conversion?tab=doc#Convertible)
93-
interfaces, a conversion webhook will be registered.`,
94-
)
95-
hackutils.CheckError("replace webhook setup text", err)
96-
}
9792
func (sp *Sample) updateSampleV2() {
9893
path := filepath.Join(sp.ctx.Dir, "config/samples/batch_v2_cronjob.yaml")
9994
oldText := `# TODO(user): Add fields here`
@@ -106,29 +101,82 @@ func (sp *Sample) updateSampleV2() {
106101
hackutils.CheckError("replacing TODO with sampleV2Code in batch_v2_cronjob.yaml", err)
107102
}
108103

109-
func (sp *Sample) createHubFiles() {
104+
func (sp *Sample) updateConversionFiles() {
110105
path := filepath.Join(sp.ctx.Dir, "api/v1/cronjob_conversion.go")
111106

112-
_, err := os.Create(path)
113-
hackutils.CheckError("creating conversion file v1", err)
107+
err := pluginutil.InsertCodeIfNotExist(path,
108+
"limitations under the License.\n*/",
109+
"\n// +kubebuilder:docs-gen:collapse=Apache License")
110+
hackutils.CheckError("appending into hub v1 collapse docs", err)
114111

115-
err = pluginutil.AppendCodeAtTheEnd(path, "")
116-
hackutils.CheckError("creating empty conversion file v1", err)
117-
118-
err = pluginutil.AppendCodeAtTheEnd(path, hubV1Code)
119-
hackutils.CheckError("appending hubV1Code to cronjob_conversion.go", err)
112+
err = pluginutil.ReplaceInFile(path,
113+
"// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!",
114+
hubV1CodeComment)
115+
hackutils.CheckError("adding comment to hub v1", err)
120116

121117
path = filepath.Join(sp.ctx.Dir, "api/v2/cronjob_conversion.go")
122118

123-
_, err = os.Create(path)
124-
hackutils.CheckError("creating conversion file v2", err)
119+
err = pluginutil.InsertCodeIfNotExist(path,
120+
"limitations under the License.\n*/",
121+
"\n// +kubebuilder:docs-gen:collapse=Apache License")
122+
hackutils.CheckError("appending into hub v2 collapse docs", err)
125123

126-
err = pluginutil.AppendCodeAtTheEnd(path, "")
127-
hackutils.CheckError("creating empty conversion file v2", err)
124+
err = pluginutil.InsertCode(path,
125+
"import (",
126+
`
127+
"fmt"
128+
"strings"
128129
129-
err = pluginutil.AppendCodeAtTheEnd(path, hubV2Code)
130-
hackutils.CheckError("appending hubV2Code to cronjob_conversion.go", err)
130+
`)
131+
hackutils.CheckError("adding imports to hub v2", err)
132+
133+
err = pluginutil.InsertCodeIfNotExist(path,
134+
"batchv1 \"tutorial.kubebuilder.io/project/api/v1\"\n)",
135+
`// +kubebuilder:docs-gen:collapse=Imports
136+
137+
/*
138+
Our "spoke" versions need to implement the
139+
[`+"`"+`Convertible`+"`"+`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/conversion?tab=doc#Convertible)
140+
interface. Namely, they'll need `+"`"+`ConvertTo()`+"`"+` and `+"`"+`ConvertFrom()`+"`"+`
141+
methods to convert to/from the hub version.
142+
*/
143+
`)
144+
hackutils.CheckError("appending into hub v2 collapse docs", err)
145+
146+
err = pluginutil.ReplaceInFile(path,
147+
"package v2",
148+
hubV2CodeComment)
149+
hackutils.CheckError("adding comment to hub v2", err)
150+
151+
err = pluginutil.ReplaceInFile(path,
152+
"// TODO: Implement conversion logic from v2 to v1",
153+
hubV2CovertTo)
154+
hackutils.CheckError("replace covertTo at hub v2", err)
155+
156+
err = pluginutil.ReplaceInFile(path,
157+
"// TODO: Implement conversion logic from v1 to v2",
158+
hubV2ConvertFromCode)
159+
hackutils.CheckError("replace covert from at hub v2", err)
160+
161+
err = pluginutil.ReplaceInFile(path,
162+
"// ConvertFrom converts the Hub version (v1) to this CronJob (v2).",
163+
`/*
164+
ConvertFrom is expected to modify its receiver to contain the converted object.
165+
Most of the conversion is straightforward copying, except for converting our changed field.
166+
*/
167+
168+
// ConvertFrom converts the Hub version (v1) to this CronJob (v2).`)
169+
hackutils.CheckError("replace covert from info at hub v2", err)
170+
171+
err = pluginutil.ReplaceInFile(path,
172+
"// ConvertTo converts this CronJob to the Hub version (v1).",
173+
`/*
174+
ConvertTo is expected to modify its argument to contain the converted object.
175+
Most of the conversion is straightforward copying, except for converting our changed field.
176+
*/
131177
178+
// ConvertTo converts this CronJob to the Hub version (v1).`)
179+
hackutils.CheckError("replace covert info at hub v2", err)
132180
}
133181

134182
func (sp *Sample) updateApiV1() {

0 commit comments

Comments
 (0)