// RunHealth launches the Health API, which only exposes a method to fetch // quay-sec's health without any security or authentification mechanism. func RunHealth(port int, st *utils.Stopper) { log.Infof("starting Health API on port %d.", port) defer func() { log.Info("Health API stopped") st.End() }() srv := &graceful.Server{ Timeout: 10 * time.Second, // Interrupt health checks when stopping NoSignalHandling: true, // We want to use our own Stopper Server: &http.Server{ Addr: ":" + strconv.Itoa(port), Handler: NewHealthRouter(), }, } listenAndServeWithStopper(srv, st, "", "") }
// listenAndServeWithStopper wraps graceful.Server's // ListenAndServe/ListenAndServeTLS and adds the ability to interrupt them with // the provided utils.Stopper func listenAndServeWithStopper(srv *graceful.Server, st *utils.Stopper, certFile, keyFile string) { go func() { <-st.Chan() srv.Stop(0) }() var err error if certFile != "" && keyFile != "" { log.Info("API: TLS Enabled") err = srv.ListenAndServeTLS(certFile, keyFile) } else { err = srv.ListenAndServe() } if opErr, ok := err.(*net.OpError); !ok || (ok && opErr.Op != "accept") { log.Fatal(err) } }
// RunMain launches the main API, which exposes every possible interactions // with quay-sec. func RunMain(conf *Config, st *utils.Stopper) { log.Infof("starting API on port %d.", conf.Port) defer func() { log.Info("API stopped") st.End() }() srv := &graceful.Server{ Timeout: 0, // Already handled by our TimeOut middleware NoSignalHandling: true, // We want to use our own Stopper Server: &http.Server{ Addr: ":" + strconv.Itoa(conf.Port), TLSConfig: setupClientCert(conf.CAFile), Handler: NewVersionRouter(conf.TimeOut), }, } listenAndServeWithStopper(srv, st, conf.CertFile, conf.KeyFile) }
// Run pops notifications from the database, lock them, send them, mark them as // send and unlock them // // It uses an exponential backoff when POST requests fail func (notifier *HTTPNotifier) Run(st *utils.Stopper) { defer st.End() whoAmI := uuid.New() log.Infof("HTTP notifier started. URL: %s. Lock Identifier: %s", notifier.url, whoAmI) for { node, notification, err := database.FindOneNotificationToSend(database.GetDefaultNotificationWrapper()) if notification == nil || err != nil { if err != nil { log.Warningf("could not get notification to send: %s.", err) } if !st.Sleep(checkInterval) { break } continue } // Try to lock the notification hasLock, hasLockUntil := database.Lock(node, lockDuration, whoAmI) if !hasLock { continue } for backOff := time.Duration(0); ; backOff = timeutil.ExpBackoff(backOff, maxBackOff) { // Backoff, it happens when an error occurs during the communication // with the notification endpoint if backOff > 0 { // Renew lock before going to sleep if necessary if time.Now().Add(backOff).After(hasLockUntil.Add(-refreshLockAnticipation)) { hasLock, hasLockUntil = database.Lock(node, lockDuration, whoAmI) if !hasLock { log.Warning("lost lock ownership, aborting") break } } // Sleep if !st.Sleep(backOff) { return } } // Get notification content content, err := notification.GetContent() if err != nil { log.Warningf("could not get content of notification '%s': %s", notification.GetName(), err.Error()) break } // Marshal the notification content jsonContent, err := json.Marshal(struct { Name, Type string Content interface{} }{ Name: notification.GetName(), Type: notification.GetType(), Content: content, }) if err != nil { log.Errorf("could not marshal content of notification '%s': %s", notification.GetName(), err.Error()) break } // Post notification req, _ := http.NewRequest("POST", notifier.url, bytes.NewBuffer(jsonContent)) req.Header.Set("Content-Type", "application/json") client := &http.Client{} res, err := client.Do(req) if err != nil { log.Warningf("could not post notification '%s': %s", notification.GetName(), err.Error()) continue } res.Body.Close() if res.StatusCode != 200 && res.StatusCode != 201 { log.Warningf("could not post notification '%s': got status code %d", notification.GetName(), res.StatusCode) continue } // Mark the notification as sent database.MarkNotificationAsSent(node) log.Infof("sent notification '%s' successfully", notification.GetName()) break } if hasLock { database.Unlock(node, whoAmI) } } log.Info("HTTP notifier stopped") }