github.com/spotahome/redis-operator@v1.2.4/cmd/redisoperator/main.go (about) 1 package main 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 _ "net/http/pprof" 8 "os" 9 "os/signal" 10 "strings" 11 "syscall" 12 "time" 13 14 "github.com/prometheus/client_golang/prometheus" 15 "github.com/prometheus/client_golang/prometheus/promhttp" 16 _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" 17 18 "github.com/spotahome/redis-operator/cmd/utils" 19 "github.com/spotahome/redis-operator/log" 20 "github.com/spotahome/redis-operator/metrics" 21 "github.com/spotahome/redis-operator/operator/redisfailover" 22 "github.com/spotahome/redis-operator/service/k8s" 23 "github.com/spotahome/redis-operator/service/redis" 24 ) 25 26 const ( 27 gracePeriod = 5 * time.Second 28 metricsNamespace = "redis_operator" 29 ) 30 31 // Main is the main runner. 32 type Main struct { 33 flags *utils.CMDFlags 34 logger log.Logger 35 stopC chan struct{} 36 } 37 38 // New returns a Main object. 39 func New(logger log.Logger) Main { 40 // Init flags. 41 flgs := &utils.CMDFlags{} 42 flgs.Init() 43 44 return Main{ 45 logger: logger, 46 flags: flgs, 47 } 48 } 49 50 // Run execs the program. 51 func (m *Main) Run() error { 52 // Create signal channels. 53 m.stopC = make(chan struct{}) 54 errC := make(chan error) 55 56 // Set correct logging. 57 err := m.logger.Set(log.Level(strings.ToLower(m.flags.LogLevel))) 58 if err != nil { 59 return err 60 } 61 62 // Create the metrics client. 63 metricsRecorder := metrics.NewRecorder(metricsNamespace, prometheus.DefaultRegisterer) 64 65 // Serve metrics. 66 go func() { 67 log.Infof("Listening on %s for metrics exposure on URL %s", m.flags.ListenAddr, m.flags.MetricsPath) 68 http.Handle(m.flags.MetricsPath, promhttp.Handler()) 69 err := http.ListenAndServe(m.flags.ListenAddr, nil) 70 if err != nil { 71 log.Fatal(err) 72 } 73 }() 74 75 // Kubernetes clients. 76 k8sClient, customClient, aeClientset, err := utils.CreateKubernetesClients(m.flags) 77 if err != nil { 78 return err 79 } 80 81 // Create kubernetes service. 82 k8sservice := k8s.New(k8sClient, customClient, aeClientset, m.logger, metricsRecorder) 83 84 // Create the redis clients 85 redisClient := redis.New(metricsRecorder) 86 87 // Get lease lock resource namespace 88 lockNamespace := getNamespace() 89 90 // Create operator and run. 91 redisfailoverOperator, err := redisfailover.New(m.flags.ToRedisOperatorConfig(), k8sservice, k8sClient, lockNamespace, redisClient, metricsRecorder, m.logger) 92 if err != nil { 93 return err 94 } 95 96 go func() { 97 errC <- redisfailoverOperator.Run(context.Background()) 98 }() 99 100 // Await signals. 101 sigC := m.createSignalCapturer() 102 var finalErr error 103 select { 104 case <-sigC: 105 m.logger.Infof("Signal captured, exiting...") 106 case err := <-errC: 107 m.logger.Errorf("Error received: %s, exiting...", err) 108 finalErr = err 109 } 110 111 m.stop(m.stopC) 112 return finalErr 113 } 114 115 func (m *Main) createSignalCapturer() <-chan os.Signal { 116 sigC := make(chan os.Signal, 1) 117 signal.Notify(sigC, syscall.SIGTERM, syscall.SIGINT) 118 return sigC 119 } 120 121 func (m *Main) stop(stopC chan struct{}) { 122 m.logger.Infof("Stopping everything, waiting %s...", gracePeriod) 123 124 // stop everything and let them time to stop 125 close(stopC) 126 time.Sleep(gracePeriod) 127 } 128 129 func getNamespace() string { 130 // This way assumes you've set the POD_NAMESPACE environment 131 // variable using the downward API. This check has to be done first 132 // for backwards compatibility with the way InClusterConfig was 133 // originally set up 134 if ns, ok := os.LookupEnv("POD_NAMESPACE"); ok { 135 return ns 136 } 137 138 // Fall back to the namespace associated with the service account 139 // token, if available 140 if data, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil { 141 if ns := strings.TrimSpace(string(data)); len(ns) > 0 { 142 return ns 143 } 144 } 145 146 return "default" 147 } 148 149 // Run app. 150 func main() { 151 logger := log.Base() 152 m := New(logger) 153 154 if err := m.Run(); err != nil { 155 fmt.Fprintf(os.Stderr, "error executing: %s", err) 156 os.Exit(1) 157 } 158 os.Exit(0) 159 }