Skip to content

Commit

Permalink
feat: add generated webhook code
Browse files Browse the repository at this point in the history
  • Loading branch information
knight42 committed Apr 18, 2022
1 parent 153bd07 commit 64b24d7
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 12 deletions.
12 changes: 6 additions & 6 deletions apis/kueue/v1alpha1/workload_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,11 @@ const (
WorkloadFinished WorkloadConditionType = "Finished"
)

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="Queue",JSONPath=".spec.queueName",type=string,description="Name of the queue this workload was submitted to"
//+kubebuilder:printcolumn:name="Admitted by",JSONPath=".spec.admission.clusterQueue",type=string,description="Name of the ClusterQueue that admitted this workload"
//+kubebuilder:printcolumn:name="Age",JSONPath=".metadata.creationTimestamp",type=date,description="Time this workload was created"
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Queue",JSONPath=".spec.queueName",type=string,description="Name of the queue this workload was submitted to"
// +kubebuilder:printcolumn:name="Admitted by",JSONPath=".spec.admission.clusterQueue",type=string,description="Name of the ClusterQueue that admitted this workload"
// +kubebuilder:printcolumn:name="Age",JSONPath=".metadata.creationTimestamp",type=date,description="Time this workload was created"

// Workload is the Schema for the workloads API
type Workload struct {
Expand All @@ -148,7 +148,7 @@ type Workload struct {
Status WorkloadStatus `json:"status,omitempty"`
}

//+kubebuilder:object:root=true
// +kubebuilder:object:root=true

// WorkloadList contains a list of ResourceClaim
type WorkloadList struct {
Expand Down
72 changes: 72 additions & 0 deletions apis/kueue/v1alpha1/workload_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
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 v1alpha1

import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/klog/v2"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)

const (
DefaultPodSetName = "main"
)

// log is for logging in this package.
var workloadlog = ctrl.Log.WithName("workload-webhook")

func (r *Workload) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
Complete()
}

// +kubebuilder:webhook:path=/mutate-kueue-x-k8s-io-v1alpha1-workload,mutating=true,failurePolicy=fail,sideEffects=None,groups=kueue.x-k8s.io,resources=workloads,verbs=create;update,versions=v1alpha1,name=mworkload.kb.io,admissionReviewVersions=v1

var _ webhook.Defaulter = &Workload{}

// Default implements webhook.Defaulter so a webhook will be registered for the type
func (r *Workload) Default() {
workloadlog.V(5).Info("defaulter", "workload", klog.KObj(r))

for i := range r.Spec.PodSets {
podSet := &r.Spec.PodSets[i]
if len(podSet.Name) == 0 {
podSet.Name = DefaultPodSetName
}
}
}

// +kubebuilder:webhook:path=/validate-kueue-x-k8s-io-v1alpha1-workload,mutating=false,failurePolicy=fail,sideEffects=None,groups=kueue.x-k8s.io,resources=workloads,verbs=create;update,versions=v1alpha1,name=vworkload.kb.io,admissionReviewVersions=v1

var _ webhook.Validator = &Workload{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *Workload) ValidateCreate() error {
return nil
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *Workload) ValidateUpdate(old runtime.Object) error {
return nil
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *Workload) ValidateDelete() error {
return nil
}
2 changes: 1 addition & 1 deletion apis/kueue/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ func main() {
setupLog.Error(err, "unable to create controller", "controller", "Job")
os.Exit(1)
}
if err = (&kueuev1alpha1.Workload{}).SetupWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "Workload")
os.Exit(1)
}
//+kubebuilder:scaffold:builder

if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
Expand Down
38 changes: 33 additions & 5 deletions test/integration/framework/framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ package framework

import (
"context"
"crypto/tls"
"fmt"
"net"
"time"

"github.com/onsi/ginkgo/v2"
"github.com/onsi/gomega"
Expand All @@ -33,16 +37,17 @@ import (
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/kueue/pkg/workload"

kueue "sigs.k8s.io/kueue/apis/kueue/v1alpha1"
//+kubebuilder:scaffold:imports
"sigs.k8s.io/kueue/pkg/workload"
// +kubebuilder:scaffold:imports
)

type ManagerSetup func(manager.Manager, context.Context)

type Framework struct {
CRDPath string
WebhookPath string
ManagerSetup ManagerSetup
testEnv *envtest.Environment
cancel context.CancelFunc
Expand All @@ -56,6 +61,10 @@ func (f *Framework) Setup() (context.Context, *rest.Config, client.Client) {
CRDDirectoryPaths: []string{f.CRDPath},
ErrorIfCRDPathMissing: true,
}
webhookEnabled := len(f.WebhookPath) > 0
if webhookEnabled {
f.testEnv.WebhookInstallOptions.Paths = []string{f.WebhookPath}
}

cfg, err := f.testEnv.Start()
gomega.ExpectWithOffset(1, err).NotTo(gomega.HaveOccurred())
Expand All @@ -64,16 +73,21 @@ func (f *Framework) Setup() (context.Context, *rest.Config, client.Client) {
err = kueue.AddToScheme(scheme.Scheme)
gomega.ExpectWithOffset(1, err).NotTo(gomega.HaveOccurred())

//+kubebuilder:scaffold:scheme
// +kubebuilder:scaffold:scheme

k8sClient, err := client.New(cfg, client.Options{Scheme: scheme.Scheme})
gomega.ExpectWithOffset(1, err).NotTo(gomega.HaveOccurred())
gomega.ExpectWithOffset(1, k8sClient).NotTo(gomega.BeNil())

mgr, err := ctrl.NewManager(cfg, ctrl.Options{
webhookInstallOptions := &f.testEnv.WebhookInstallOptions
mgrOpts := manager.Options{
Scheme: scheme.Scheme,
MetricsBindAddress: "0", // disable metrics to avoid conflicts between packages.
})
Host: webhookInstallOptions.LocalServingHost,
Port: webhookInstallOptions.LocalServingPort,
CertDir: webhookInstallOptions.LocalServingCertDir,
}
mgr, err := ctrl.NewManager(cfg, mgrOpts)
gomega.ExpectWithOffset(1, err).NotTo(gomega.HaveOccurred(), "failed to create manager")

ctx, cancel := context.WithCancel(context.Background())
Expand All @@ -86,6 +100,20 @@ func (f *Framework) Setup() (context.Context, *rest.Config, client.Client) {
gomega.ExpectWithOffset(1, err).NotTo(gomega.HaveOccurred(), "failed to run manager")
}()

if webhookEnabled {
// wait for the webhook server to get ready
dialer := &net.Dialer{Timeout: time.Second}
addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort)
gomega.Eventually(func() error {
conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true})
if err != nil {
return err
}
conn.Close()
return nil
}).Should(gomega.Succeed())
}

return ctx, cfg, k8sClient
}

Expand Down
63 changes: 63 additions & 0 deletions test/integration/webhook/v1alpha1/suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
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 v1alpha1

import (
"context"
"path/filepath"
"testing"

"github.com/onsi/ginkgo/v2"
"github.com/onsi/gomega"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/manager"

kueuev1alpha1 "sigs.k8s.io/kueue/apis/kueue/v1alpha1"
"sigs.k8s.io/kueue/test/integration/framework"
// +kubebuilder:scaffold:imports
)

var (
cfg *rest.Config
k8sClient client.Client
fwk *framework.Framework
ctx context.Context
)

func TestAPIs(t *testing.T) {
gomega.RegisterFailHandler(ginkgo.Fail)
ginkgo.RunSpecs(t,
"Webhook Suite",
)
}

var _ = ginkgo.BeforeSuite(func() {
fwk = &framework.Framework{
CRDPath: filepath.Join("..", "..", "..", "..", "config", "crd", "bases"),
WebhookPath: filepath.Join("..", "..", "..", "..", "config", "webhook"),
ManagerSetup: func(mgr manager.Manager, ctx context.Context) {
err := (&kueuev1alpha1.Workload{}).SetupWebhookWithManager(mgr)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
},
}
ctx, cfg, k8sClient = fwk.Setup()
})

var _ = ginkgo.AfterSuite(func() {
fwk.Teardown()
})
72 changes: 72 additions & 0 deletions test/integration/webhook/v1alpha1/workload_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
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 v1alpha1

import (
"github.com/onsi/ginkgo/v2"
"github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"

"sigs.k8s.io/kueue/apis/kueue/v1alpha1"
"sigs.k8s.io/kueue/pkg/util/testing"
"sigs.k8s.io/kueue/test/integration/framework"
)

var ns *corev1.Namespace

var _ = ginkgo.BeforeEach(func() {
ns = &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "core-",
},
}
gomega.Expect(k8sClient.Create(ctx, ns)).To(gomega.Succeed())
})

var _ = ginkgo.AfterEach(func() {
gomega.Expect(framework.DeleteNamespace(ctx, k8sClient, ns)).To(gomega.Succeed())
})

var _ = ginkgo.Describe("Workload defaulting webhook", func() {
ginkgo.Context("When creating Workload", func() {
ginkgo.It("Should set default values", func() {
ginkgo.By("Creating a new Workload")
workload := testing.MakeWorkload("workload1", ns.Name).Obj()
workload.Spec.PodSets = []v1alpha1.PodSet{
{
Count: 2,
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "test",
Image: "fake-image",
},
},
},
},
}
gomega.Expect(k8sClient.Create(ctx, workload)).Should(gomega.Succeed())

created := &v1alpha1.Workload{}
gomega.Expect(k8sClient.Get(ctx, types.NamespacedName{
Name: workload.Name,
Namespace: workload.Namespace,
}, created)).Should(gomega.Succeed())

gomega.Expect(created.Spec.PodSets[0].Name).Should(gomega.Equal(v1alpha1.DefaultPodSetName))
})
})
})

0 comments on commit 64b24d7

Please sign in to comment.