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  }