// 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) } }
// Run updates the vulnerability database at regular intervals. func Run(config *config.UpdaterConfig, datastore database.Datastore, st *utils.Stopper) { defer st.End() // Do not run the updater if there is no config or if the interval is 0. if config == nil || config.Interval == 0 { log.Infof("updater service is disabled.") return } whoAmI := uuid.New() log.Infof("updater service started. lock identifier: %s", whoAmI) for { var stop bool // Determine if this is the first update and define the next update time. // The next update time is (last update time + interval) or now if this is the first update. nextUpdate := time.Now().UTC() lastUpdate, firstUpdate, err := getLastUpdate(datastore) if err != nil { log.Errorf("an error occured while getting the last update time") nextUpdate = nextUpdate.Add(config.Interval) } else if firstUpdate == false { nextUpdate = lastUpdate.Add(config.Interval) } // If the next update timer is in the past, then try to update. if nextUpdate.Before(time.Now().UTC()) { // Attempt to get a lock on the the update. log.Debug("attempting to obtain update lock") hasLock, hasLockUntil := datastore.Lock(lockName, whoAmI, lockDuration, false) if hasLock { // Launch update in a new go routine. doneC := make(chan bool, 1) go func() { Update(datastore, firstUpdate) doneC <- true }() for done := false; !done && !stop; { select { case <-doneC: done = true case <-time.After(refreshLockDuration): // Refresh the lock until the update is done. datastore.Lock(lockName, whoAmI, lockDuration, true) case <-st.Chan(): stop = true } } // Unlock the update. datastore.Unlock(lockName, whoAmI) if stop { break } continue } else { lockOwner, lockExpiration, err := datastore.FindLock(lockName) if err != nil { log.Debug("update lock is already taken") nextUpdate = hasLockUntil } else { log.Debugf("update lock is already taken by %s until %v", lockOwner, lockExpiration) nextUpdate = lockExpiration } } } // Sleep, but remain stoppable until approximately the next update time. now := time.Now().UTC() waitUntil := nextUpdate.Add(time.Duration(rand.ExpFloat64()/0.5) * time.Second) log.Debugf("next update attempt scheduled for %v.", waitUntil) if !waitUntil.Before(now) { if !st.Sleep(waitUntil.Sub(time.Now())) { break } } } // Clean resources. for _, metadataFetcher := range metadataFetchers { metadataFetcher.Clean() } for _, fetcher := range fetchers { fetcher.Clean() } log.Info("updater service stopped") }
// Run updates the vulnerability database at regular intervals. func Run(config *config.UpdaterConfig, st *utils.Stopper) { defer st.End() // Do not run the updater if there is no config or if the interval is 0. if config == nil || config.Interval == 0 { log.Infof("updater service is disabled.") return } // Register healthchecker. health.RegisterHealthchecker("updater", Healthcheck) whoAmI := uuid.New() log.Infof("updater service started. lock identifier: %s", whoAmI) for { // Set the next update time to (last update time + interval) or now if there // is no last update time stored in database (first update) or if an error // occurs. var nextUpdate time.Time var stop bool if lastUpdate := getLastUpdate(); !lastUpdate.IsZero() { nextUpdate = lastUpdate.Add(config.Interval) } else { nextUpdate = time.Now().UTC() } // If the next update timer is in the past, then try to update. if nextUpdate.Before(time.Now().UTC()) { // Attempt to get a lock on the the update. log.Debug("attempting to obtain update lock") hasLock, hasLockUntil := database.Lock(flagName, lockDuration, whoAmI) if hasLock { // Launch update in a new go routine. doneC := make(chan bool, 1) go func() { Update() doneC <- true }() for done := false; !done && !stop; { select { case <-doneC: done = true case <-time.After(refreshLockDuration): // Refresh the lock until the update is done. database.Lock(flagName, lockDuration, whoAmI) case <-st.Chan(): stop = true } } // Unlock the update. database.Unlock(flagName, whoAmI) if stop { break } continue } else { lockOwner, lockExpiration, err := database.LockInfo(flagName) if err != nil { log.Debug("update lock is already taken") nextUpdate = hasLockUntil } else { log.Debugf("update lock is already taken by %s until %v", lockOwner, lockExpiration) nextUpdate = lockExpiration } } } // Sleep, but remain stoppable until approximately the next update time. now := time.Now().UTC() waitUntil := nextUpdate.Add(time.Duration(rand.ExpFloat64()/0.5) * time.Second) log.Debugf("next update attempt scheduled for %v.", waitUntil) if !waitUntil.Before(now) { if !st.Sleep(waitUntil.Sub(time.Now())) { break } } } log.Info("updater service stopped") }