Skip to content

Commit

Permalink
⚠️ split the webhook builder out as a separate builder.
Browse files Browse the repository at this point in the history
Controller builder now only build controller; while webhook builder only build webhook.
  • Loading branch information
Mengqi Yu committed Jun 27, 2019
1 parent 71cdf35 commit d36a00e
Show file tree
Hide file tree
Showing 7 changed files with 541 additions and 367 deletions.
3 changes: 3 additions & 0 deletions alias.go
Expand Up @@ -94,6 +94,9 @@ var (
// NewControllerManagedBy returns a new controller builder that will be started by the provided Manager
NewControllerManagedBy = builder.ControllerManagedBy

// NewWebhookManagedBy returns a new webhook builder that will be started by the provided Manager
NewWebhookManagedBy = builder.WebhookManagedBy

// NewManager returns a new Manager for creating Controllers.
NewManager = manager.New

Expand Down
9 changes: 8 additions & 1 deletion examples/crd/main.go
Expand Up @@ -125,12 +125,19 @@ func main() {
Client: mgr.GetClient(),
scheme: mgr.GetScheme(),
})

if err != nil {
setupLog.Error(err, "unable to create controller")
os.Exit(1)
}

err = ctrl.NewWebhookManagedBy(mgr).
For(&api.ChaosPod{}).
Complete()
if err != nil {
setupLog.Error(err, "unable to create webhook")
os.Exit(1)
}

setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
Expand Down
94 changes: 2 additions & 92 deletions pkg/builder/build.go → pkg/builder/controller.go
Expand Up @@ -18,12 +18,9 @@ package builder

import (
"fmt"
"net/http"
"net/url"
"strings"

"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"sigs.k8s.io/controller-runtime/pkg/client/config"
Expand All @@ -33,8 +30,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"sigs.k8s.io/controller-runtime/pkg/webhook/conversion"
)

// Supporting mocking out functions for testing
Expand Down Expand Up @@ -71,8 +66,6 @@ func ControllerManagedBy(m manager.Manager) *Builder {
// update events by *reconciling the object*.
// This is the equivalent of calling
// Watches(&source.Kind{Type: apiType}, &handler.EnqueueRequestForObject{})
// If the passed in object has implemented the admission.Defaulter interface, a MutatingWebhook will be wired for this type.
// If the passed in object has implemented the admission.Validator interface, a ValidatingWebhook will be wired for this type.
//
// Deprecated: Use For
func (blder *Builder) ForType(apiType runtime.Object) *Builder {
Expand All @@ -83,8 +76,6 @@ func (blder *Builder) ForType(apiType runtime.Object) *Builder {
// update events by *reconciling the object*.
// This is the equivalent of calling
// Watches(&source.Kind{Type: apiType}, &handler.EnqueueRequestForObject{})
// If the passed in object has implemented the admission.Defaulter interface, a MutatingWebhook will be wired for this type.
// If the passed in object has implemented the admission.Validator interface, a ValidatingWebhook will be wired for this type.
func (blder *Builder) For(apiType runtime.Object) *Builder {
blder.apiType = apiType
return blder
Expand Down Expand Up @@ -159,7 +150,7 @@ func (blder *Builder) Build(r reconcile.Reconciler) (manager.Manager, error) {
}

// Set the Config
if err := blder.doConfig(); err != nil {
if err := blder.loadRestConfig(); err != nil {
return nil, err
}

Expand All @@ -173,11 +164,6 @@ func (blder *Builder) Build(r reconcile.Reconciler) (manager.Manager, error) {
return nil, err
}

// Set the Webook if needed
if err := blder.doWebhook(); err != nil {
return nil, err
}

// Set the Watch
if err := blder.doWatch(); err != nil {
return nil, err
Expand Down Expand Up @@ -217,7 +203,7 @@ func (blder *Builder) doWatch() error {
return nil
}

func (blder *Builder) doConfig() error {
func (blder *Builder) loadRestConfig() error {
if blder.config != nil {
return nil
}
Expand Down Expand Up @@ -258,79 +244,3 @@ func (blder *Builder) doController(r reconcile.Reconciler) error {
blder.ctrl, err = newController(name, blder.mgr, controller.Options{Reconciler: r})
return err
}

func (blder *Builder) doWebhook() error {
// Create a webhook for each type
gvk, err := apiutil.GVKForObject(blder.apiType, blder.mgr.GetScheme())
if err != nil {
return err
}

// TODO: When the conversion webhook lands, we need to handle all registered versions of a given group-kind.
// A potential workflow for defaulting webhook
// 1) a bespoke (non-hub) version comes in
// 2) convert it to the hub version
// 3) do defaulting
// 4) convert it back to the same bespoke version
// 5) calculate the JSON patch
//
// A potential workflow for validating webhook
// 1) a bespoke (non-hub) version comes in
// 2) convert it to the hub version
// 3) do validation
if defaulter, isDefaulter := blder.apiType.(admission.Defaulter); isDefaulter {
mwh := admission.DefaultingWebhookFor(defaulter)
if mwh != nil {
path := generateMutatePath(gvk)

// Checking if the path is already registered.
// If so, just skip it.
if !blder.isAlreadyHandled(path) {
log.Info("Registering a mutating webhook",
"GVK", gvk,
"path", path)
blder.mgr.GetWebhookServer().Register(path, mwh)
}
}
}

if validator, isValidator := blder.apiType.(admission.Validator); isValidator {
vwh := admission.ValidatingWebhookFor(validator)
if vwh != nil {
path := generateValidatePath(gvk)

// Checking if the path is already registered.
// If so, just skip it.
if !blder.isAlreadyHandled(path) {
log.Info("Registering a validating webhook",
"GVK", gvk,
"path", path)
blder.mgr.GetWebhookServer().Register(path, vwh)
}
}
}

err = conversion.CheckConvertibility(blder.mgr.GetScheme(), blder.apiType)
if err != nil {
log.Error(err, "conversion check failed", "GVK", gvk)
}
return nil
}

func (blder *Builder) isAlreadyHandled(path string) bool {
h, p := blder.mgr.GetWebhookServer().WebhookMux.Handler(&http.Request{URL: &url.URL{Path: path}})
if p == path && h != nil {
return true
}
return false
}

func generateMutatePath(gvk schema.GroupVersionKind) string {
return "/mutate-" + strings.Replace(gvk.Group, ".", "-", -1) + "-" +
gvk.Version + "-" + strings.ToLower(gvk.Kind)
}

func generateValidatePath(gvk schema.GroupVersionKind) string {
return "/validate-" + strings.Replace(gvk.Group, ".", "-", -1) + "-" +
gvk.Version + "-" + strings.ToLower(gvk.Kind)
}

0 comments on commit d36a00e

Please sign in to comment.