Skip to content

Commit

Permalink
Add the unit test template for webhook and controllers.
Browse files Browse the repository at this point in the history
Signed-off-by: dashanji <dashanjic@gmail.com>
  • Loading branch information
dashanji committed Oct 9, 2023
1 parent f06fb9e commit 54748ad
Show file tree
Hide file tree
Showing 48 changed files with 2,898 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
Copyright 2023 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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1

import (
. "github.com/onsi/ginkgo/v2"
)

var _ = Describe("CronJob Webhook", func() {

Context("When creating CronJob under Defaulting Webhook", func() {
It("Should fill in the default value if a required field is empty", func() {

// TODO(user): Add your logic here

})
})

Context("When creating CronJob under Validating Webhook", func() {
It("Should deny if a required field is empty", func() {

// TODO(user): Add your logic here

})

It("Should admit if all required fields are provided", func() {

// TODO(user): Add your logic here

})
})

})
2 changes: 1 addition & 1 deletion pkg/plugin/util/testdata/exampleFile.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
exampleTargetexampleCodeexampleCodeexampleCodeexampleCodeexampleCodeexampleCode
exampleTargetexampleCodeexampleCodeexampleCodeexampleCodeexampleCodeexampleCodeexampleCode
1 change: 1 addition & 0 deletions pkg/plugins/common/kustomize/v2/scaffolds/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package scaffolds

import (
"fmt"

pluginutil "sigs.k8s.io/kubebuilder/v3/pkg/plugin/util"

log "github.com/sirupsen/logrus"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ func (f *ControllerTest) SetTemplateDefaults() error {
f.PackageName = "controllers"
}

f.IfExistsAction = machinery.OverwriteFile

log.Println("creating import for %", f.Resource.Path)
f.TemplateBody = controllerTestTemplate

Expand Down
1 change: 1 addition & 0 deletions pkg/plugins/golang/v4/scaffolds/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ func (s *apiScaffolder) Scaffold() error {
if err := scaffold.Execute(
&controllers.SuiteTest{Force: s.force, K8SVersion: EnvtestK8SVersion},
&controllers.Controller{ControllerRuntimeVersion: ControllerRuntimeVersion, Force: s.force},
&controllers.ControllerTest{Force: s.force, DoAPI: doAPI},
); err != nil {
return fmt.Errorf("error scaffolding controller: %v", err)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
Copyright 2022 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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package api

import (
"fmt"
"path/filepath"
"strings"

log "github.com/sirupsen/logrus"

"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
)

var _ machinery.Template = &WebhookTest{}

// WebhookTest scaffolds the file that sets up the webhook unit tests
type WebhookTest struct { // nolint:maligned
machinery.TemplateMixin
machinery.MultiGroupMixin
machinery.BoilerplateMixin
machinery.ResourceMixin

Force bool
}

// SetTemplateDefaults implements file.Template
func (f *WebhookTest) SetTemplateDefaults() error {
if f.Path == "" {
if f.MultiGroup && f.Resource.Group != "" {
f.Path = filepath.Join("api", "%[group]", "%[version]", "%[kind]_webhook_test.go")
} else {
f.Path = filepath.Join("api", "%[version]", "%[kind]_webhook_test.go")
}
}
f.Path = f.Resource.Replacer().Replace(f.Path)
log.Println(f.Path)

webhookTestTemplate := webhookTestTemplate
templates := make([]string, 0)
if f.Resource.HasDefaultingWebhook() {
templates = append(templates, defaultWebhookTestTemplate)
}
if f.Resource.HasValidationWebhook() {
templates = append(templates, validateWebhookTestTemplate)
}
if f.Resource.HasConversionWebhook() {
templates = append(templates, conversionWebhookTestTemplate)
}
f.TemplateBody = fmt.Sprintf(webhookTestTemplate, strings.Join(templates, "\n"))

if f.Force {
f.IfExistsAction = machinery.OverwriteFile
}

return nil
}

const webhookTestTemplate = `{{ .Boilerplate }}
package {{ .Resource.Version }}
import (
. "github.com/onsi/ginkgo/v2"
)
var _ = Describe("{{ .Resource.Kind }} Webhook", func() {
%s
})
`

const conversionWebhookTestTemplate = `
Context("When creating {{ .Resource.Kind }} under Conversion Webhook", func() {
It("Should get the converted version of {{ .Resource.Kind }}" , func() {
// TODO(user): Add your logic here
})
})
`

const validateWebhookTestTemplate = `
Context("When creating {{ .Resource.Kind }} under Validating Webhook", func() {
It("Should deny if a required field is empty", func() {
// TODO(user): Add your logic here
})
It("Should admit if all required fields are provided", func() {
// TODO(user): Add your logic here
})
})
`

const defaultWebhookTestTemplate = `
Context("When creating {{ .Resource.Kind }} under Defaulting Webhook", func() {
It("Should fill in the default value if a required field is empty", func() {
// TODO(user): Add your logic here
})
})
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
Copyright 2022 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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package controllers

import (
"path/filepath"

log "github.com/sirupsen/logrus"

"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
)

var _ machinery.Template = &ControllerTest{}

// ControllerTest scaffolds the file that sets up the controller unit tests
// nolint:maligned
type ControllerTest struct {
machinery.TemplateMixin
machinery.MultiGroupMixin
machinery.BoilerplateMixin
machinery.ResourceMixin

Force bool

DoAPI bool
}

// SetTemplateDefaults implements file.Template
func (f *ControllerTest) SetTemplateDefaults() error {
if f.Path == "" {
if f.MultiGroup && f.Resource.Group != "" {
f.Path = filepath.Join("internal", "controller", "%[group]", "%[kind]_controller_test.go")
} else {
f.Path = filepath.Join("internal", "controller", "%[kind]_controller_test.go")
}
}

f.Path = f.Resource.Replacer().Replace(f.Path)
log.Println(f.Path)

f.TemplateBody = controllerTestTemplate

if f.Force {
f.IfExistsAction = machinery.OverwriteFile
}

return nil
}

const controllerTestTemplate = `{{ .Boilerplate }}
{{if and .MultiGroup .Resource.Group }}
package {{ .Resource.PackageName }}
{{else}}
package controller
{{end}}
import (
{{ if .DoAPI -}}
"context"
{{- end }}
. "github.com/onsi/ginkgo/v2"
{{ if .DoAPI -}}
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
{{ if not (isEmptyStr .Resource.Path) -}}
{{ .Resource.ImportAlias }} "{{ .Resource.Path }}"
{{- end }}
{{- end }}
)
var _ = Describe("{{ .Resource.Kind }} Controller", func() {
Context("When reconciling a resource", func() {
{{ if .DoAPI -}}
const resourceName = "test-resource"
ctx := context.Background()
typeNamespacedName := types.NamespacedName{
Name: resourceName,
Namespace: "default", // TODO(user):Modify as needed
}
{{ lower .Resource.Kind }} := &{{ .Resource.ImportAlias }}.{{ .Resource.Kind }}{}
BeforeEach(func() {
By("creating the custom resource for the Kind {{ .Resource.Kind }}")
err := k8sClient.Get(ctx, typeNamespacedName, {{ lower .Resource.Kind }})
if err != nil && errors.IsNotFound(err) {
resource := &{{ .Resource.ImportAlias }}.{{ .Resource.Kind }}{
ObjectMeta: metav1.ObjectMeta{
Name: resourceName,
Namespace: "default",
},
// TODO(user): Specify other spec details if needed.
}
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
}
})
AfterEach(func() {
// TODO(user): Cleanup logic after each test, like removing the resource instance.
resource := &{{ .Resource.ImportAlias }}.{{ .Resource.Kind }}{}
err := k8sClient.Get(ctx, typeNamespacedName, resource)
Expect(err).NotTo(HaveOccurred())
By("Cleanup the specific resource instance {{ .Resource.Kind }}")
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
})
{{- end }}
It("should successfully reconcile the resource", func() {
{{ if .DoAPI -}}
By("Reconciling the created resource")
controllerReconciler := &{{ .Resource.Kind }}Reconciler{
Client: k8sClient,
Scheme: k8sClient.Scheme(),
}
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: typeNamespacedName,
})
Expect(err).NotTo(HaveOccurred())
{{- end }}
// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
// Example: If you expect a certain status condition after reconciliation, verify it here.
})
})
})
`
1 change: 1 addition & 0 deletions pkg/plugins/golang/v4/scaffolds/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ func (s *webhookScaffolder) Scaffold() error {
if err := scaffold.Execute(
&api.Webhook{Force: s.force},
&templates.MainUpdater{WireWebhook: true},
&api.WebhookTest{Force: s.force},
); err != nil {
return err
}
Expand Down

0 comments on commit 54748ad

Please sign in to comment.