github.com/Financial-Times/publish-availability-monitor@v1.12.0/main.go (about)

     1  package main
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"net/http"
     7  	"os"
     8  	"os/signal"
     9  	"regexp"
    10  	"sync"
    11  	"syscall"
    12  	"time"
    13  
    14  	"github.com/Financial-Times/go-logger/v2"
    15  	"github.com/Financial-Times/kafka-client-go/v4"
    16  	"github.com/Financial-Times/publish-availability-monitor/config"
    17  	"github.com/Financial-Times/publish-availability-monitor/envs"
    18  	"github.com/Financial-Times/publish-availability-monitor/feeds"
    19  	"github.com/Financial-Times/publish-availability-monitor/metrics"
    20  	status "github.com/Financial-Times/service-status-go/httphandlers"
    21  	"github.com/gorilla/mux"
    22  )
    23  
    24  var configFileName = flag.String("config", "", "Path to configuration file")
    25  var envsFileName = flag.String("envs-file-name", "/etc/pam/envs/read-environments.json", "Path to json file that contains environments configuration")
    26  var envCredentialsFileName = flag.String("envs-credentials-file-name", "/etc/pam/credentials/read-environments-credentials.json", "Path to json file that contains environments credentials")
    27  var validatorCredentialsFileName = flag.String("validator-credentials-file-name", "/etc/pam/credentials/validator-credentials.json", "Path to json file that contains validation endpoints configuration")
    28  var configRefreshPeriod = flag.Int("config-refresh-period", 1, "Refresh period for configuration in minutes. By default it is 1 minute.")
    29  
    30  var carouselTransactionIDRegExp = regexp.MustCompile(`^.+_carousel_[\d]{10}.*$`)
    31  
    32  func main() {
    33  	flag.Parse()
    34  
    35  	log := logger.NewUPPLogger("publish-availability-monitor", "INFO")
    36  
    37  	var err error
    38  	appConfig, err := config.NewAppConfig(*configFileName, log)
    39  	if err != nil {
    40  		log.WithError(err).Error("Cannot load configuration")
    41  		return
    42  	}
    43  
    44  	var environments = envs.NewEnvironments()
    45  	var subscribedFeeds = make(map[string][]feeds.Feed)
    46  	var metricSink = make(chan metrics.PublishMetric)
    47  	var configFilesHashValues = make(map[string]string)
    48  
    49  	wg := new(sync.WaitGroup)
    50  	wg.Add(1)
    51  
    52  	log.Info("Sourcing dynamic configs from file")
    53  
    54  	go envs.WatchConfigFiles(
    55  		wg,
    56  		*envsFileName,
    57  		*envCredentialsFileName,
    58  		*validatorCredentialsFileName,
    59  		*configRefreshPeriod,
    60  		configFilesHashValues,
    61  		environments,
    62  		subscribedFeeds,
    63  		appConfig,
    64  		log,
    65  	)
    66  
    67  	wg.Wait()
    68  
    69  	metricContainer := metrics.NewHistory(make([]metrics.PublishMetric, 0))
    70  
    71  	var e2eTestUUIDs []string
    72  	for _, c := range appConfig.Capabilities {
    73  		for _, id := range c.TestIDs {
    74  			if !sliceContains(e2eTestUUIDs, id) {
    75  				e2eTestUUIDs = append(e2eTestUUIDs, id)
    76  			}
    77  		}
    78  	}
    79  
    80  	var arn *string
    81  	if appConfig.QueueConf.ClusterARN != "" {
    82  		arn = &appConfig.QueueConf.ClusterARN
    83  	}
    84  
    85  	messageHandler := NewKafkaMessageHandler(appConfig, environments, subscribedFeeds, metricSink, metricContainer, e2eTestUUIDs, log)
    86  	consumer, err := kafka.NewConsumer(
    87  		kafka.ConsumerConfig{
    88  			ClusterArn:              arn,
    89  			BrokersConnectionString: appConfig.QueueConf.ConnectionString,
    90  			ConsumerGroup:           appConfig.QueueConf.ConsumerGroup,
    91  			Options:                 kafka.DefaultConsumerOptions(),
    92  		},
    93  		[]*kafka.Topic{
    94  			kafka.NewTopic(appConfig.QueueConf.Topic, kafka.WithLagTolerance(int64(appConfig.QueueConf.LagTolerance))),
    95  		},
    96  		log,
    97  	)
    98  	if err != nil {
    99  		log.WithError(err).Fatal("Failed to create Kafka consumer")
   100  	}
   101  
   102  	go startHTTPServer(appConfig, environments, subscribedFeeds, metricContainer, consumer, log)
   103  
   104  	publishMetricDestinations := []metrics.Destination{
   105  		metrics.NewSplunkFeeder(appConfig.SplunkConf.LogPrefix),
   106  	}
   107  
   108  	capabilityMetricDestinations := []metrics.Destination{
   109  		metrics.NewGraphiteSender(appConfig, log),
   110  	}
   111  
   112  	aggregator := metrics.NewAggregator(metricSink, publishMetricDestinations, capabilityMetricDestinations, log)
   113  	go aggregator.Run()
   114  
   115  	for !environments.AreReady() {
   116  		log.Info("Environments not set, retry in 3s...")
   117  		time.Sleep(3 * time.Second)
   118  	}
   119  
   120  	go consumer.Start(messageHandler.HandleMessage)
   121  	defer func() {
   122  		if err = consumer.Close(); err != nil {
   123  			log.WithError(err).Error("Error terminating consumer")
   124  		}
   125  	}()
   126  
   127  	ch := make(chan os.Signal, 1)
   128  	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
   129  	<-ch
   130  }
   131  
   132  func startHTTPServer(
   133  	appConfig *config.AppConfig,
   134  	environments *envs.Environments,
   135  	subscribedFeeds map[string][]feeds.Feed,
   136  	metricContainer *metrics.History,
   137  	consumer *kafka.Consumer,
   138  	log *logger.UPPLogger,
   139  ) {
   140  	router := mux.NewRouter()
   141  
   142  	hc := newHealthcheck(appConfig, metricContainer, environments, subscribedFeeds, consumer, log)
   143  	router.HandleFunc("/__health", hc.checkHealth())
   144  	router.HandleFunc(status.GTGPath, status.NewGoodToGoHandler(hc.GTG))
   145  
   146  	router.HandleFunc("/__history", loadHistory(metricContainer))
   147  
   148  	router.HandleFunc(status.PingPath, status.PingHandler)
   149  	router.HandleFunc(status.PingPathDW, status.PingHandler)
   150  
   151  	router.HandleFunc(status.BuildInfoPath, status.BuildInfoHandler)
   152  	router.HandleFunc(status.BuildInfoPathDW, status.BuildInfoHandler)
   153  
   154  	http.Handle("/", router)
   155  	err := http.ListenAndServe(":8080", nil) //nolint:gosec
   156  	if err != nil {
   157  		log.Panicf("Couldn't set up HTTP listener: %+v\n", err)
   158  	}
   159  }
   160  
   161  func loadHistory(metricContainer *metrics.History) func(w http.ResponseWriter, r *http.Request) {
   162  	return func(w http.ResponseWriter, r *http.Request) {
   163  		fmt.Fprint(w, metricContainer.String())
   164  	}
   165  }
   166  
   167  func sliceContains(s []string, e string) bool {
   168  	for _, a := range s {
   169  		if a == e {
   170  			return true
   171  		}
   172  	}
   173  	return false
   174  }