func initLogger() {
	if cfg.LogFormat == "console" {
		log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
	}
	lvl, err := zerolog.ParseLevel(cfg.LogLevel)
	if err != nil {
		lvl = zerolog.InfoLevel
		log.Err(err).Msgf("Could not set log level to '%v'.", cfg.LogLevel)
	}
	zerolog.SetGlobalLevel(lvl)
	if lvl == zerolog.TraceLevel {
		log.Logger = log.With().Caller().Logger()
	}
}
initLogger configures the log level
package cmd
import (
	"context"
	"fmt"
	"net/http"
	"os/signal"
	"syscall"
	"time"
	"os"
	"github.com/estahn/k8s-image-swapper/pkg/config"
	"github.com/estahn/k8s-image-swapper/pkg/registry"
	"github.com/estahn/k8s-image-swapper/pkg/secrets"
	"github.com/estahn/k8s-image-swapper/pkg/types"
	"github.com/estahn/k8s-image-swapper/pkg/webhook"
	homedir "github.com/mitchellh/go-homedir"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	kwhhttp "github.com/slok/kubewebhook/v2/pkg/http"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
)
var cfgFile string
var cfg *config.Config = &config.Config{}
var rootCmd = &cobra.Command{
	Use:   "k8s-image-swapper",
	Short: "Mirror images into your own registry and swap image references automatically.",
	Long: `Mirror images into your own registry and swap image references automatically.
A mutating webhook for Kubernetes, pointing the images to a new location.`,
	Run: func(cmd *cobra.Command, args []string) {
		log.Trace().Interface("config", cfg).Msg("config")
		rClient, err := registry.NewECRClient(cfg.Target.AWS.Region, cfg.Target.AWS.EcrDomain())
		if err != nil {
			log.Err(err).Msg("error connecting to registry client")
			os.Exit(1)
		}
		imageSwapPolicy, err := types.ParseImageSwapPolicy(cfg.ImageSwapPolicy)
		if err != nil {
			log.Err(err)
		}
		imageCopyPolicy, err := types.ParseImageCopyPolicy(cfg.ImageCopyPolicy)
		if err != nil {
			log.Err(err)
		}
		imagePullSecretProvider := setupImagePullSecretsProvider()
		wh, err := webhook.NewImageSwapperWebhookWithOpts(
			rClient,
			webhook.Filters(cfg.Source.Filters),
			webhook.ImagePullSecretsProvider(imagePullSecretProvider),
			webhook.ImageSwapPolicy(imageSwapPolicy),
			webhook.ImageCopyPolicy(imageCopyPolicy),
		)
		if err != nil {
			log.Err(err).Msg("error creating webhook")
			os.Exit(1)
		}
		whHandler, err := kwhhttp.HandlerFor(kwhhttp.HandlerConfig{Webhook: wh})
		if err != nil {
			log.Err(err).Msg("error creating webhook handler")
			os.Exit(1)
		}
		handler := http.NewServeMux()
		handler.Handle("/webhook", whHandler)
		handler.Handle("/metrics", promhttp.Handler())
		handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			_, err := w.Write([]byte(`<html>
			 <head><title>k8s-image-webhook</title></head>
			 <body>
			 <h1>k8s-image-webhook</h1>
			 <ul><li><a href='/metrics'>Metrics</a></li><li><a href='/webhook'>Webhook</a></li></ul>
			 </body>
			 </html>`))
			if err != nil {
				log.Error()
			}
		})
		srv := &http.Server{
			Addr: cfg.ListenAddress,
			WriteTimeout: time.Second * 15,
			ReadTimeout:  time.Second * 15,
			IdleTimeout:  time.Second * 60,
			Handler:      handler,
		}
		go func() {
			log.Info().Msgf("Listening on %v", cfg.ListenAddress)
			if cfg.TLSCertFile != "" && cfg.TLSKeyFile != "" {
				if err := srv.ListenAndServeTLS(cfg.TLSCertFile, cfg.TLSKeyFile); err != nil {
					log.Err(err).Msg("error serving webhook")
					os.Exit(1)
				}
			} else {
				if err := srv.ListenAndServe(); err != nil {
					log.Err(err).Msg("error serving webhook")
					os.Exit(1)
				}
			}
		}()
		c := make(chan os.Signal, 1)
		signal.Notify(c, os.Interrupt, syscall.SIGTERM)
		<-c
		var wait time.Duration
		ctx, cancel := context.WithTimeout(context.Background(), wait)
		defer cancel()
		if err := srv.Shutdown(ctx); err != nil {
			log.Err(err).Msg("Error during shutdown")
		}
		log.Info().Msg("Shutting down")
		os.Exit(0)
	},
}
func Execute() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}
func init() {
	cobra.OnInitialize(initConfig, initLogger)
	rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.k8s-image-swapper.yaml)")
	rootCmd.PersistentFlags().StringVar(&cfg.LogLevel, "log-level", "info", "Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal]")
	rootCmd.PersistentFlags().StringVar(&cfg.LogFormat, "log-format", "json", "Format of the log messages. Valid levels: [json, console]")
	rootCmd.Flags().StringVar(&cfg.ListenAddress, "listen-address", ":8443", "Address on which to expose the webhook")
	rootCmd.Flags().StringVar(&cfg.TLSCertFile, "tls-cert-file", "", "File containing the TLS certificate")
	rootCmd.Flags().StringVar(&cfg.TLSKeyFile, "tls-key-file", "", "File containing the TLS private key")
	rootCmd.Flags().BoolVar(&cfg.DryRun, "dry-run", true, "If true, print the action taken without taking it")
}
func initConfig() {
	if cfgFile != "" {
		viper.SetConfigFile(cfgFile)
	} else {
		home, err := homedir.Dir()
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		viper.AddConfigPath(home)
		viper.AddConfigPath(".")
		viper.SetConfigType("yaml")
		viper.SetConfigName(".k8s-image-swapper")
	}
	viper.AutomaticEnv()  
	if err := viper.ReadInConfig(); err == nil {
		log.Info().Str("file", viper.ConfigFileUsed()).Msg("using config file")
	}
	if err := viper.Unmarshal(&cfg); err != nil {
		log.Err(err).Msg("failed to unmarshal the config file")
	}
}