diff --git a/charts/kubefed/charts/controllermanager/templates/deployments.yaml b/charts/kubefed/charts/controllermanager/templates/deployments.yaml index 35ef6c2c60..5b4310d81c 100644 --- a/charts/kubefed/charts/controllermanager/templates/deployments.yaml +++ b/charts/kubefed/charts/controllermanager/templates/deployments.yaml @@ -83,9 +83,7 @@ spec: command: - "/hyperfed/webhook" - "--secure-port=8443" - - "--audit-log-path=-" - - "--tls-cert-file=/var/serving-cert/tls.crt" - - "--tls-private-key-file=/var/serving-cert/tls.key" + - "--cert-dir=/var/serving-cert/" - "--v={{ .Values.webhook.logLevel }}" ports: - containerPort: 8443 @@ -94,7 +92,7 @@ spec: name: serving-cert readinessProbe: httpGet: - path: /healthz + path: /readyz port: 8443 scheme: HTTPS resources: diff --git a/charts/kubefed/charts/controllermanager/templates/webhook.yaml b/charts/kubefed/charts/controllermanager/templates/webhook.yaml index b4e9a0f851..adebdf9e11 100644 --- a/charts/kubefed/charts/controllermanager/templates/webhook.yaml +++ b/charts/kubefed/charts/controllermanager/templates/webhook.yaml @@ -19,7 +19,7 @@ webhooks: service: namespace: {{ .Release.Namespace | quote }} name: kubefed-admission-webhook - path: /apis/validation.core.kubefed.io/v1beta1/federatedtypeconfigs + path: /validate-federatedtypeconfigs caBundle: {{ b64enc $ca.Cert | quote }} rules: - operations: @@ -49,7 +49,7 @@ webhooks: service: namespace: {{ .Release.Namespace | quote }} name: kubefed-admission-webhook - path: /apis/validation.core.kubefed.io/v1beta1/kubefedclusters + path: /validate-kubefedcluster caBundle: {{ b64enc $ca.Cert | quote }} rules: - operations: @@ -74,7 +74,7 @@ webhooks: service: namespace: {{ .Release.Namespace | quote }} name: kubefed-admission-webhook - path: /apis/validation.core.kubefed.io/v1beta1/kubefedconfigs + path: /validate-kubefedconfig caBundle: {{ b64enc $ca.Cert | quote }} rules: - operations: @@ -110,7 +110,7 @@ webhooks: service: namespace: {{ .Release.Namespace | quote }} name: kubefed-admission-webhook - path: /apis/mutation.core.kubefed.io/v1beta1/kubefedconfigs + path: /default-kubefedconfig caBundle: {{ b64enc $ca.Cert | quote }} rules: - operations: diff --git a/cmd/hyperfed/main.go b/cmd/hyperfed/main.go index b92d5ef6e1..2849de2eb1 100644 --- a/cmd/hyperfed/main.go +++ b/cmd/hyperfed/main.go @@ -37,9 +37,9 @@ import ( utilflag "k8s.io/component-base/cli/flag" "k8s.io/component-base/logs" - "sigs.k8s.io/kubefed/cmd/controller-manager/app" + ctrlapp "sigs.k8s.io/kubefed/cmd/controller-manager/app" + webhookapp "sigs.k8s.io/kubefed/cmd/webhook/app" "sigs.k8s.io/kubefed/pkg/kubefedctl" - "sigs.k8s.io/kubefed/pkg/webhook" ) func main() { @@ -80,9 +80,9 @@ func commandFor(basename string, defaultCommand *cobra.Command, commands []func( func NewHyperFedCommand() (*cobra.Command, []func() *cobra.Command) { stopChan := genericapiserver.SetupSignalHandler() - controller := func() *cobra.Command { return app.NewControllerManagerCommand(stopChan) } + controller := func() *cobra.Command { return ctrlapp.NewControllerManagerCommand(stopChan) } kubefedctlCmd := func() *cobra.Command { return kubefedctl.NewKubeFedCtlCommand(os.Stdout) } - webhookCmd := func() *cobra.Command { return webhook.NewWebhookCommand(stopChan) } + webhookCmd := func() *cobra.Command { return webhookapp.NewWebhookCommand(stopChan) } commandFns := []func() *cobra.Command{ controller, diff --git a/cmd/webhook/app/webhook.go b/cmd/webhook/app/webhook.go new file mode 100644 index 0000000000..065d0fb9fd --- /dev/null +++ b/cmd/webhook/app/webhook.go @@ -0,0 +1,92 @@ +package app + +import ( + "flag" + "fmt" + "net/http" + "os" + + "github.com/spf13/cobra" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/component-base/logs" + "k8s.io/klog" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/manager/signals" + ctrwebhook "sigs.k8s.io/controller-runtime/pkg/webhook" + + "sigs.k8s.io/kubefed/pkg/controller/webhook/federatedtypeconfig" + "sigs.k8s.io/kubefed/pkg/controller/webhook/kubefedcluster" + "sigs.k8s.io/kubefed/pkg/controller/webhook/kubefedconfig" + "sigs.k8s.io/kubefed/pkg/version" +) + +var ( + certDir, kubeconfig, masterURL string + port = 8443 +) + +// NewWebhookCommand creates a *cobra.Command object with default parameters +func NewWebhookCommand(stopChan <-chan struct{}) *cobra.Command { + verFlag := false + + cmd := &cobra.Command{ + Use: "webhook", + Short: "Start a kubefed webhook server", + Long: "Start a kubefed webhook server", + Run: func(cmd *cobra.Command, args []string) { + fmt.Fprintf(os.Stdout, "KubeFed webhook version: %s\n", fmt.Sprintf("%#v", version.Get())) + if verFlag { + os.Exit(0) + } + // PrintFlags(cmd.Flags()) + + if err := Run(stopChan); err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + os.Exit(1) + } + }, + } + + // Add the command line flags from other dependencies(klog, kubebuilder, etc.) + cmd.Flags().AddGoFlagSet(flag.CommandLine) + + cmd.Flags().StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") + cmd.Flags().StringVar(&masterURL, "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.") + cmd.Flags().StringVar(&certDir, "cert-dir", "", "The directory where the TLS certs are located.") + cmd.Flags().IntVar(&port, "secure-port", port, "The port on which to serve HTTPS.") + + return cmd +} + +// Run runs the webhook with options. This should never exit. +func Run(stopChan <-chan struct{}) error { + logs.InitLogs() + defer logs.FlushLogs() + + config, err := clientcmd.BuildConfigFromFlags(masterURL, kubeconfig) + if err != nil { + klog.Fatalf("error setting up webhook's config: %s", err) + } + mgr, err := manager.New(config, manager.Options{ + Port: port, + CertDir: certDir, + }) + if err != nil { + klog.Fatalf("error setting up webhook manager: %s", err) + } + hookServer := mgr.GetWebhookServer() + + hookServer.Register("/validate-federatedtypeconfigs", &ctrwebhook.Admission{Handler: &federatedtypeconfig.FederatedTypeConfigAdmissionHook{}}) + hookServer.Register("/validate-kubefedcluster", &ctrwebhook.Admission{Handler: &kubefedcluster.KubeFedClusterAdmissionHook{}}) + hookServer.Register("/validate-kubefedconfig", &ctrwebhook.Admission{Handler: &kubefedconfig.KubeFedConfigValidator{}}) + hookServer.Register("/default-kubefedconfig", &ctrwebhook.Admission{Handler: &kubefedconfig.KubeFedConfigDefaulter{}}) + + hookServer.WebhookMux.Handle("/readyz/", http.StripPrefix("/readyz/", &healthz.Handler{})) + + if err := mgr.Start(signals.SetupSignalHandler()); err != nil { + klog.Fatalf("unable to run manager: %s", err) + } + + return nil +} diff --git a/cmd/webhook/main.go b/cmd/webhook/main.go index 93bcc51990..93db56073e 100644 --- a/cmd/webhook/main.go +++ b/cmd/webhook/main.go @@ -17,25 +17,21 @@ limitations under the License. package main import ( - "flag" + "fmt" + "os" - genericapiserver "k8s.io/apiserver/pkg/server" "k8s.io/component-base/logs" - "k8s.io/klog" + "sigs.k8s.io/controller-runtime/pkg/manager/signals" - "sigs.k8s.io/kubefed/pkg/webhook" + "sigs.k8s.io/kubefed/cmd/webhook/app" ) func main() { logs.InitLogs() defer logs.FlushLogs() - stopChan := genericapiserver.SetupSignalHandler() - - cmd := webhook.NewWebhookCommand(stopChan) - cmd.Flags().AddGoFlagSet(flag.CommandLine) - - if err := cmd.Execute(); err != nil { - klog.Fatal(err) + if err := app.NewWebhookCommand(signals.SetupSignalHandler()); err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + os.Exit(1) } } diff --git a/go.mod b/go.mod index 2d7ccf806d..aa5a27aa24 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/json-iterator/go v1.1.9 github.com/onsi/ginkgo v1.13.0 github.com/onsi/gomega v1.10.1 - github.com/openshift/generic-admission-server v1.14.0 github.com/pborman/uuid v1.2.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.0.0 diff --git a/go.sum b/go.sum index fcaf62b0b8..c3f7f4eabd 100644 --- a/go.sum +++ b/go.sum @@ -87,6 +87,7 @@ github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -283,8 +284,6 @@ github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/openshift/generic-admission-server v1.14.0 h1:GAQy5JNVcbmUuIpPvLd39+2rPecxEm7WQ2sP7ACrse4= -github.com/openshift/generic-admission-server v1.14.0/go.mod h1:GD9KN/W4KxqRQGVMbqQHpHzb2XcQVvLCaBaSciqXvfM= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -488,7 +487,6 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= diff --git a/pkg/controller/webhook/federatedtypeconfig/webhook.go b/pkg/controller/webhook/federatedtypeconfig/webhook.go index d702194564..0bdf869f30 100644 --- a/pkg/controller/webhook/federatedtypeconfig/webhook.go +++ b/pkg/controller/webhook/federatedtypeconfig/webhook.go @@ -17,16 +17,11 @@ limitations under the License. package federatedtypeconfig import ( - "strings" - "sync" + "context" - "github.com/openshift/generic-admission-server/pkg/apiserver" - admissionv1beta1 "k8s.io/api/admission/v1beta1" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/rest" "k8s.io/klog" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1" "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1/validation" @@ -38,52 +33,34 @@ const ( resourcePluralName = "federatedtypeconfigs" ) -type FederatedTypeConfigAdmissionHook struct { - client dynamic.ResourceInterface +type FederatedTypeConfigAdmissionHook struct{} - lock sync.RWMutex - initialized bool -} - -var _ apiserver.ValidatingAdmissionHook = &FederatedTypeConfigAdmissionHook{} +var _ admission.Handler = &FederatedTypeConfigAdmissionHook{} -func (a *FederatedTypeConfigAdmissionHook) ValidatingResource() (plural schema.GroupVersionResource, singular string) { - klog.Infof("New ValidatingResource for %q", ResourceName) - return webhook.NewValidatingResource(resourcePluralName), strings.ToLower(ResourceName) -} - -func (a *FederatedTypeConfigAdmissionHook) Validate(admissionSpec *admissionv1beta1.AdmissionRequest) *admissionv1beta1.AdmissionResponse { - status := &admissionv1beta1.AdmissionResponse{} +func (a *FederatedTypeConfigAdmissionHook) Handle(ctx context.Context, admissionSpec admission.Request) admission.Response { + status := admission.Response{} klog.V(4).Infof("Validating %q AdmissionRequest = %s", ResourceName, webhook.AdmissionRequestDebugString(admissionSpec)) // We want to let through: // - Requests that are not for create, update // - Requests for things that are not FederatedTypeConfigs - if webhook.Allowed(admissionSpec, resourcePluralName, status) { + if webhook.Allowed(admissionSpec, resourcePluralName, &status) { return status } admittingObject := &v1beta1.FederatedTypeConfig{} - err := webhook.Unmarshal(&admissionSpec.Object, admittingObject, status) + err := webhook.Unmarshal(&admissionSpec.Object, admittingObject, &status) if err != nil { return status } - if !webhook.Initialized(&a.initialized, &a.lock, status) { - return status - } - klog.V(4).Infof("Validating %q = %+v", ResourceName, *admittingObject) isStatusSubResource := len(admissionSpec.SubResource) != 0 - webhook.Validate(status, func() field.ErrorList { + webhook.Validate(&status, func() field.ErrorList { return validation.ValidateFederatedTypeConfig(admittingObject, isStatusSubResource) }) return status } - -func (a *FederatedTypeConfigAdmissionHook) Initialize(kubeClientConfig *rest.Config, stopCh <-chan struct{}) error { - return webhook.Initialize(kubeClientConfig, &a.client, &a.lock, &a.initialized, ResourceName) -} diff --git a/pkg/controller/webhook/kubefedcluster/webhook.go b/pkg/controller/webhook/kubefedcluster/webhook.go index a76a76eb64..63a3795ed1 100644 --- a/pkg/controller/webhook/kubefedcluster/webhook.go +++ b/pkg/controller/webhook/kubefedcluster/webhook.go @@ -17,16 +17,11 @@ limitations under the License. package kubefedcluster import ( - "strings" - "sync" + "context" - "github.com/openshift/generic-admission-server/pkg/apiserver" - admissionv1beta1 "k8s.io/api/admission/v1beta1" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/rest" "k8s.io/klog" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1" "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1/validation" @@ -38,52 +33,34 @@ const ( resourcePluralName = "kubefedclusters" ) -type KubeFedClusterAdmissionHook struct { - client dynamic.ResourceInterface +type KubeFedClusterAdmissionHook struct{} - lock sync.RWMutex - initialized bool -} - -var _ apiserver.ValidatingAdmissionHook = &KubeFedClusterAdmissionHook{} +var _ admission.Handler = &KubeFedClusterAdmissionHook{} -func (a *KubeFedClusterAdmissionHook) ValidatingResource() (plural schema.GroupVersionResource, singular string) { - klog.Infof("New ValidatingResource for %q", ResourceName) - return webhook.NewValidatingResource(resourcePluralName), strings.ToLower(ResourceName) -} - -func (a *KubeFedClusterAdmissionHook) Validate(admissionSpec *admissionv1beta1.AdmissionRequest) *admissionv1beta1.AdmissionResponse { - status := &admissionv1beta1.AdmissionResponse{} +func (a *KubeFedClusterAdmissionHook) Handle(ctx context.Context, admissionSpec admission.Request) admission.Response { + status := admission.Response{} klog.V(4).Infof("Validating %q AdmissionRequest = %s", ResourceName, webhook.AdmissionRequestDebugString(admissionSpec)) // We want to let through: // - Requests that are not for create, update // - Requests for things that are not FederatedTypeConfigs - if webhook.Allowed(admissionSpec, resourcePluralName, status) { + if webhook.Allowed(admissionSpec, resourcePluralName, &status) { return status } admittingObject := &v1beta1.KubeFedCluster{} - err := webhook.Unmarshal(&admissionSpec.Object, admittingObject, status) + err := webhook.Unmarshal(&admissionSpec.Object, admittingObject, &status) if err != nil { return status } - if !webhook.Initialized(&a.initialized, &a.lock, status) { - return status - } - klog.V(4).Infof("Validating %q = %+v", ResourceName, *admittingObject) isStatusSubResource := admissionSpec.SubResource == "status" - webhook.Validate(status, func() field.ErrorList { + webhook.Validate(&status, func() field.ErrorList { return validation.ValidateKubeFedCluster(admittingObject, isStatusSubResource) }) return status } - -func (a *KubeFedClusterAdmissionHook) Initialize(kubeClientConfig *rest.Config, stopCh <-chan struct{}) error { - return webhook.Initialize(kubeClientConfig, &a.client, &a.lock, &a.initialized, ResourceName) -} diff --git a/pkg/controller/webhook/kubefedconfig/webhook.go b/pkg/controller/webhook/kubefedconfig/webhook.go index d0d5da0bf9..7b6f0f6905 100644 --- a/pkg/controller/webhook/kubefedconfig/webhook.go +++ b/pkg/controller/webhook/kubefedconfig/webhook.go @@ -17,21 +17,17 @@ limitations under the License. package kubefedconfig import ( + "context" "encoding/json" "fmt" "net/http" "reflect" - "strings" - "sync" - "github.com/openshift/generic-admission-server/pkg/apiserver" admissionv1beta1 "k8s.io/api/admission/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/rest" "k8s.io/klog" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1" "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1/defaults" @@ -44,31 +40,21 @@ const ( resourcePluralName = "kubefedconfigs" ) -type KubeFedConfigAdmissionHook struct { - client dynamic.ResourceInterface +type KubeFedConfigValidator struct{} - lock sync.RWMutex - initialized bool -} - -var _ apiserver.ValidatingAdmissionHook = &KubeFedConfigAdmissionHook{} - -func (a *KubeFedConfigAdmissionHook) ValidatingResource() (plural schema.GroupVersionResource, singular string) { - klog.Infof("New ValidatingResource for %q", ResourceName) - return webhook.NewValidatingResource(resourcePluralName), strings.ToLower(ResourceName) -} +var _ admission.Handler = &KubeFedConfigValidator{} -func (a *KubeFedConfigAdmissionHook) Validate(admissionSpec *admissionv1beta1.AdmissionRequest) *admissionv1beta1.AdmissionResponse { - status := &admissionv1beta1.AdmissionResponse{} +func (a *KubeFedConfigValidator) Handle(ctx context.Context, admissionSpec admission.Request) admission.Response { + status := admission.Response{} klog.V(4).Infof("Validating %q AdmissionRequest = %s", ResourceName, webhook.AdmissionRequestDebugString(admissionSpec)) - if webhook.Allowed(admissionSpec, resourcePluralName, status) { + if webhook.Allowed(admissionSpec, resourcePluralName, &status) { return status } admittingObject := &v1beta1.KubeFedConfig{} - err := webhook.Unmarshal(&admissionSpec.Object, admittingObject, status) + err := webhook.Unmarshal(&admissionSpec.Object, admittingObject, &status) if err != nil { return status } @@ -76,48 +62,37 @@ func (a *KubeFedConfigAdmissionHook) Validate(admissionSpec *admissionv1beta1.Ad var oldObject *v1beta1.KubeFedConfig if admissionSpec.Operation == admissionv1beta1.Update { oldObject = &v1beta1.KubeFedConfig{} - err = webhook.Unmarshal(&admissionSpec.OldObject, oldObject, status) + err = webhook.Unmarshal(&admissionSpec.OldObject, oldObject, &status) if err != nil { return status } } - if !webhook.Initialized(&a.initialized, &a.lock, status) { - return status - } - klog.V(4).Infof("Validating %q = %+v", ResourceName, *admittingObject) - webhook.Validate(status, func() field.ErrorList { + webhook.Validate(&status, func() field.ErrorList { return validation.ValidateKubeFedConfig(admittingObject, oldObject) }) return status } -var _ apiserver.MutatingAdmissionHook = &KubeFedConfigAdmissionHook{} +type KubeFedConfigDefaulter struct{} -func (a *KubeFedConfigAdmissionHook) MutatingResource() (plural schema.GroupVersionResource, singular string) { - klog.Infof("New MutatingResource for %q", ResourceName) - return webhook.NewMutatingResource(resourcePluralName), strings.ToLower(ResourceName) -} +var _ admission.Handler = &KubeFedConfigDefaulter{} -func (a *KubeFedConfigAdmissionHook) Admit(admissionSpec *admissionv1beta1.AdmissionRequest) *admissionv1beta1.AdmissionResponse { - status := &admissionv1beta1.AdmissionResponse{} +func (a *KubeFedConfigDefaulter) Handle(ctx context.Context, admissionSpec admission.Request) admission.Response { + status := admission.Response{} klog.V(4).Infof("Admitting %q AdmissionRequest = %s", ResourceName, webhook.AdmissionRequestDebugString(admissionSpec)) admittingObject := &v1beta1.KubeFedConfig{} - err := webhook.Unmarshal(&admissionSpec.Object, admittingObject, status) + err := webhook.Unmarshal(&admissionSpec.Object, admittingObject, &status) if err != nil { return status } klog.V(4).Infof("Admitting %q = %+v", ResourceName, *admittingObject) - if !webhook.Initialized(&a.initialized, &a.lock, status) { - return status - } - defaultedObject := admittingObject.DeepCopyObject().(*v1beta1.KubeFedConfig) defaults.SetDefaultKubeFedConfig(defaultedObject) @@ -154,10 +129,6 @@ func (a *KubeFedConfigAdmissionHook) Admit(admissionSpec *admissionv1beta1.Admis return status } -func (a *KubeFedConfigAdmissionHook) Initialize(kubeClientConfig *rest.Config, stopCh <-chan struct{}) error { - return webhook.Initialize(kubeClientConfig, &a.client, &a.lock, &a.initialized, ResourceName) -} - type patchOperation struct { Op string `json:"op"` Path string `json:"path"` diff --git a/pkg/controller/webhook/util.go b/pkg/controller/webhook/util.go index 8dbdf78cd6..55f5a4f451 100644 --- a/pkg/controller/webhook/util.go +++ b/pkg/controller/webhook/util.go @@ -20,16 +20,14 @@ import ( "encoding/json" "fmt" "net/http" - "sync" + + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" admissionv1beta1 "k8s.io/api/admission/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/rest" - "k8s.io/klog" "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1" ) @@ -58,7 +56,7 @@ func NewValidatingResource(resourcePluralName string) schema.GroupVersionResourc // Allowed returns true if the admission request for the plural name of the // resource passed in should be allowed to pass through, false otherwise. -func Allowed(a *admissionv1beta1.AdmissionRequest, pluralResourceName string, status *admissionv1beta1.AdmissionResponse) bool { +func Allowed(a admission.Request, pluralResourceName string, status *admission.Response) bool { // We want to let through: // - Requests that are not for create, update // - Requests for things that are not @@ -71,7 +69,7 @@ func Allowed(a *admissionv1beta1.AdmissionRequest, pluralResourceName string, st return false } -func Unmarshal(rawExt *runtime.RawExtension, object interface{}, status *admissionv1beta1.AdmissionResponse) error { +func Unmarshal(rawExt *runtime.RawExtension, object interface{}, status *admission.Response) error { err := json.Unmarshal(rawExt.Raw, object) if err != nil { status.Allowed = false @@ -84,22 +82,7 @@ func Unmarshal(rawExt *runtime.RawExtension, object interface{}, status *admissi return err } -func Initialized(initialized *bool, lock *sync.RWMutex, status *admissionv1beta1.AdmissionResponse) bool { - lock.RLock() - defer lock.RUnlock() - - if !*initialized { - status.Allowed = false - status.Result = &metav1.Status{ - Status: metav1.StatusFailure, Code: http.StatusInternalServerError, Reason: metav1.StatusReasonInternalError, - Message: "not initialized", - } - } - - return *initialized -} - -func Validate(status *admissionv1beta1.AdmissionResponse, validateFn func() field.ErrorList) { +func Validate(status *admission.Response, validateFn func() field.ErrorList) { errs := validateFn() if len(errs) != 0 { status.Allowed = false @@ -112,35 +95,7 @@ func Validate(status *admissionv1beta1.AdmissionResponse, validateFn func() fiel } } -func Initialize(kubeClientConfig *rest.Config, client *dynamic.ResourceInterface, lock *sync.RWMutex, initialized *bool, resourceName string) error { - lock.Lock() - defer lock.Unlock() - - *initialized = true - - shallowClientConfigCopy := *kubeClientConfig - shallowClientConfigCopy.GroupVersion = &schema.GroupVersion{ - Group: v1beta1.SchemeGroupVersion.Group, - Version: v1beta1.SchemeGroupVersion.Version, - } - - shallowClientConfigCopy.APIPath = "/apis" - dynamicClient, err := dynamic.NewForConfig(&shallowClientConfigCopy) - if err != nil { - return err - } - - *client = dynamicClient.Resource(schema.GroupVersionResource{ - Group: v1beta1.SchemeGroupVersion.Group, - Version: v1beta1.SchemeGroupVersion.Version, - Resource: resourceName, - }) - - klog.Infof("Initialized admission webhook for %q", resourceName) - return nil -} - -func AdmissionRequestDebugString(a *admissionv1beta1.AdmissionRequest) string { +func AdmissionRequestDebugString(a admission.Request) string { return fmt.Sprintf("UID=%v Kind={%v} Resource=%+v SubResource=%v Name=%v Namespace=%v Operation=%v UserInfo=%+v DryRun=%v", a.UID, a.Kind, a.Resource, a.SubResource, a.Name, a.Namespace, a.Operation, a.UserInfo, *a.DryRun) } diff --git a/pkg/webhook/webhook.go b/pkg/webhook/webhook.go deleted file mode 100644 index c10c7011c5..0000000000 --- a/pkg/webhook/webhook.go +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright 2019 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 webhook - -import ( - "fmt" - "os" - - "github.com/openshift/generic-admission-server/pkg/apiserver" - "github.com/openshift/generic-admission-server/pkg/cmd/server" - "github.com/spf13/cobra" - - "sigs.k8s.io/kubefed/pkg/controller/webhook/federatedtypeconfig" - "sigs.k8s.io/kubefed/pkg/controller/webhook/kubefedcluster" - "sigs.k8s.io/kubefed/pkg/controller/webhook/kubefedconfig" - "sigs.k8s.io/kubefed/pkg/version" -) - -func NewWebhookCommand(stopChan <-chan struct{}) *cobra.Command { - admissionHooks := []apiserver.AdmissionHook{ - &federatedtypeconfig.FederatedTypeConfigAdmissionHook{}, - &kubefedcluster.KubeFedClusterAdmissionHook{}, - &kubefedconfig.KubeFedConfigAdmissionHook{}, - } - - cmd := server.NewCommandStartAdmissionServer(os.Stdout, os.Stderr, stopChan, admissionHooks...) - cmd.Use = "webhook" - cmd.Short = "Start a kubefed webhook server" - cmd.Long = "Start a kubefed webhook server" - - versionFlag := false - cmd.Flags().BoolVar(&versionFlag, "version", false, - "Prints version information for kubefed admission webhook and quits") - cmd.PreRun = func(c *cobra.Command, args []string) { - fmt.Fprintf(os.Stdout, "KubeFed admission webhook version: %s\n", - fmt.Sprintf("%#v", version.Get())) - if versionFlag { - os.Exit(0) - } - } - - return cmd -}