package cmd import ( "context" "net/http" "os" "os/signal" "syscall" "time" "github.com/oklog/run" "github.com/spf13/cobra" "go.uber.org/zap" kwhhttp "github.com/slok/kubewebhook/v2/pkg/http" "github.com/slok/kubewebhook/v2/pkg/log" kwhmutating "github.com/slok/kubewebhook/v2/pkg/webhook/mutating" kwhmodel "github.com/slok/kubewebhook/v2/pkg/model" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type wrapLogger struct { *zap.SugaredLogger } func (n *wrapLogger) WithValues(map[string]interface{}) log.Logger { return n } func (n *wrapLogger) WithCtxValues(context.Context) log.Logger { return n } func (n *wrapLogger) SetValuesOnCtx(parent context.Context, _ map[string]interface{}) context.Context { return parent } func (n *wrapLogger) Warningf(template string, args ...interface{}) { n.Warnf(template, args...) } type Config struct { WebhookListenAddr string TLSCertFilePath string TLSKeyFilePath string EnableIngressSingleHost bool IngressHostRegexes []string MinSMScrapeInterval time.Duration LabelMarks map[string]string } var ( cmdConfig = &Config{ LabelMarks: map[string]string{}, } serverCmd = &cobra.Command{ Use: "server", Run: func(cmd *cobra.Command, args []string) { var err error logger := zap.L() var g run.Group // OS signals. { sigC := make(chan os.Signal, 1) exitC := make(chan struct{}) signal.Notify(sigC, syscall.SIGTERM, syscall.SIGINT) g.Add( func() error { select { case s := <-sigC: logger.Sugar().Info("signal %s received", s) return nil case <-exitC: return nil } }, func(_ error) { close(exitC) }, ) } // Webhook HTTP server. { logger := logger.With(zap.String("addr", cmdConfig.WebhookListenAddr), zap.String("http-server", "webhooks")) mt := kwhmutating.MutatorFunc(func(_ context.Context, _ *kwhmodel.AdmissionReview, obj metav1.Object) (*kwhmutating.MutatorResult, error) { logger.Info("mutating webhook called", zap.Any("object", obj)) return &kwhmutating.MutatorResult{MutatedObject: obj}, nil }) // Create webhook. wh, err := kwhmutating.NewWebhook(kwhmutating.WebhookConfig{ ID: "mutator.cilium-envoy-hook.io", Mutator: mt, Logger: &wrapLogger{logger.Sugar()}, }) if err != nil { logger.Fatal("error creating webhook", zap.Error(err)) } // Get HTTP handler from webhook. whHandler, err := kwhhttp.HandlerFor(kwhhttp.HandlerConfig{Webhook: wh, Logger: &wrapLogger{logger.Sugar()}}) if err != nil { logger.Fatal("error creating webhook handler", zap.Error(err)) } server := &http.Server{ Addr: cmdConfig.WebhookListenAddr, Handler: whHandler, } g.Add( func() error { if cmdConfig.TLSCertFilePath == "" || cmdConfig.TLSKeyFilePath == "" { logger.Warn("webhook running without TLS") logger.Info("http server listening...") return server.ListenAndServe() } logger.Info("https server listening...") return server.ListenAndServeTLS(cmdConfig.TLSCertFilePath, cmdConfig.TLSKeyFilePath) }, func(_ error) { logger.Info("start draining connections") ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() err := server.Shutdown(ctx) if err != nil { logger.Error("error while shutting down the server", zap.Error(err)) } else { logger.Info("server stopped") } }, ) } err = g.Run() if err != nil { logger.Fatal("error while running the application", zap.Error(err)) } }, } ) func init() { rootCmd.AddCommand(serverCmd) serverCmd.PersistentFlags().StringVar(&cmdConfig.WebhookListenAddr, "webhook-listen-address", ":8080", "The address where the HTTPS server will be listening to serve the webhooks.") serverCmd.PersistentFlags().StringVar(&cmdConfig.TLSCertFilePath, "tls-cert-file-path", "", "The path for the webhook HTTPS server TLS cert file.") serverCmd.PersistentFlags().StringVar(&cmdConfig.TLSKeyFilePath, "tls-key-file-path", "", "The path for the webhook HTTPS server TLS key file.") }