Skip to content

Commit

Permalink
✨ add new kubebuider scaffold makers to webhooks checks on e2e tests
Browse files Browse the repository at this point in the history
We are enabling the execution of e2e tests for deploy-image in the GitHub action. However, to allow we easily configure the tests we needed to change the scaffold of the samples to have defaulting and validation webhooks.

So, we just added the --defaulting flag to generate the webhook for the testdata sample using deploy image and regenerated it. If you want to understand why see: kubernetes-sigs#4119
  • Loading branch information
camilamacedo86 committed Aug 31, 2024
1 parent a5fe5be commit 58f086f
Show file tree
Hide file tree
Showing 25 changed files with 527 additions and 45 deletions.
16 changes: 6 additions & 10 deletions .github/workflows/test-e2e-samples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,9 @@ jobs:
KUSTOMIZATION_FILE_PATH="testdata/project-v4/config/default/kustomization.yaml"
sed -i '25s/^#//' $KUSTOMIZATION_FILE_PATH
sed -i '51s/^#//' $KUSTOMIZATION_FILE_PATH
sed -i '55,151s/^#//' $KUSTOMIZATION_FILE_PATH
cd testdata/project-v4/
go mod tidy
make generate
make manifests
- name: Testing make test-e2e for project-v4
working-directory: testdata/project-v4/
Expand All @@ -54,14 +53,11 @@ jobs:
KUSTOMIZATION_FILE_PATH="testdata/project-v4-with-deploy-image/config/default/kustomization.yaml"
sed -i '25s/^#//' $KUSTOMIZATION_FILE_PATH
sed -i '51s/^#//' $KUSTOMIZATION_FILE_PATH
sed -i '55,151s/^#//' $KUSTOMIZATION_FILE_PATH
cd testdata/project-v4-with-deploy-image/
go mod tidy
make generate
make manifests
# Fixme: The e2e tests for deploy image are failing and we
# need to fix in a follow up
# - name: Testing make test-e2e for project-v4-with-deploy-image
# working-directory: testdata/project-v4-with-deploy-image
# run: |
# make test-e2e
- name: Testing make test-e2e for project-v4-with-deploy-image
working-directory: testdata/project-v4-with-deploy-image
run: |
make test-e2e
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,19 @@ func TestE2E(t *testing.T) {
}

var _ = BeforeSuite(func() {
By("building the manager(Operator) image")
cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage))
By("generating files")
cmd := exec.Command("make", "generate")
_, err := utils.Run(cmd)
ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to run make generate")

By("generating manifests")
cmd = exec.Command("make", "manifests")
_, err = utils.Run(cmd)
ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to run make manifests")

By("building the manager(Operator) image")
cmd = exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage))
_, err = utils.Run(cmd)
ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image")

// TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ const namespace = "project-system"

// Define a set of end-to-end (e2e) tests to validate the behavior of the controller.
var _ = Describe("controller", Ordered, func() {
// controllerPodName stores the name of the controller pod
var controllerPodName string

// Before running the tests, set up the environment by creating the namespace,
// installing CRDs, and deploying the controller.
BeforeAll(func() {
Expand Down Expand Up @@ -69,8 +72,6 @@ var _ = Describe("controller", Ordered, func() {
// The Context block contains the actual tests that validate the operator's behavior.
Context("Operator", func() {
It("should run successfully", func() {
var controllerPodName string

By("validating that the controller-manager pod is running as expected")
verifyControllerUp := func() error {
// Get the name of the controller-manager pod
Expand Down Expand Up @@ -108,6 +109,47 @@ var _ = Describe("controller", Ordered, func() {
EventuallyWithOffset(1, verifyControllerUp, time.Minute, time.Second).Should(Succeed())
})

It("cert-manager should be successfully provisioned", func() {
By("validating that cert-manager has provisioned the certificate Secret")
EventuallyWithOffset(1, func() error {
cmd := exec.Command("kubectl", "get", "secrets", "webhook-server-cert", "-n", namespace)
_, err := utils.Run(cmd)
return err
}, time.Minute, time.Second).Should(Succeed())
})

It("should validate that the mutating webhooks have the CA injected", func() {
By("checking CA injection for mutating webhooks")
verifyCAInjection := func() error {
cmd := exec.Command("kubectl", "get",
"mutatingwebhookconfigurations.admissionregistration.k8s.io",
"project-mutating-webhook-configuration",
"-o", "go-template={{ range .webhooks }}{{ .clientConfig.caBundle }}{{ end }}")
mwhOutput, err := utils.Run(cmd)
ExpectWithOffset(2, err).NotTo(HaveOccurred())
ExpectWithOffset(2, len(mwhOutput)).To(BeNumerically(">", 10))
return nil
}
EventuallyWithOffset(1, verifyCAInjection, time.Minute, time.Second).Should(Succeed())
})

It("should validate that the validating webhooks have the CA injected", func() {
By("checking CA injection for validating webhooks")
verifyCAInjection := func() error {
cmd := exec.Command("kubectl", "get",
"validatingwebhookconfigurations.admissionregistration.k8s.io",
"project-validating-webhook-configuration",
"-o", "go-template={{ range .webhooks }}{{ .clientConfig.caBundle }}{{ end }}")
vwhOutput, err := utils.Run(cmd)
ExpectWithOffset(2, err).NotTo(HaveOccurred())
ExpectWithOffset(2, len(vwhOutput)).To(BeNumerically(">", 10))
return nil
}
EventuallyWithOffset(1, verifyCAInjection, time.Minute, time.Second).Should(Succeed())
})

// +kubebuilder:scaffold:webhookchecks

// TODO(user): Customize the e2e test suite to include
// additional scenarios specific to your project.
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,19 @@ func TestE2E(t *testing.T) {
}

var _ = BeforeSuite(func() {
By("building the manager(Operator) image")
cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage))
By("generating files")
cmd := exec.Command("make", "generate")
_, err := utils.Run(cmd)
ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to run make generate")

By("generating manifests")
cmd = exec.Command("make", "manifests")
_, err = utils.Run(cmd)
ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to run make manifests")

By("building the manager(Operator) image")
cmd = exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage))
_, err = utils.Run(cmd)
ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image")

// TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ const namespace = "project-system"

// Define a set of end-to-end (e2e) tests to validate the behavior of the controller.
var _ = Describe("controller", Ordered, func() {
// controllerPodName stores the name of the controller pod
var controllerPodName string

// Before running the tests, set up the environment by creating the namespace,
// installing CRDs, and deploying the controller.
BeforeAll(func() {
Expand Down Expand Up @@ -69,8 +72,6 @@ var _ = Describe("controller", Ordered, func() {
// The Context block contains the actual tests that validate the operator's behavior.
Context("Operator", func() {
It("should run successfully", func() {
var controllerPodName string

By("validating that the controller-manager pod is running as expected")
verifyControllerUp := func() error {
// Get the name of the controller-manager pod
Expand Down Expand Up @@ -108,6 +109,8 @@ var _ = Describe("controller", Ordered, func() {
EventuallyWithOffset(1, verifyControllerUp, time.Minute, time.Second).Should(Succeed())
})

// +kubebuilder:scaffold:webhookchecks

// TODO(user): Customize the e2e test suite to include
// additional scenarios specific to your project.
})
Expand Down
1 change: 1 addition & 0 deletions pkg/plugins/golang/v4/scaffolds/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ func (s *initScaffolder) Scaffold() error {
&templates.Readme{},
&templates.Golangci{},
&e2e.Test{},
&e2e.WebhookTestUpdater{WireWebhook: false},
&e2e.SuiteTest{},
&utils.Utils{},
&templates.DevContainer{},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,19 @@ func TestE2E(t *testing.T) {
}
var _ = BeforeSuite(func() {
By("building the manager(Operator) image")
cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage))
By("generating files")
cmd := exec.Command("make", "generate")
_, err := utils.Run(cmd)
ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to run make generate")
By("generating manifests")
cmd = exec.Command("make", "manifests")
_, err = utils.Run(cmd)
ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to run make manifests")
By("building the manager(Operator) image")
cmd = exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage))
_, err = utils.Run(cmd)
ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image")
// TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is
Expand Down
122 changes: 118 additions & 4 deletions pkg/plugins/golang/v4/scaffolds/internal/templates/test/e2e/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,18 @@ limitations under the License.
package e2e

import (
"fmt"
"path/filepath"

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

var _ machinery.Template = &SuiteTest{}
var _ machinery.Template = &Test{}
var _ machinery.Inserter = &WebhookTestUpdater{}

const webhookChecksMarker = "webhookchecks"

// Test defines the basic setup for the e2e test
type Test struct {
machinery.TemplateMixin
machinery.BoilerplateMixin
Expand All @@ -31,13 +38,117 @@ type Test struct {

func (f *Test) SetTemplateDefaults() error {
if f.Path == "" {
f.Path = "test/e2e/e2e_test.go"
f.Path = filepath.Join("test", "e2e", "e2e_test.go")
}

// This is where the template body is defined with markers
f.TemplateBody = TestTemplate

return nil
}

// WebhookTestUpdater updates e2e_test.go to insert additional webhook validation tests
type WebhookTestUpdater struct {
machinery.RepositoryMixin
machinery.ProjectNameMixin
machinery.ResourceMixin
WireWebhook bool
}

// GetPath implements file.Builder
func (*WebhookTestUpdater) GetPath() string {
return filepath.Join("test", "e2e", "e2e_test.go")
}

// GetIfExistsAction implements file.Builder
func (*WebhookTestUpdater) GetIfExistsAction() machinery.IfExistsAction {
return machinery.OverwriteFile // Ensures only the marker is replaced
}

// GetMarkers implements file.Inserter
func (f *WebhookTestUpdater) GetMarkers() []machinery.Marker {
return []machinery.Marker{
machinery.NewMarkerFor(f.GetPath(), webhookChecksMarker),
}
}

// GetCodeFragments implements file.Inserter
func (f *WebhookTestUpdater) GetCodeFragments() machinery.CodeFragmentsMap {
codeFragments := machinery.CodeFragmentsMap{}
if !f.WireWebhook {
return nil
}
if f.WireWebhook {
codeFragments[machinery.NewMarkerFor(f.GetPath(), webhookChecksMarker)] = append(
codeFragments[machinery.NewMarkerFor(f.GetPath(), webhookChecksMarker)],
webhookChecksFragment,
)
}

if f.Resource != nil && f.Resource.HasDefaultingWebhook() {
mutatingWebhookCode := fmt.Sprintf(mutatingWebhookChecksFragment, f.ProjectName)
codeFragments[machinery.NewMarkerFor(f.GetPath(), webhookChecksMarker)] = append(
codeFragments[machinery.NewMarkerFor(f.GetPath(), webhookChecksMarker)],
mutatingWebhookCode,
)
}

if f.Resource.HasValidationWebhook() {
validatingWebhookCode := fmt.Sprintf(validatingWebhookChecksFragment, f.ProjectName)
codeFragments[machinery.NewMarkerFor(f.GetPath(), webhookChecksMarker)] = append(
codeFragments[machinery.NewMarkerFor(f.GetPath(), webhookChecksMarker)],
validatingWebhookCode,
)
}

return codeFragments
}

const webhookChecksFragment = `It("cert-manager should be successfully provisioned", func() {
By("validating that cert-manager has provisioned the certificate Secret")
EventuallyWithOffset(1, func() error {
cmd := exec.Command("kubectl", "get", "secrets", "webhook-server-cert", "-n", namespace)
_, err := utils.Run(cmd)
return err
}, time.Minute, time.Second).Should(Succeed())
})
`

const mutatingWebhookChecksFragment = `It("should validate that the mutating webhooks have the CA injected", func() {
By("checking CA injection for mutating webhooks")
verifyCAInjection := func() error {
cmd := exec.Command("kubectl", "get",
"mutatingwebhookconfigurations.admissionregistration.k8s.io",
"%s-mutating-webhook-configuration",
"-o", "go-template={{ range .webhooks }}{{ .clientConfig.caBundle }}{{ end }}")
mwhOutput, err := utils.Run(cmd)
ExpectWithOffset(2, err).NotTo(HaveOccurred())
ExpectWithOffset(2, len(mwhOutput)).To(BeNumerically(">", 10))
return nil
}
EventuallyWithOffset(1, verifyCAInjection, time.Minute, time.Second).Should(Succeed())
})
`

const validatingWebhookChecksFragment = `It("should validate that the validating webhooks have the CA injected", func() {
By("checking CA injection for validating webhooks")
verifyCAInjection := func() error {
cmd := exec.Command("kubectl", "get",
"validatingwebhookconfigurations.admissionregistration.k8s.io",
"%s-validating-webhook-configuration",
"-o", "go-template={{ range .webhooks }}{{ .clientConfig.caBundle }}{{ end }}")
vwhOutput, err := utils.Run(cmd)
ExpectWithOffset(2, err).NotTo(HaveOccurred())
ExpectWithOffset(2, len(vwhOutput)).To(BeNumerically(">", 10))
return nil
}
EventuallyWithOffset(1, verifyCAInjection, time.Minute, time.Second).Should(Succeed())
})
`

var TestTemplate = `{{ .Boilerplate }}
Expand All @@ -58,6 +169,9 @@ const namespace = "{{ .ProjectName }}-system"
// Define a set of end-to-end (e2e) tests to validate the behavior of the controller.
var _ = Describe("controller", Ordered, func() {
// controllerPodName stores the name of the controller pod
var controllerPodName string
// Before running the tests, set up the environment by creating the namespace,
// installing CRDs, and deploying the controller.
BeforeAll(func() {
Expand Down Expand Up @@ -96,8 +210,6 @@ var _ = Describe("controller", Ordered, func() {
// The Context block contains the actual tests that validate the operator's behavior.
Context("Operator", func() {
It("should run successfully", func() {
var controllerPodName string
By("validating that the controller-manager pod is running as expected")
verifyControllerUp := func() error {
// Get the name of the controller-manager pod
Expand Down Expand Up @@ -135,6 +247,8 @@ var _ = Describe("controller", Ordered, func() {
EventuallyWithOffset(1, verifyControllerUp, time.Minute, time.Second).Should(Succeed())
})
// +kubebuilder:scaffold:webhookchecks
// TODO(user): Customize the e2e test suite to include
// additional scenarios specific to your project.
})
Expand Down
3 changes: 3 additions & 0 deletions pkg/plugins/golang/v4/scaffolds/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package scaffolds
import (
"fmt"

"sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang/v4/scaffolds/internal/templates/test/e2e"

log "github.com/sirupsen/logrus"
"github.com/spf13/afero"

Expand Down Expand Up @@ -86,6 +88,7 @@ func (s *webhookScaffolder) Scaffold() error {

if err := scaffold.Execute(
&api.Webhook{Force: s.force},
&e2e.WebhookTestUpdater{WireWebhook: true},
&templates.MainUpdater{WireWebhook: true},
&api.WebhookTest{Force: s.force},
); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion test/testdata/generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ function scaffold_test_project {
$kb create api --group example.com --version v1alpha1 --kind Memcached --image=memcached:memcached:1.6.26-alpine3.19 --image-container-command="memcached,-m=64,-o,modern,-v" --image-container-port="11211" --run-as-user="1001" --plugins="deploy-image/v1-alpha" --make=false
$kb create api --group example.com --version v1alpha1 --kind Busybox --image=busybox:1.36.1 --plugins="deploy-image/v1-alpha" --make=false
header_text 'Creating Memcached webhook ...'
$kb create webhook --group example.com --version v1alpha1 --kind Memcached --programmatic-validation
$kb create webhook --group example.com --version v1alpha1 --kind Memcached --defaulting --programmatic-validation
fi

if [[ $project == project-v4-with-grafana ]]; then
Expand Down
Loading

0 comments on commit 58f086f

Please sign in to comment.