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  }