1
0
Fork 0
mirror of https://github.com/prometheus-operator/prometheus-operator.git synced 2025-04-16 01:06:27 +00:00
prometheus-operator/cmd/operator/main.go
Arthur Silva Sens cc47b1e160
Prometheus Agent support (#5385)
* Introduce PrometheusAgent CRD

Operator is able to run with PrometheusAgent resources in the cluster, but doesn't do anything with them yet. This is the first step to implement the Prometheus Agent Operator.

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>

* Re-enable configmap and secret informers

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>
(cherry picked from commit 1a71db03db6b41cd0cee9d0193b6ea3884bb5bae)

* Implement Resolve for Agent operator

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>
(cherry picked from commit 49558165b9178b6c1bda833a48f7bfe1468c942a)

* Operator is able to create Agent Statefulset

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>
(cherry picked from commit 7a3826683c92f917312c866a2bb6401dc54b95f2)

* Agent Operator creates secret from ServiceMonitors

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>
(cherry picked from commit 11232669befb4de9d0765dfadfe5fae00b575f11)

* Agent Operator creates secret from PodMonitors

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>
(cherry picked from commit 5ae551734bac2babc056c86443d15729d43d12b0)

* Agent Operator creates secret from Probes

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>
(cherry picked from commit 9637612fbbe9617335fd6188271ebf2cc74a3693)

* Agent Operator configures remote-write

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>
(cherry picked from commit c4bdf230d527e19f8b77ca5f938b9254ed344f7d)

* Agent Operator configures additionalScrapeConfigs

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>
(cherry picked from commit d9f28db764641e682bf4fe8963310f791979c387)

* Implement UpdateStatus

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>
(cherry picked from commit c546ecaf3e8b73916df44a8f48b279c6988e32f5)

* Add resource handlers

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>
(cherry picked from commit 5b83359445e20f88ea5fff80302fce62d58058b9)

* make format

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>
(cherry picked from commit 6507964ba28f4ebf32ce3203db752444e288c45d)

* Only start agent operator if there is enough permission

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>

* Remove node endpoint syncronization from agent operator

The server operator already handles it

Signed-off-by: ArthurSens <arthursens2005@gmail.com>

* Move PrometheusAgent API from v1 to v1alpha1

Signed-off-by: ArthurSens <arthursens2005@gmail.com>

* pkg/prometheus/agent/statefulset.go: Fix image concatenation

Signed-off-by: ArthurSens <arthursens2005@gmail.com>

* Avoid name colisions between Prometheus Agents and Servers

Signed-off-by: ArthurSens <arthursens2005@gmail.com>

* agent/createOrUpdateConfigurationSecret: Do not handle case where servicemonitor and podmonitor selectors are empty

Signed-off-by: ArthurSens <arthursens2005@gmail.com>

* make format

Signed-off-by: ArthurSens <arthursens2005@gmail.com>

* make --always-make format generate

Signed-off-by: ArthurSens <arthursens2005@gmail.com>

* Remove unused fields from Operator struct

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>

* Add deployment mode as new selector label for agent/server ssts

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>

* WIP: Fix OperatorUpgrade e2e test

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>

* Panic if type casting PrometheusInterface doesn't return Prometheus/Agent

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>

* Detect whether PrometheusAgent CRD is installed or not

If the operator's service account has all permissions on the cluster and
the CRD isn't installed then the PrometheusAgent controller will run
but fail because of the absence of the CRD.

Signed-off-by: Simon Pasquier <spasquie@redhat.com>

* Create dedicated governing service for Prometheus agent

Signed-off-by: Simon Pasquier <spasquie@redhat.com>

---------

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>
Signed-off-by: ArthurSens <arthursens2005@gmail.com>
Signed-off-by: Simon Pasquier <spasquie@redhat.com>
Co-authored-by: Simon Pasquier <spasquie@redhat.com>
2023-03-27 12:30:01 +02:00

433 lines
19 KiB
Go

// Copyright 2016 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"context"
"crypto/tls"
"errors"
"flag"
"fmt"
stdlog "log"
"net"
"net/http"
"net/http/pprof"
"os"
"os/signal"
"strings"
"syscall"
"time"
logging "github.com/prometheus-operator/prometheus-operator/internal/log"
"github.com/prometheus-operator/prometheus-operator/pkg/admission"
alertmanagercontroller "github.com/prometheus-operator/prometheus-operator/pkg/alertmanager"
"github.com/prometheus-operator/prometheus-operator/pkg/k8sutil"
"github.com/prometheus-operator/prometheus-operator/pkg/operator"
prometheusagentcontroller "github.com/prometheus-operator/prometheus-operator/pkg/prometheus/agent"
prometheuscontroller "github.com/prometheus-operator/prometheus-operator/pkg/prometheus/server"
"github.com/prometheus-operator/prometheus-operator/pkg/server"
thanoscontroller "github.com/prometheus-operator/prometheus-operator/pkg/thanos"
"github.com/prometheus-operator/prometheus-operator/pkg/versionutil"
rbacproxytls "github.com/brancz/kube-rbac-proxy/pkg/tls"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/prometheus/common/version"
"golang.org/x/sync/errgroup"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
klog "k8s.io/klog/v2"
)
const (
defaultOperatorTLSDir = "/etc/tls/private"
)
const (
defaultReloaderCPU = "100m"
defaultReloaderMemory = "50Mi"
)
var (
ns = namespaces{}
deniedNs = namespaces{}
prometheusNs = namespaces{}
alertmanagerNs = namespaces{}
alertmanagerConfigNs = namespaces{}
thanosRulerNs = namespaces{}
)
type namespaces map[string]struct{}
// Set implements the flagset.Value interface.
func (n namespaces) Set(value string) error {
if n == nil {
return errors.New("expected n of type namespaces to be initialized")
}
for _, ns := range strings.Split(value, ",") {
n[ns] = struct{}{}
}
return nil
}
// String implements the flagset.Value interface.
func (n namespaces) String() string {
return strings.Join(n.asSlice(), ",")
}
func (n namespaces) asSlice() []string {
var ns = make([]string, 0, len(n))
for k := range n {
ns = append(ns, k)
}
return ns
}
func serve(srv *http.Server, listener net.Listener, logger log.Logger) func() error {
return func() error {
level.Info(logger).Log("msg", "Starting insecure server on "+listener.Addr().String())
if err := srv.Serve(listener); err != http.ErrServerClosed {
return err
}
return nil
}
}
func serveTLS(srv *http.Server, listener net.Listener, logger log.Logger) func() error {
return func() error {
level.Info(logger).Log("msg", "Starting secure server on "+listener.Addr().String())
if err := srv.ServeTLS(listener, "", ""); err != http.ErrServerClosed {
return err
}
return nil
}
}
var (
cfg = operator.Config{}
rawTLSCipherSuites string
serverTLS bool
flagset = flag.CommandLine
)
func init() {
// With migration to klog-gokit, calling klogv2.InitFlags(flagset) is not applicable.
flagset.StringVar(&cfg.ListenAddress, "web.listen-address", ":8080", "Address on which to expose metrics and web interface.")
flagset.BoolVar(&serverTLS, "web.enable-tls", false, "Activate prometheus operator web server TLS. "+
" This is useful for example when using the rule validation webhook.")
flagset.StringVar(&cfg.ServerTLSConfig.CertFile, "web.cert-file", defaultOperatorTLSDir+"/tls.crt", "Cert file to be used for operator web server endpoints.")
flagset.StringVar(&cfg.ServerTLSConfig.KeyFile, "web.key-file", defaultOperatorTLSDir+"/tls.key", "Private key matching the cert file to be used for operator web server endpoints.")
flagset.StringVar(&cfg.ServerTLSConfig.ClientCAFile, "web.client-ca-file", defaultOperatorTLSDir+"/tls-ca.crt", "Client CA certificate file to be used for operator web server endpoints.")
flagset.DurationVar(&cfg.ServerTLSConfig.ReloadInterval, "web.tls-reload-interval", time.Minute, "The interval at which to watch for TLS certificate changes, by default set to 1 minute. (default 1m0s).")
flagset.StringVar(&cfg.ServerTLSConfig.MinVersion, "web.tls-min-version", "VersionTLS13",
"Minimum TLS version supported. Value must match version names from https://golang.org/pkg/crypto/tls/#pkg-constants.")
flagset.StringVar(&rawTLSCipherSuites, "web.tls-cipher-suites", "", "Comma-separated list of cipher suites for the server."+
" Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants)."+
"If omitted, the default Go cipher suites will be used."+
"Note that TLS 1.3 ciphersuites are not configurable.")
flagset.StringVar(&cfg.Host, "apiserver", "", "API Server addr, e.g. ' - NOT RECOMMENDED FOR PRODUCTION - http://127.0.0.1:8080'. Omit parameter to run in on-cluster mode and utilize the service account token.")
flagset.StringVar(&cfg.TLSConfig.CertFile, "cert-file", "", " - NOT RECOMMENDED FOR PRODUCTION - Path to public TLS certificate file.")
flagset.StringVar(&cfg.TLSConfig.KeyFile, "key-file", "", "- NOT RECOMMENDED FOR PRODUCTION - Path to private TLS certificate file.")
flagset.StringVar(&cfg.TLSConfig.CAFile, "ca-file", "", "- NOT RECOMMENDED FOR PRODUCTION - Path to TLS CA file.")
flagset.StringVar(&cfg.KubeletObject, "kubelet-service", "", "Service/Endpoints object to write kubelets into in format \"namespace/name\"")
flagset.BoolVar(&cfg.TLSInsecure, "tls-insecure", false, "- NOT RECOMMENDED FOR PRODUCTION - Don't verify API server's CA certificate.")
// The Prometheus config reloader image is released along with the
// Prometheus Operator image, tagged with the same semver version. Default to
// the Prometheus Operator version if no Prometheus config reloader image is
// specified.
flagset.StringVar(&cfg.ReloaderConfig.Image, "prometheus-config-reloader", operator.DefaultPrometheusConfigReloaderImage, "Prometheus config reloader image")
flagset.StringVar(&cfg.ReloaderConfig.CPURequest, "config-reloader-cpu-request", defaultReloaderCPU, "Config Reloader CPU request. Value \"0\" disables it and causes no request to be configured. Flag overrides `--config-reloader-cpu` value for the CPU request")
flagset.StringVar(&cfg.ReloaderConfig.CPULimit, "config-reloader-cpu-limit", defaultReloaderCPU, "Config Reloader CPU limit. Value \"0\" disables it and causes no limit to be configured. Flag overrides `--config-reloader-cpu` for the CPU limit")
flagset.StringVar(&cfg.ReloaderConfig.MemoryRequest, "config-reloader-memory-request", defaultReloaderMemory, "Config Reloader Memory request. Value \"0\" disables it and causes no request to be configured. Flag overrides `--config-reloader-memory` for the memory request")
flagset.StringVar(&cfg.ReloaderConfig.MemoryLimit, "config-reloader-memory-limit", defaultReloaderMemory, "Config Reloader Memory limit. Value \"0\" disables it and causes no limit to be configured. Flag overrides `--config-reloader-memory` for the memory limit")
flagset.StringVar(&cfg.AlertmanagerDefaultBaseImage, "alertmanager-default-base-image", operator.DefaultAlertmanagerBaseImage, "Alertmanager default base image (path without tag/version)")
flagset.StringVar(&cfg.PrometheusDefaultBaseImage, "prometheus-default-base-image", operator.DefaultPrometheusBaseImage, "Prometheus default base image (path without tag/version)")
flagset.StringVar(&cfg.ThanosDefaultBaseImage, "thanos-default-base-image", operator.DefaultThanosBaseImage, "Thanos default base image (path without tag/version)")
flagset.Var(ns, "namespaces", "Namespaces to scope the interaction of the Prometheus Operator and the apiserver (allow list). This is mutually exclusive with --deny-namespaces.")
flagset.Var(deniedNs, "deny-namespaces", "Namespaces not to scope the interaction of the Prometheus Operator (deny list). This is mutually exclusive with --namespaces.")
flagset.Var(prometheusNs, "prometheus-instance-namespaces", "Namespaces where Prometheus custom resources and corresponding Secrets, Configmaps and StatefulSets are watched/created. If set this takes precedence over --namespaces or --deny-namespaces for Prometheus custom resources.")
flagset.Var(alertmanagerNs, "alertmanager-instance-namespaces", "Namespaces where Alertmanager custom resources and corresponding StatefulSets are watched/created. If set this takes precedence over --namespaces or --deny-namespaces for Alertmanager custom resources.")
flagset.Var(alertmanagerConfigNs, "alertmanager-config-namespaces", "Namespaces where AlertmanagerConfig custom resources and corresponding Secrets are watched/created. If set this takes precedence over --namespaces or --deny-namespaces for AlertmanagerConfig custom resources.")
flagset.Var(thanosRulerNs, "thanos-ruler-instance-namespaces", "Namespaces where ThanosRuler custom resources and corresponding StatefulSets are watched/created. If set this takes precedence over --namespaces or --deny-namespaces for ThanosRuler custom resources.")
flagset.Var(&cfg.Labels, "labels", "Labels to be add to all resources created by the operator")
flagset.StringVar(&cfg.LocalHost, "localhost", "localhost", "EXPERIMENTAL (could be removed in future releases) - Host used to communicate between local services on a pod. Fixes issues where localhost resolves incorrectly.")
flagset.StringVar(&cfg.ClusterDomain, "cluster-domain", "", "The domain of the cluster. This is used to generate service FQDNs. If this is not specified, DNS search domain expansion is used instead.")
flagset.StringVar(&cfg.LogLevel, "log-level", "info", fmt.Sprintf("Log level to use. Possible values: %s", strings.Join(logging.AvailableLogLevels, ", ")))
flagset.StringVar(&cfg.LogFormat, "log-format", "logfmt", fmt.Sprintf("Log format to use. Possible values: %s", strings.Join(logging.AvailableLogFormats, ", ")))
flagset.StringVar(&cfg.PromSelector, "prometheus-instance-selector", "", "Label selector to filter Prometheus Custom Resources to watch.")
flagset.StringVar(&cfg.AlertManagerSelector, "alertmanager-instance-selector", "", "Label selector to filter AlertManager Custom Resources to watch.")
flagset.StringVar(&cfg.ThanosRulerSelector, "thanos-ruler-instance-selector", "", "Label selector to filter ThanosRuler Custom Resources to watch.")
flagset.StringVar(&cfg.SecretListWatchSelector, "secret-field-selector", "", "Field selector to filter Secrets to watch")
}
func Main() int {
versionutil.RegisterFlags()
// No need to check for errors because Parse would exit on error.
_ = flagset.Parse(os.Args[1:])
if versionutil.ShouldPrintVersion() {
versionutil.Print(os.Stdout, "prometheus-operator")
return 0
}
logger, err := logging.NewLogger(cfg.LogLevel, cfg.LogFormat)
if err != nil {
stdlog.Fatal(err)
}
// Check validity of reloader resource values given to flags
_, err1 := resource.ParseQuantity(cfg.ReloaderConfig.CPULimit)
if err1 != nil {
fmt.Fprintf(os.Stderr, "The CPU limit specified for reloader \"%v\" is not a valid quantity! %v\n", cfg.ReloaderConfig.CPULimit, err1)
}
_, err2 := resource.ParseQuantity(cfg.ReloaderConfig.CPURequest)
if err2 != nil {
fmt.Fprintf(os.Stderr, "The CPU request specified for reloader \"%v\" is not a valid quantity! %v\n", cfg.ReloaderConfig.CPURequest, err2)
}
_, err3 := resource.ParseQuantity(cfg.ReloaderConfig.MemoryLimit)
if err3 != nil {
fmt.Fprintf(os.Stderr, "The Memory limit specified for reloader \"%v\" is not a valid quantity! %v\n", cfg.ReloaderConfig.MemoryLimit, err3)
}
_, err4 := resource.ParseQuantity(cfg.ReloaderConfig.MemoryRequest)
if err4 != nil {
fmt.Fprintf(os.Stderr, "The Memory request specified for reloader \"%v\" is not a valid quantity! %v\n", cfg.ReloaderConfig.MemoryRequest, err4)
}
if err1 != nil || err2 != nil || err3 != nil || err4 != nil {
return 1
}
// Above level 6, the k8s client would log bearer tokens in clear-text.
klog.ClampLevel(6)
klog.SetLogger(log.With(logger, "component", "k8s_client_runtime"))
level.Info(logger).Log("msg", "Starting Prometheus Operator", "version", version.Info())
level.Info(logger).Log("build_context", version.BuildContext())
if len(ns) > 0 && len(deniedNs) > 0 {
fmt.Fprint(os.Stderr, "--namespaces and --deny-namespaces are mutually exclusive. Please provide only one of them.\n")
return 1
}
cfg.Namespaces.AllowList = ns
if len(cfg.Namespaces.AllowList) == 0 {
cfg.Namespaces.AllowList[v1.NamespaceAll] = struct{}{}
}
cfg.Namespaces.DenyList = deniedNs
cfg.Namespaces.PrometheusAllowList = prometheusNs
cfg.Namespaces.AlertmanagerAllowList = alertmanagerNs
cfg.Namespaces.AlertmanagerConfigAllowList = alertmanagerConfigNs
cfg.Namespaces.ThanosRulerAllowList = thanosRulerNs
if len(cfg.Namespaces.PrometheusAllowList) == 0 {
cfg.Namespaces.PrometheusAllowList = cfg.Namespaces.AllowList
}
if len(cfg.Namespaces.AlertmanagerAllowList) == 0 {
cfg.Namespaces.AlertmanagerAllowList = cfg.Namespaces.AllowList
}
if len(cfg.Namespaces.AlertmanagerConfigAllowList) == 0 {
cfg.Namespaces.AlertmanagerConfigAllowList = cfg.Namespaces.AllowList
}
if len(cfg.Namespaces.ThanosRulerAllowList) == 0 {
cfg.Namespaces.ThanosRulerAllowList = cfg.Namespaces.AllowList
}
ctx, cancel := context.WithCancel(context.Background())
wg, ctx := errgroup.WithContext(ctx)
r := prometheus.NewRegistry()
k8sutil.MustRegisterClientGoMetrics(r)
po, err := prometheuscontroller.New(ctx, cfg, log.With(logger, "component", "prometheusoperator"), r)
if err != nil {
fmt.Fprint(os.Stderr, "instantiating prometheus controller failed: ", err)
cancel()
return 1
}
pao, err := prometheusagentcontroller.New(ctx, cfg, log.With(logger, "component", "prometheusagentoperator"), r)
if err != nil {
fmt.Fprint(os.Stderr, "instantiating prometheus-agent controller failed: ", err)
cancel()
return 1
}
ao, err := alertmanagercontroller.New(ctx, cfg, log.With(logger, "component", "alertmanageroperator"), r)
if err != nil {
fmt.Fprint(os.Stderr, "instantiating alertmanager controller failed: ", err)
cancel()
return 1
}
to, err := thanoscontroller.New(ctx, cfg, log.With(logger, "component", "thanosoperator"), r)
if err != nil {
fmt.Fprint(os.Stderr, "instantiating thanos controller failed: ", err)
cancel()
return 1
}
mux := http.NewServeMux()
admit := admission.New(log.With(logger, "component", "admissionwebhook"))
admit.Register(mux)
l, err := net.Listen("tcp", cfg.ListenAddress)
if err != nil {
fmt.Fprint(os.Stderr, "listening failed", cfg.ListenAddress, err)
cancel()
return 1
}
var tlsConfig *tls.Config
if serverTLS {
if rawTLSCipherSuites != "" {
cfg.ServerTLSConfig.CipherSuites = strings.Split(rawTLSCipherSuites, ",")
}
tlsConfig, err = server.NewTLSConfig(logger, cfg.ServerTLSConfig.CertFile, cfg.ServerTLSConfig.KeyFile,
cfg.ServerTLSConfig.ClientCAFile, cfg.ServerTLSConfig.MinVersion, cfg.ServerTLSConfig.CipherSuites)
if tlsConfig == nil || err != nil {
fmt.Fprint(os.Stderr, "invalid TLS config", err)
cancel()
return 1
}
}
validationTriggeredCounter := prometheus.NewCounter(prometheus.CounterOpts{
Name: "prometheus_operator_rule_validation_triggered_total",
Help: "DEPRECATED, removed in v0.57.0: Number of times a prometheusRule object triggered validation",
})
validationErrorsCounter := prometheus.NewCounter(prometheus.CounterOpts{
Name: "prometheus_operator_rule_validation_errors_total",
Help: "DEPRECATED, removed in v0.57.0: Number of errors that occurred while validating a prometheusRules object",
})
alertManagerConfigValidationTriggered := prometheus.NewCounter(prometheus.CounterOpts{
Name: "prometheus_operator_alertmanager_config_validation_triggered_total",
Help: "DEPRECATED, removed in v0.57.0: Number of times an alertmanagerconfig object triggered validation",
})
alertManagerConfigValidationError := prometheus.NewCounter(prometheus.CounterOpts{
Name: "prometheus_operator_alertmanager_config_validation_errors_total",
Help: "DEPRECATED, removed in v0.57.0: Number of errors that occurred while validating a alertmanagerconfig object",
})
r.MustRegister(
collectors.NewGoCollector(),
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
validationTriggeredCounter,
validationErrorsCounter,
alertManagerConfigValidationTriggered,
alertManagerConfigValidationError,
version.NewCollector("prometheus_operator"),
)
admit.RegisterMetrics(
validationTriggeredCounter,
validationErrorsCounter,
alertManagerConfigValidationTriggered,
alertManagerConfigValidationError,
)
mux.Handle("/metrics", promhttp.HandlerFor(r, promhttp.HandlerOpts{}))
mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
mux.Handle("/healthz", http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
}))
wg.Go(func() error { return po.Run(ctx) })
wg.Go(func() error { return pao.Run(ctx) })
wg.Go(func() error { return ao.Run(ctx) })
wg.Go(func() error { return to.Run(ctx) })
if tlsConfig != nil {
r, err := rbacproxytls.NewCertReloader(
cfg.ServerTLSConfig.CertFile,
cfg.ServerTLSConfig.KeyFile,
cfg.ServerTLSConfig.ReloadInterval,
)
if err != nil {
fmt.Fprint(os.Stderr, "failed to initialize certificate reloader", err)
cancel()
return 1
}
tlsConfig.GetCertificate = r.GetCertificate
wg.Go(func() error {
for {
// r.Watch will wait ReloadInterval, so this is not
// a hot loop
if err := r.Watch(ctx); err != nil {
level.Warn(logger).Log("msg", "error watching certificate reloader",
"err", err)
} else {
return nil
}
}
})
}
srv := &http.Server{
Handler: mux,
TLSConfig: tlsConfig,
ReadHeaderTimeout: 30 * time.Second,
ReadTimeout: 30 * time.Second,
// use flags on standard logger to align with base logger and get consistent parsed fields form adapter:
// use shortfile flag to get proper 'caller' field (avoid being wrongly parsed/extracted from message)
// and no datetime related flag to keep 'ts' field from base logger (with controlled format)
ErrorLog: stdlog.New(log.NewStdlibAdapter(logger), "", stdlog.Lshortfile),
}
if srv.TLSConfig == nil {
wg.Go(serve(srv, l, logger))
} else {
wg.Go(serveTLS(srv, l, logger))
}
term := make(chan os.Signal, 1)
signal.Notify(term, os.Interrupt, syscall.SIGTERM)
select {
case <-term:
level.Info(logger).Log("msg", "Received SIGTERM, exiting gracefully...")
case <-ctx.Done():
}
if err := srv.Shutdown(ctx); err != nil {
level.Warn(logger).Log("msg", "Server shutdown error", "err", err)
}
cancel()
if err := wg.Wait(); err != nil {
level.Warn(logger).Log("msg", "Unhandled error received. Exiting...", "err", err)
return 1
}
return 0
}
func main() {
os.Exit(Main())
}