github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/charmrevision/worker.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package charmrevision 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 "github.com/juju/utils/clock" 11 "gopkg.in/tomb.v1" 12 13 "github.com/juju/juju/worker" 14 ) 15 16 // RevisionUpdater exposes the "single" capability required by the worker. 17 // As the worker gains more responsibilities, it will likely need more; see 18 // storageprovisioner for a helpful model to grow towards. 19 type RevisionUpdater interface { 20 21 // UpdateLatestRevisions causes the environment to be scanned, the charm 22 // store to be interrogated, and model representations of updated charms 23 // to be stored in the environment. 24 // 25 // That is sufficiently complex that the logic should be implemented by 26 // the worker, not directly on the apiserver; as this functionality needs 27 // to change/mature, please migrate responsibilities down to the worker 28 // and grow this interface to match. 29 UpdateLatestRevisions() error 30 } 31 32 // Config defines the operation of a charm revision updater worker. 33 type Config struct { 34 35 // RevisionUpdater is the worker's view of the controller. 36 RevisionUpdater RevisionUpdater 37 38 // Clock is the worker's view of time. 39 Clock clock.Clock 40 41 // Period is the time between charm revision updates. 42 Period time.Duration 43 } 44 45 // Validate returns an error if the configuration cannot be expected 46 // to start a functional worker. 47 func (config Config) Validate() error { 48 if config.RevisionUpdater == nil { 49 return errors.NotValidf("nil RevisionUpdater") 50 } 51 if config.Clock == nil { 52 return errors.NotValidf("nil Clock") 53 } 54 if config.Period <= 0 { 55 return errors.NotValidf("non-positive Period") 56 } 57 return nil 58 } 59 60 // NewWorker returns a worker that calls UpdateLatestRevisions on the 61 // configured RevisionUpdater, once when started and subsequently every 62 // Period. 63 func NewWorker(config Config) (worker.Worker, error) { 64 if err := config.Validate(); err != nil { 65 return nil, errors.Trace(err) 66 } 67 w := &revisionUpdateWorker{ 68 config: config, 69 } 70 go func() { 71 defer w.tomb.Done() 72 w.tomb.Kill(w.loop()) 73 }() 74 return w, nil 75 } 76 77 type revisionUpdateWorker struct { 78 tomb tomb.Tomb 79 config Config 80 } 81 82 func (ruw *revisionUpdateWorker) loop() error { 83 var delay time.Duration 84 for { 85 select { 86 case <-ruw.tomb.Dying(): 87 return tomb.ErrDying 88 case <-ruw.config.Clock.After(delay): 89 err := ruw.config.RevisionUpdater.UpdateLatestRevisions() 90 if err != nil { 91 return errors.Trace(err) 92 } 93 } 94 delay = ruw.config.Period 95 } 96 } 97 98 // Kill is part of the worker.Worker interface. 99 func (ruw *revisionUpdateWorker) Kill() { 100 ruw.tomb.Kill(nil) 101 } 102 103 // Wait is part of the worker.Worker interface. 104 func (ruw *revisionUpdateWorker) Wait() error { 105 return ruw.tomb.Wait() 106 }