Skip to content
This repository has been archived by the owner on Apr 25, 2023. It is now read-only.

Commit

Permalink
feat: migrate off of openshift/generic-admission-server
Browse files Browse the repository at this point in the history
That library os not actively maintained, anymore, and (worse) blocks
updating k8s dependencies to v0.18 and beyond because of breaking
changes to client-go (context.Context pass to most of the public
functions now).

Therefore, I refactored the webhook to rely on controller-runtime.
This also simplifies much of the webhook's code.
  • Loading branch information
Max Jonas Werner committed Aug 5, 2020
1 parent 32dba94 commit 98ff2a1
Show file tree
Hide file tree
Showing 12 changed files with 166 additions and 244 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -94,7 +92,7 @@ spec:
name: serving-cert
readinessProbe:
httpGet:
path: /healthz
path: /readyz
port: 8443
scheme: HTTPS
resources:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
8 changes: 4 additions & 4 deletions cmd/hyperfed/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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,
Expand Down
105 changes: 105 additions & 0 deletions cmd/webhook/app/webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package app

import (
"flag"
"fmt"
"net/http"
"os"

"sigs.k8s.io/controller-runtime/pkg/healthz"

"k8s.io/client-go/tools/clientcmd"

"github.com/spf13/cobra"
"k8s.io/component-base/logs"
"k8s.io/klog"
"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"
)

const (
defaultPort = 443
)

var (
certDir, kubeconfig, masterURL string
port int
)

// 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", defaultPort, "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{}})

if err != nil {
klog.Fatalf("error getting clientset: %s", err)
}

hookServer.WebhookMux.Handle("/readyz", http.StripPrefix("/readyz", &healthz.Handler{
Checks: map[string]healthz.Checker{
"ping": healthz.Ping,
},
}))

if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
klog.Fatalf("unable to run manager: %s", err)
}

return nil
}
20 changes: 8 additions & 12 deletions cmd/webhook/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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/cmd/webhook/app"

"sigs.k8s.io/kubefed/pkg/webhook"
"k8s.io/component-base/logs"
)

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)
}
}
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 1 addition & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down
42 changes: 10 additions & 32 deletions pkg/controller/webhook/federatedtypeconfig/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,11 @@ limitations under the License.
package federatedtypeconfig

import (
"strings"
"sync"
"context"

"sigs.k8s.io/controller-runtime/pkg/webhook/admission"

"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/kubefed/pkg/apis/core/v1beta1"
Expand All @@ -38,52 +34,34 @@ const (
resourcePluralName = "federatedtypeconfigs"
)

type FederatedTypeConfigAdmissionHook struct {
client dynamic.ResourceInterface

lock sync.RWMutex
initialized bool
}
type FederatedTypeConfigAdmissionHook struct{}

var _ apiserver.ValidatingAdmissionHook = &FederatedTypeConfigAdmissionHook{}

func (a *FederatedTypeConfigAdmissionHook) ValidatingResource() (plural schema.GroupVersionResource, singular string) {
klog.Infof("New ValidatingResource for %q", ResourceName)
return webhook.NewValidatingResource(resourcePluralName), strings.ToLower(ResourceName)
}
var _ admission.Handler = &FederatedTypeConfigAdmissionHook{}

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)
}
Loading

0 comments on commit 98ff2a1

Please sign in to comment.