diff --git a/cmd/config.go b/cmd/config.go index 08d720c..6e35c35 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -10,13 +10,14 @@ import ( type CmdConfig struct { Debug bool Development bool + keepDNS bool WebhookListenAddr string MetricsListenAddr string MetricsPath string TLSCertFilePath string TLSKeyFilePath string gateway string - keepDNS bool + keepGatewayLabel string } // NewCmdConfig returns a new command configuration. @@ -32,6 +33,7 @@ func NewCmdConfig() (*CmdConfig, error) { app.Flag("tls-key-file-path", "the path for the webhook HTTPS server TLS key file.").StringVar(&c.TLSKeyFilePath) app.Flag("gateway", "name/IP of gateway pod").StringVar(&c.gateway) app.Flag("keepDNS", "do not modify pod DNS").BoolVar(&c.keepDNS) + app.Flag("keepGatewayLabel", "do not mutate pods with this label set to 'true'").StringVar(&c.keepGatewayLabel) _, err := app.Parse(os.Args[1:]) if err != nil { diff --git a/cmd/main.go b/cmd/main.go index 89d7b7d..cb0ee04 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -36,7 +36,7 @@ func runApp() error { // Set up logger. logrusLog := logrus.New() - logrusLogEntry := logrus.NewEntry(logrusLog).WithField("app", "k8s-webhook-example") + logrusLogEntry := logrus.NewEntry(logrusLog).WithField("app", "gateway-admision-controller") if cfg.Debug { logrusLogEntry.Logger.SetLevel(logrus.DebugLevel) } @@ -76,9 +76,10 @@ func runApp() error { // Webhook handler. wh, err := webhook.New(webhook.Config{ - Gateway: cfg.gateway, - KeepDNS: cfg.keepDNS, - Logger: logger, + Gateway: cfg.gateway, + KeepDNS: cfg.keepDNS, + KeepGatewayLabel: cfg.keepGatewayLabel, + Logger: logger, }) if err != nil { return fmt.Errorf("could not create webhooks handler: %w", err) diff --git a/internal/http/webhook/handler.go b/internal/http/webhook/handler.go index c384b0d..ac33655 100644 --- a/internal/http/webhook/handler.go +++ b/internal/http/webhook/handler.go @@ -31,7 +31,7 @@ func (l kubewebhookLogger) SetValuesOnCtx(parent context.Context, values map[str // allmark sets up the webhook handler for marking all kubernetes resources using Kubewebhook library. func (h handler) gatewayPodMutator() (http.Handler, error) { // Create our mutator - gwPodMutator, err := gatewayPodMutator.NewGatewayPodMutator(h.gateway, h.keepDNS) + gwPodMutator, err := gatewayPodMutator.NewGatewayPodMutator(h.gateway, h.keepGatewayLabel, h.keepDNS) if err != nil { return nil, fmt.Errorf("error creating webhook mutator: %w", err) } diff --git a/internal/http/webhook/routes.go b/internal/http/webhook/routes.go index 3274fed..d967ca3 100644 --- a/internal/http/webhook/routes.go +++ b/internal/http/webhook/routes.go @@ -1,16 +1,25 @@ package webhook import ( + "encoding/json" "net/http" ) // routes wires the routes to handlers on a specific router. func (h handler) routes(router *http.ServeMux) error { + + // Add gatewayPodMutator gatewayPodMutator, err := h.gatewayPodMutator() if err != nil { return err } - router.Handle("/wh/mutating/gatewayPodMutator", gatewayPodMutator) + router.Handle("/wh/mutating/setgateway", gatewayPodMutator) + + //Add health + router.HandleFunc("/wh/health", func(w http.ResponseWriter, r *http.Request) { + // an example API handler + json.NewEncoder(w).Encode(map[string]bool{"ok": true}) + }) return nil } diff --git a/internal/http/webhook/webhook.go b/internal/http/webhook/webhook.go index 72062b7..9472348 100644 --- a/internal/http/webhook/webhook.go +++ b/internal/http/webhook/webhook.go @@ -9,9 +9,10 @@ import ( // Config is the handler configuration. type Config struct { - Gateway string - KeepDNS bool - Logger log.Logger + Gateway string + KeepGatewayLabel string + KeepDNS bool + Logger log.Logger } func (c *Config) defaults() error { @@ -27,10 +28,11 @@ func (c *Config) defaults() error { } type handler struct { - gateway string - keepDNS bool - handler http.Handler - logger log.Logger + gateway string + keepGatewayLabel string + keepDNS bool + handler http.Handler + logger log.Logger } // New returns a new webhook handler. @@ -43,10 +45,11 @@ func New(config Config) (http.Handler, error) { mux := http.NewServeMux() h := handler{ - handler: mux, - gateway: config.Gateway, - keepDNS: config.KeepDNS, - logger: config.Logger.WithKV(log.KV{"service": "webhook-handler"}), + handler: mux, + gateway: config.Gateway, + keepGatewayLabel: config.KeepGatewayLabel, + keepDNS: config.KeepDNS, + logger: config.Logger.WithKV(log.KV{"service": "webhook-handler"}), } // Register all the routes with our router. diff --git a/internal/mutation/gatewayPodMutator.go b/internal/mutation/gatewayPodMutator.go index a9d7e72..729eee2 100644 --- a/internal/mutation/gatewayPodMutator.go +++ b/internal/mutation/gatewayPodMutator.go @@ -3,6 +3,7 @@ package gatewayPodMutator import ( "context" "net" + "strconv" "strings" corev1 "k8s.io/api/core/v1" @@ -17,17 +18,22 @@ type GatewayPodMutator interface { } // NewLabelMarker returns a new marker that will mark with labels. -func NewGatewayPodMutator(gateway string, keepDNS bool) (GatewayPodMutator, error) { +func NewGatewayPodMutator(gateway string, keepGatewayLabel string, keepDNS bool) (GatewayPodMutator, error) { gatewayIPs, error := net.LookupIP(gateway) if error != nil { return nil, error } - return gatewayPodMutatorCfg{gatewayIPs: gatewayIPs, keepDNS: keepDNS}, nil + return gatewayPodMutatorCfg{ + gatewayIPs: gatewayIPs, + keepGatewayLabel: keepGatewayLabel, + keepDNS: keepDNS, + }, nil } type gatewayPodMutatorCfg struct { - gatewayIPs []net.IP - keepDNS bool + gatewayIPs []net.IP + keepGatewayLabel string + keepDNS bool } func (cfg gatewayPodMutatorCfg) GatewayPodMutator(_ context.Context, _ *kwhmodel.AdmissionReview, obj metav1.Object) (*kwhmutating.MutatorResult, error) { @@ -37,9 +43,17 @@ func (cfg gatewayPodMutatorCfg) GatewayPodMutator(_ context.Context, _ *kwhmodel return &kwhmutating.MutatorResult{}, nil } + if val, ok := pod.GetLabels()[cfg.keepGatewayLabel]; cfg.keepGatewayLabel != "" && ok { + if val, err := strconv.ParseBool(val); err == nil && val { + return &kwhmutating.MutatorResult{ + MutatedObject: pod, + }, nil + } + } + // Create init container container := corev1.Container{ - Name: "addGateway", + Name: "add-gateway", Image: "alpine", Command: append( strings.Split("ip route add default via", " "), diff --git a/internal/mutation/gatewayPodMutator_test.go b/internal/mutation/gatewayPodMutator_test.go index 0e099c2..8d50f28 100644 --- a/internal/mutation/gatewayPodMutator_test.go +++ b/internal/mutation/gatewayPodMutator_test.go @@ -14,36 +14,43 @@ import ( mutator "github.com/k8s-at-home/gateway-admision-controller/internal/mutation" ) -func getExpectedObj(gatewayIP string, keepDNS bool) metav1.Object { - pod := corev1.Pod{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{}, - Spec: corev1.PodSpec{ - InitContainers: []corev1.Container{ - corev1.Container{ - Name: "addGateway", - Image: "alpine", - Command: append( - strings.Split("ip route add default via", " "), - gatewayIP, - ), - SecurityContext: &corev1.SecurityContext{ - Privileged: &[]bool{true}[0], - }, +func getExpectedPodSpec_all( + gatewayIP string, + keepDNS bool, +) corev1.PodSpec { + spec := corev1.PodSpec{ + InitContainers: []corev1.Container{ + corev1.Container{ + Name: "add-gateway", + Image: "alpine", + Command: append( + strings.Split("ip route add default via", " "), + gatewayIP, + ), + SecurityContext: &corev1.SecurityContext{ + Privileged: &[]bool{true}[0], }, }, }, } if !keepDNS { - pod.Spec.DNSPolicy = "None" - pod.Spec.DNSConfig = &corev1.PodDNSConfig{ + spec.DNSPolicy = "None" + spec.DNSConfig = &corev1.PodDNSConfig{ Nameservers: []string{ gatewayIP, }, } } - return &pod + return spec +} + +func getExpectedPodSpec(gatewayIP string) corev1.PodSpec { + return getExpectedPodSpec_all(gatewayIP, false) +} + +func getExpectedPodSpec_keepDNS(gatewayIP string) corev1.PodSpec { + return getExpectedPodSpec_all(gatewayIP, true) } func TestGatewayPodMutator(t *testing.T) { @@ -52,28 +59,70 @@ func TestGatewayPodMutator(t *testing.T) { exampleGatewayNameIPs, _ := net.LookupIP(exampleGatewayName) tests := map[string]struct { - gateway string - keepDNS bool - obj metav1.Object - expObj metav1.Object + gateway string + keepGatewayLabel string + keepDNS bool + obj metav1.Object + expObj metav1.Object }{ - "Gateway IP, keepDNS=false - Having a pod, gateway should be added": { + "Gateway IP - Having a pod, gateway should be added": { gateway: "1.2.3.4", - keepDNS: false, obj: &corev1.Pod{}, - expObj: getExpectedObj("1.2.3.4", false), + expObj: &corev1.Pod{ + Spec: getExpectedPodSpec("1.2.3.4"), + }, + }, + "Gateway name - Having a pod, gateway should be added": { + gateway: exampleGatewayName, + obj: &corev1.Pod{}, + expObj: &corev1.Pod{ + Spec: getExpectedPodSpec(exampleGatewayNameIPs[0].String()), + }, }, "Gateway IP, keepDNS=true - Having a pod, gateway should be added": { gateway: "1.2.3.4", keepDNS: true, obj: &corev1.Pod{}, - expObj: getExpectedObj("1.2.3.4", true), + expObj: &corev1.Pod{ + Spec: getExpectedPodSpec_keepDNS("1.2.3.4"), + }, }, - "Gateway name, keepDNS=true - Having a pod, gateway should be added": { - gateway: exampleGatewayName, - keepDNS: false, - obj: &corev1.Pod{}, - expObj: getExpectedObj(exampleGatewayNameIPs[0].String(), false), + "Gateway IP, keepGatewayLabel='keepGateway' - it should be a NOP": { + gateway: "1.2.3.4", + keepGatewayLabel: "keepGateway", + obj: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "keepGateway": "true", + }, + }, + }, + expObj: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "keepGateway": "true", + }, + }, + }, + }, + "Gateway IP, keepGatewayLabel='keepGateway' - it should set gateway since label is false": { + gateway: "1.2.3.4", + keepGatewayLabel: "keepGateway", + obj: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "keepGateway": "false", + }, + }, + }, + expObj: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "keepGateway": "false", + }, + }, + Spec: getExpectedPodSpec("1.2.3.4"), + }, }, } @@ -82,7 +131,7 @@ func TestGatewayPodMutator(t *testing.T) { assert := assert.New(t) require := require.New(t) - m, err := mutator.NewGatewayPodMutator(test.gateway, test.keepDNS) + m, err := mutator.NewGatewayPodMutator(test.gateway, test.keepGatewayLabel, test.keepDNS) require.NoError(err) _, err = m.GatewayPodMutator(context.TODO(), nil, test.obj)