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 }