Skip to content

Commit

Permalink
dd
Browse files Browse the repository at this point in the history
  • Loading branch information
camilamacedo86 committed Nov 10, 2024
1 parent ad4afdb commit b42ccaf
Show file tree
Hide file tree
Showing 38 changed files with 588 additions and 190 deletions.
12 changes: 10 additions & 2 deletions docs/book/src/multiversion-tutorial/conversion.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
# Implementing conversion

With our model for conversion in place, it's time to actually implement
the conversion functions. We'll put them in a file called
`cronjob_conversion.go` next to our `cronjob_types.go` file, to avoid
the conversion functions. We'll create a conversion webhook
for our CronJob API version `v1` (Hub) to Spoke our CronJob API version
`v2` see:

```go
kubebuilder create webhook --group batch --version v1 --kind CronJob --conversion--spoke v2
```

The above command will generate the `cronjob_conversion.go` next to our
`cronjob_types.go` file, to avoid
cluttering up our main types file with extra functions.

## Hub...
Expand Down
3 changes: 2 additions & 1 deletion docs/book/src/multiversion-tutorial/testdata/project/PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ resources:
path: tutorial.kubebuilder.io/project/api/v1
version: v1
webhooks:
conversion: true
defaulting: true
spoke: v2
validation: true
webhookVersion: v1
- api:
Expand All @@ -30,7 +32,6 @@ resources:
path: tutorial.kubebuilder.io/project/api/v2
version: v2
webhooks:
conversion: true
defaulting: true
validation: true
webhookVersion: v1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/*
Copyright 2024 The Kubernetes authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ type CronJobStatus struct {
*/

// +kubebuilder:object:root=true
// +kubebuilder:storageversion
// +kubebuilder:conversion:hub
// +kubebuilder:subresource:status
// +versionName=v1
// +kubebuilder:storageversion
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/*
Copyright 2024 The Kubernetes authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Expand All @@ -21,16 +23,17 @@ For imports, we'll need the controller-runtime
package, plus the API version for our hub type (v1), and finally some of the
standard packages.
*/

import (
"fmt"
"strings"

"sigs.k8s.io/controller-runtime/pkg/conversion"
"log"

v1 "tutorial.kubebuilder.io/project/api/v1"
)
"sigs.k8s.io/controller-runtime/pkg/conversion"

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

/*
Our "spoke" versions need to implement the
Expand All @@ -43,9 +46,11 @@ methods to convert to/from the hub version.
ConvertTo is expected to modify its argument to contain the converted object.
Most of the conversion is straightforward copying, except for converting our changed field.
*/

// ConvertTo converts this CronJob to the Hub version (v1).
func (src *CronJob) ConvertTo(dstRaw conversion.Hub) error {
dst := dstRaw.(*v1.CronJob)
dst := dstRaw.(*batchv1.CronJob)
log.Printf("Converting from %T to %T", dst.APIVersion, src.APIVersion)

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

// Spec
dst.Spec.StartingDeadlineSeconds = src.Spec.StartingDeadlineSeconds
dst.Spec.ConcurrencyPolicy = v1.ConcurrencyPolicy(src.Spec.ConcurrencyPolicy)
dst.Spec.ConcurrencyPolicy = batchv1.ConcurrencyPolicy(src.Spec.ConcurrencyPolicy)
dst.Spec.Suspend = src.Spec.Suspend
dst.Spec.JobTemplate = src.Spec.JobTemplate
dst.Spec.SuccessfulJobsHistoryLimit = src.Spec.SuccessfulJobsHistoryLimit
Expand All @@ -85,6 +90,7 @@ func (src *CronJob) ConvertTo(dstRaw conversion.Hub) error {
dst.Status.LastScheduleTime = src.Status.LastScheduleTime

// +kubebuilder:docs-gen:collapse=rote conversion

return nil
}

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

// ConvertFrom converts from the Hub version (v1) to this version.
// ConvertFrom converts the Hub version (v1) to this CronJob (v2).
func (dst *CronJob) ConvertFrom(srcRaw conversion.Hub) error {
src := srcRaw.(*v1.CronJob)
src := srcRaw.(*batchv1.CronJob)
log.Printf("Converting from %T to %T", src.APIVersion, dst.APIVersion)

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

// +kubebuilder:docs-gen:collapse=rote conversion

return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,7 @@ Next, we'll setup a logger for the webhooks.
var cronjoblog = logf.Log.WithName("cronjob-resource")

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

// SetupCronJobWebhookWithManager registers the webhook for CronJob in the manager.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,4 @@ var _ = Describe("CronJob Webhook", func() {
// })
})

Context("When creating CronJob under Conversion Webhook", func() {
// TODO (user): Add logic to convert the object to the desired version and verify the conversion
// Example:
// It("Should convert the object correctly", func() {
// convertedObj := &batchv2.CronJob{}
// Expect(obj.ConvertTo(convertedObj)).To(Succeed())
// Expect(convertedObj).ToNot(BeNil())
// })
})

})
9 changes: 0 additions & 9 deletions docs/book/src/multiversion-tutorial/webhooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,6 @@
Our conversion is in place, so all that's left is to tell
controller-runtime about our conversion.

Normally, we'd run

```shell
kubebuilder create webhook --group batch --version v1 --kind CronJob --conversion
```

to scaffold out the webhook setup. However, we've already got webhook
setup, from when we built our defaulting and validating webhooks!

## Webhook setup...

{{#literatego ./testdata/project/internal/webhook/v1/cronjob_webhook.go}}
Expand Down
1 change: 1 addition & 0 deletions docs/book/src/reference/project-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ Now let's check its layout fields definition:
| `resources.core` | It is `true` when the group used is from Kubernetes API and the API resource is not defined on the project. |
| `resources.external` | It is `true` when the flag `--external-api-path` was used to generated the scaffold for an [External Type][external-type]. |
| `resources.webhooks` | Store the webhooks data when the sub-command `create webhook` is used. |
| `resources.webhooks.spoke` | Store the API version that will act as the Spoke with the designated Hub version for conversion webhooks. |
| `resources.webhooks.webhookVersion` | The Kubernetes API version (`apiVersion`) used to scaffold the webhook resource. |
| `resources.webhooks.conversion` | It is `true` when the webhook was scaffold with the `--conversion` flag which means that is a conversion webhook. |
| `resources.webhooks.defaulting` | It is `true` when the webhook was scaffold with the `--defaulting` flag which means that is a defaulting webhook. |
Expand Down
108 changes: 78 additions & 30 deletions hack/docs/internal/multiversion-tutorial/generate_multiversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ limitations under the License.
package multiversion

import (
"os"
"os/exec"
"path/filepath"

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

log.Infof("Creating conversion webhook for v1")
err = sp.ctx.CreateWebhook(
"--group", "batch",
"--version", "v1",
"--kind", "CronJob",
"--conversion",
"--spoke", "v2",
)
hackutils.CheckError("Creating conversion webhook for v1", err)

log.Infof("Creating defaulting and validation webhook for v2")
err = sp.ctx.CreateWebhook(
"--group", "batch",
"--version", "v2",
"--kind", "CronJob",
"--defaulting",
"--programmatic-validation",
"--conversion",
)
hackutils.CheckError("Creating defaulting and validation webhook for v2", err)
}
Expand All @@ -75,9 +83,8 @@ func (sp *Sample) UpdateTutorial() {
// Update files according to the multiversion
sp.updateApiV1()
sp.updateApiV2()
sp.updateWebhookV1()
sp.updateWebhookV2()
sp.createHubFiles()
sp.updateConversionFiles()
sp.updateSampleV2()
sp.updateMain()
sp.updateDefaultKustomize()
Expand All @@ -91,18 +98,6 @@ func (sp *Sample) updateDefaultKustomize() {
hackutils.CheckError("fixing default/kustomization", err)
}

func (sp *Sample) updateWebhookV1() {
err := pluginutil.ReplaceInFile(
filepath.Join(sp.ctx.Dir, "internal/webhook/v1/cronjob_webhook.go"),
"Then, we set up the webhook with the manager.",
`This setup doubles as setup for our conversion webhooks: as long as our
types implement the
[Hub](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/conversion?tab=doc#Hub) and
[Convertible](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/conversion?tab=doc#Convertible)
interfaces, a conversion webhook will be registered.`,
)
hackutils.CheckError("replace webhook setup text", err)
}
func (sp *Sample) updateSampleV2() {
path := filepath.Join(sp.ctx.Dir, "config/samples/batch_v2_cronjob.yaml")
oldText := `# TODO(user): Add fields here`
Expand All @@ -115,29 +110,82 @@ func (sp *Sample) updateSampleV2() {
hackutils.CheckError("replacing TODO with sampleV2Code in batch_v2_cronjob.yaml", err)
}

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

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

err = pluginutil.AppendCodeAtTheEnd(path, "")
hackutils.CheckError("creating empty conversion file v1", err)

err = pluginutil.AppendCodeAtTheEnd(path, hubV1Code)
hackutils.CheckError("appending hubV1Code to cronjob_conversion.go", err)
err = pluginutil.ReplaceInFile(path,
"// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!",
hubV1CodeComment)
hackutils.CheckError("adding comment to hub v1", err)

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

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

err = pluginutil.AppendCodeAtTheEnd(path, "")
hackutils.CheckError("creating empty conversion file v2", err)
err = pluginutil.InsertCode(path,
"import (",
`
"fmt"
"strings"
err = pluginutil.AppendCodeAtTheEnd(path, hubV2Code)
hackutils.CheckError("appending hubV2Code to cronjob_conversion.go", err)
`)
hackutils.CheckError("adding imports to hub v2", err)

err = pluginutil.InsertCodeIfNotExist(path,
"batchv1 \"tutorial.kubebuilder.io/project/api/v1\"\n)",
`// +kubebuilder:docs-gen:collapse=Imports
/*
Our "spoke" versions need to implement the
[`+"`"+`Convertible`+"`"+`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/conversion?tab=doc#Convertible)
interface. Namely, they'll need `+"`"+`ConvertTo()`+"`"+` and `+"`"+`ConvertFrom()`+"`"+`
methods to convert to/from the hub version.
*/
`)
hackutils.CheckError("appending into hub v2 collapse docs", err)

err = pluginutil.ReplaceInFile(path,
"package v2",
hubV2CodeComment)
hackutils.CheckError("adding comment to hub v2", err)

err = pluginutil.ReplaceInFile(path,
"// TODO: Implement conversion logic from v2 to v1",
hubV2CovertTo)
hackutils.CheckError("replace covertTo at hub v2", err)

err = pluginutil.ReplaceInFile(path,
"// TODO: Implement conversion logic from v1 to v2",
hubV2ConvertFromCode)
hackutils.CheckError("replace covert from at hub v2", err)

err = pluginutil.ReplaceInFile(path,
"// ConvertFrom converts the Hub version (v1) to this CronJob (v2).",
`/*
ConvertFrom is expected to modify its receiver to contain the converted object.
Most of the conversion is straightforward copying, except for converting our changed field.
*/
// ConvertFrom converts the Hub version (v1) to this CronJob (v2).`)
hackutils.CheckError("replace covert from info at hub v2", err)

err = pluginutil.ReplaceInFile(path,
"// ConvertTo converts this CronJob to the Hub version (v1).",
`/*
ConvertTo is expected to modify its argument to contain the converted object.
Most of the conversion is straightforward copying, except for converting our changed field.
*/
// ConvertTo converts this CronJob to the Hub version (v1).`)
hackutils.CheckError("replace covert info at hub v2", err)
}

func (sp *Sample) updateApiV1() {
Expand Down
Loading

0 comments on commit b42ccaf

Please sign in to comment.