github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/cleaner/cleaner.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package cleaner 5 6 import ( 7 "time" 8 9 "github.com/juju/clock" 10 "github.com/juju/errors" 11 "github.com/juju/loggo" 12 "gopkg.in/juju/worker.v1" 13 "gopkg.in/juju/worker.v1/catacomb" 14 15 "github.com/juju/juju/core/watcher" 16 ) 17 18 // period is the amount of time to wait before running cleanups, 19 // since the last time they were run. It is necessary to run 20 // cleanups periodically because Cleanup will not return an 21 // error if a specific cleanup fails, and the watcher will not 22 // be triggered unless a new cleanup is added. 23 const period = 30 * time.Second 24 25 var logger = loggo.GetLogger("juju.worker.cleaner") 26 27 type StateCleaner interface { 28 Cleanup() error 29 WatchCleanups() (watcher.NotifyWatcher, error) 30 } 31 32 // Cleaner is responsible for cleaning up the state. 33 type Cleaner struct { 34 catacomb catacomb.Catacomb 35 st StateCleaner 36 watcher watcher.NotifyWatcher 37 clock clock.Clock 38 } 39 40 // NewCleaner returns a worker.Worker that runs state.Cleanup() 41 // periodically, and whenever the CleanupWatcher signals documents 42 // marked for deletion. 43 func NewCleaner(st StateCleaner, clock clock.Clock) (worker.Worker, error) { 44 watcher, err := st.WatchCleanups() 45 if err != nil { 46 return nil, errors.Trace(err) 47 } 48 c := Cleaner{ 49 st: st, 50 watcher: watcher, 51 clock: clock, 52 } 53 if err := catacomb.Invoke(catacomb.Plan{ 54 Site: &c.catacomb, 55 Work: c.loop, 56 Init: []worker.Worker{watcher}, 57 }); err != nil { 58 return nil, errors.Trace(err) 59 } 60 return &c, nil 61 } 62 63 func (c *Cleaner) loop() error { 64 timer := c.clock.NewTimer(period) 65 defer timer.Stop() 66 for { 67 select { 68 case <-c.catacomb.Dying(): 69 return c.catacomb.ErrDying() 70 case _, ok := <-c.watcher.Changes(): 71 if !ok { 72 return errors.New("change channel closed") 73 } 74 case <-timer.Chan(): 75 } 76 err := c.st.Cleanup() 77 if err != nil { 78 // We don't exit if a cleanup fails, we just 79 // retry after when the timer fires. This 80 // enables us to retry cleanups that fail due 81 // to a transient failure, even when there 82 // are no new cleanups added. 83 logger.Errorf("cannot cleanup state: %v", err) 84 } 85 timer.Reset(period) 86 } 87 } 88 89 // Kill is part of the worker.Worker interface. 90 func (c *Cleaner) Kill() { 91 c.catacomb.Kill(nil) 92 } 93 94 // Wait is part of the worker.Worker interface. 95 func (c *Cleaner) Wait() error { 96 return c.catacomb.Wait() 97 }