github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/worker/uniter/filter.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package uniter
     5  
     6  import (
     7  	"sort"
     8  
     9  	"github.com/juju/loggo"
    10  	"github.com/juju/names"
    11  	"launchpad.net/tomb"
    12  
    13  	"github.com/juju/juju/charm"
    14  	"github.com/juju/juju/state/api/params"
    15  	"github.com/juju/juju/state/api/uniter"
    16  	apiwatcher "github.com/juju/juju/state/api/watcher"
    17  	"github.com/juju/juju/state/watcher"
    18  	"github.com/juju/juju/worker"
    19  )
    20  
    21  var filterLogger = loggo.GetLogger("juju.worker.uniter.filter")
    22  
    23  // filter collects unit, service, and service config information from separate
    24  // state watchers, and presents it as events on channels designed specifically
    25  // for the convenience of the uniter.
    26  type filter struct {
    27  	st   *uniter.State
    28  	tomb tomb.Tomb
    29  
    30  	// outUnitDying is closed when the unit's life becomes Dying.
    31  	outUnitDying chan struct{}
    32  
    33  	// The out*On chans are used to deliver events to clients.
    34  	// The out* chans, when set to the corresponding out*On chan (rather than
    35  	// nil) indicate that an event of the appropriate type is ready to send
    36  	// to the client.
    37  	outConfig      chan struct{}
    38  	outConfigOn    chan struct{}
    39  	outUpgrade     chan *charm.URL
    40  	outUpgradeOn   chan *charm.URL
    41  	outResolved    chan params.ResolvedMode
    42  	outResolvedOn  chan params.ResolvedMode
    43  	outRelations   chan []int
    44  	outRelationsOn chan []int
    45  
    46  	// The want* chans are used to indicate that the filter should send
    47  	// events if it has them available.
    48  	wantForcedUpgrade chan bool
    49  	wantResolved      chan struct{}
    50  
    51  	// discardConfig is used to indicate that any pending config event
    52  	// should be discarded.
    53  	discardConfig chan struct{}
    54  
    55  	// setCharm is used to request that the unit's charm URL be set to
    56  	// a new value. This must be done in the filter's goroutine, so
    57  	// that config watches can be stopped and restarted pointing to
    58  	// the new charm URL. If we don't stop the watch before the
    59  	// (potentially) last reference to that settings document is
    60  	// removed, we'll see spurious errors (and even in the best case,
    61  	// we risk getting notifications for the wrong settings version).
    62  	setCharm chan *charm.URL
    63  
    64  	// didSetCharm is used to report back after setting a charm URL.
    65  	didSetCharm chan struct{}
    66  
    67  	// clearResolved is used to request that the unit's resolved flag
    68  	// be cleared. This must be done on the filter's goroutine so that
    69  	// it can immediately trigger the unit change handler, and thus
    70  	// ensure that subsquent requests for resolved events -- that land
    71  	// before the next watcher update for the unit -- do not erroneously
    72  	// send out stale values.
    73  	clearResolved chan struct{}
    74  
    75  	// didClearResolved is used to report back after clearing the resolved
    76  	// flag.
    77  	didClearResolved chan struct{}
    78  
    79  	// The following fields hold state that is collected while running,
    80  	// and used to detect interesting changes to express as events.
    81  	unit             *uniter.Unit
    82  	life             params.Life
    83  	resolved         params.ResolvedMode
    84  	service          *uniter.Service
    85  	upgradeFrom      serviceCharm
    86  	upgradeAvailable serviceCharm
    87  	upgrade          *charm.URL
    88  	relations        []int
    89  }
    90  
    91  // newFilter returns a filter that handles state changes pertaining to the
    92  // supplied unit.
    93  func newFilter(st *uniter.State, unitTag string) (*filter, error) {
    94  	f := &filter{
    95  		st:                st,
    96  		outUnitDying:      make(chan struct{}),
    97  		outConfig:         make(chan struct{}),
    98  		outConfigOn:       make(chan struct{}),
    99  		outUpgrade:        make(chan *charm.URL),
   100  		outUpgradeOn:      make(chan *charm.URL),
   101  		outResolved:       make(chan params.ResolvedMode),
   102  		outResolvedOn:     make(chan params.ResolvedMode),
   103  		outRelations:      make(chan []int),
   104  		outRelationsOn:    make(chan []int),
   105  		wantForcedUpgrade: make(chan bool),
   106  		wantResolved:      make(chan struct{}),
   107  		discardConfig:     make(chan struct{}),
   108  		setCharm:          make(chan *charm.URL),
   109  		didSetCharm:       make(chan struct{}),
   110  		clearResolved:     make(chan struct{}),
   111  		didClearResolved:  make(chan struct{}),
   112  	}
   113  	go func() {
   114  		defer f.tomb.Done()
   115  		err := f.loop(unitTag)
   116  		filterLogger.Errorf("%v", err)
   117  		f.tomb.Kill(err)
   118  	}()
   119  	return f, nil
   120  }
   121  
   122  func (f *filter) Stop() error {
   123  	f.tomb.Kill(nil)
   124  	return f.tomb.Wait()
   125  }
   126  
   127  func (f *filter) Dead() <-chan struct{} {
   128  	return f.tomb.Dead()
   129  }
   130  
   131  func (f *filter) Wait() error {
   132  	return f.tomb.Wait()
   133  }
   134  
   135  // UnitDying returns a channel which is closed when the Unit enters a Dying state.
   136  func (f *filter) UnitDying() <-chan struct{} {
   137  	return f.outUnitDying
   138  }
   139  
   140  // UpgradeEvents returns a channel that will receive a new charm URL whenever an
   141  // upgrade is indicated. Events should not be read until the baseline state
   142  // has been specified by calling WantUpgradeEvent.
   143  func (f *filter) UpgradeEvents() <-chan *charm.URL {
   144  	return f.outUpgradeOn
   145  }
   146  
   147  // ResolvedEvents returns a channel that may receive a ResolvedMode when the
   148  // unit's Resolved value changes, or when an event is explicitly requested.
   149  // A ResolvedNone state will never generate events, but ResolvedRetryHooks and
   150  // ResolvedNoHooks will always be delivered as described.
   151  func (f *filter) ResolvedEvents() <-chan params.ResolvedMode {
   152  	return f.outResolvedOn
   153  }
   154  
   155  // ConfigEvents returns a channel that will receive a signal whenever the service's
   156  // configuration changes, or when an event is explicitly requested.
   157  func (f *filter) ConfigEvents() <-chan struct{} {
   158  	return f.outConfigOn
   159  }
   160  
   161  // RelationsEvents returns a channel that will receive the ids of all the service's
   162  // relations whose Life status has changed.
   163  func (f *filter) RelationsEvents() <-chan []int {
   164  	return f.outRelationsOn
   165  }
   166  
   167  // WantUpgradeEvent controls whether the filter will generate upgrade
   168  // events for unforced service charm changes.
   169  func (f *filter) WantUpgradeEvent(mustForce bool) {
   170  	select {
   171  	case <-f.tomb.Dying():
   172  	case f.wantForcedUpgrade <- mustForce:
   173  	}
   174  }
   175  
   176  // SetCharm notifies the filter that the unit is running a new
   177  // charm. It causes the unit's charm URL to be set in state, and the
   178  // following changes to the filter's behaviour:
   179  //
   180  // * Upgrade events will only be generated for charms different to
   181  //   that supplied;
   182  // * A fresh relations event will be generated containing every relation
   183  //   the service is participating in;
   184  // * A fresh configuration event will be generated, and subsequent
   185  //   events will only be sent in response to changes in the version
   186  //   of the service's settings that is specific to that charm.
   187  //
   188  // SetCharm blocks until the charm URL is set in state, returning any
   189  // error that occurred.
   190  func (f *filter) SetCharm(curl *charm.URL) error {
   191  	select {
   192  	case <-f.tomb.Dying():
   193  		return tomb.ErrDying
   194  	case f.setCharm <- curl:
   195  	}
   196  	select {
   197  	case <-f.tomb.Dying():
   198  		return tomb.ErrDying
   199  	case <-f.didSetCharm:
   200  		return nil
   201  	}
   202  }
   203  
   204  // WantResolvedEvent indicates that the filter should send a resolved event
   205  // if one is available.
   206  func (f *filter) WantResolvedEvent() {
   207  	select {
   208  	case <-f.tomb.Dying():
   209  	case f.wantResolved <- nothing:
   210  	}
   211  }
   212  
   213  // ClearResolved notifies the filter that a resolved event has been handled
   214  // and should not be reported again.
   215  func (f *filter) ClearResolved() error {
   216  	select {
   217  	case <-f.tomb.Dying():
   218  		return tomb.ErrDying
   219  	case f.clearResolved <- nothing:
   220  	}
   221  	select {
   222  	case <-f.tomb.Dying():
   223  		return tomb.ErrDying
   224  	case <-f.didClearResolved:
   225  		filterLogger.Debugf("resolved clear completed")
   226  		return nil
   227  	}
   228  }
   229  
   230  // DiscardConfigEvent indicates that the filter should discard any pending
   231  // config event.
   232  func (f *filter) DiscardConfigEvent() {
   233  	select {
   234  	case <-f.tomb.Dying():
   235  	case f.discardConfig <- nothing:
   236  	}
   237  }
   238  
   239  func (f *filter) maybeStopWatcher(w watcher.Stopper) {
   240  	if w != nil {
   241  		watcher.Stop(w, &f.tomb)
   242  	}
   243  }
   244  
   245  func (f *filter) loop(unitTag string) (err error) {
   246  	defer func() {
   247  		if params.IsCodeNotFoundOrCodeUnauthorized(err) {
   248  			err = worker.ErrTerminateAgent
   249  		}
   250  	}()
   251  	if f.unit, err = f.st.Unit(unitTag); err != nil {
   252  		return err
   253  	}
   254  	if err = f.unitChanged(); err != nil {
   255  		return err
   256  	}
   257  	f.service, err = f.unit.Service()
   258  	if err != nil {
   259  		return err
   260  	}
   261  	if err = f.serviceChanged(); err != nil {
   262  		return err
   263  	}
   264  	unitw, err := f.unit.Watch()
   265  	if err != nil {
   266  		return err
   267  	}
   268  	defer f.maybeStopWatcher(unitw)
   269  	servicew, err := f.service.Watch()
   270  	if err != nil {
   271  		return err
   272  	}
   273  	defer f.maybeStopWatcher(servicew)
   274  	// configw and relationsw can get restarted, so we need to use
   275  	// their eventual values in the defer calls.
   276  	var configw apiwatcher.NotifyWatcher
   277  	var configChanges <-chan struct{}
   278  	curl, err := f.unit.CharmURL()
   279  	if err == nil {
   280  		configw, err = f.unit.WatchConfigSettings()
   281  		if err != nil {
   282  			return err
   283  		}
   284  		configChanges = configw.Changes()
   285  		f.upgradeFrom.url = curl
   286  	} else if err != uniter.ErrNoCharmURLSet {
   287  		filterLogger.Errorf("unit charm: %v", err)
   288  		return err
   289  	}
   290  	defer func() {
   291  		if configw != nil {
   292  			watcher.Stop(configw, &f.tomb)
   293  		}
   294  	}()
   295  	relationsw, err := f.service.WatchRelations()
   296  	if err != nil {
   297  		return err
   298  	}
   299  	defer func() {
   300  		if relationsw != nil {
   301  			watcher.Stop(relationsw, &f.tomb)
   302  		}
   303  	}()
   304  
   305  	// Config events cannot be meaningfully discarded until one is available;
   306  	// once we receive the initial change, we unblock discard requests by
   307  	// setting this channel to its namesake on f.
   308  	var discardConfig chan struct{}
   309  	for {
   310  		var ok bool
   311  		select {
   312  		case <-f.tomb.Dying():
   313  			return tomb.ErrDying
   314  
   315  		// Handle watcher changes.
   316  		case _, ok = <-unitw.Changes():
   317  			filterLogger.Debugf("got unit change")
   318  			if !ok {
   319  				return watcher.MustErr(unitw)
   320  			}
   321  			if err = f.unitChanged(); err != nil {
   322  				return err
   323  			}
   324  		case _, ok = <-servicew.Changes():
   325  			filterLogger.Debugf("got service change")
   326  			if !ok {
   327  				return watcher.MustErr(servicew)
   328  			}
   329  			if err = f.serviceChanged(); err != nil {
   330  				return err
   331  			}
   332  		case _, ok = <-configChanges:
   333  			filterLogger.Debugf("got config change")
   334  			if !ok {
   335  				return watcher.MustErr(configw)
   336  			}
   337  			filterLogger.Debugf("preparing new config event")
   338  			f.outConfig = f.outConfigOn
   339  			discardConfig = f.discardConfig
   340  		case keys, ok := <-relationsw.Changes():
   341  			filterLogger.Debugf("got relations change")
   342  			if !ok {
   343  				return watcher.MustErr(relationsw)
   344  			}
   345  			var ids []int
   346  			for _, key := range keys {
   347  				relationTag := names.RelationTag(key)
   348  				rel, err := f.st.Relation(relationTag)
   349  				if params.IsCodeNotFoundOrCodeUnauthorized(err) {
   350  					// If it's actually gone, this unit cannot have entered
   351  					// scope, and therefore never needs to know about it.
   352  				} else if err != nil {
   353  					return err
   354  				} else {
   355  					ids = append(ids, rel.Id())
   356  				}
   357  			}
   358  			f.relationsChanged(ids)
   359  
   360  		// Send events on active out chans.
   361  		case f.outUpgrade <- f.upgrade:
   362  			filterLogger.Debugf("sent upgrade event")
   363  			f.outUpgrade = nil
   364  		case f.outResolved <- f.resolved:
   365  			filterLogger.Debugf("sent resolved event")
   366  			f.outResolved = nil
   367  		case f.outConfig <- nothing:
   368  			filterLogger.Debugf("sent config event")
   369  			f.outConfig = nil
   370  		case f.outRelations <- f.relations:
   371  			filterLogger.Debugf("sent relations event")
   372  			f.outRelations = nil
   373  			f.relations = nil
   374  
   375  		// Handle explicit requests.
   376  		case curl := <-f.setCharm:
   377  			filterLogger.Debugf("changing charm to %q", curl)
   378  			// We need to restart the config watcher after setting the
   379  			// charm, because service config settings are distinct for
   380  			// different service charms.
   381  			if configw != nil {
   382  				if err := configw.Stop(); err != nil {
   383  					return err
   384  				}
   385  			}
   386  			if err := f.unit.SetCharmURL(curl); err != nil {
   387  				filterLogger.Debugf("failed setting charm url %q: %v", curl, err)
   388  				return err
   389  			}
   390  			select {
   391  			case <-f.tomb.Dying():
   392  				return tomb.ErrDying
   393  			case f.didSetCharm <- nothing:
   394  			}
   395  			configw, err = f.unit.WatchConfigSettings()
   396  			if err != nil {
   397  				return err
   398  			}
   399  			configChanges = configw.Changes()
   400  
   401  			// Restart the relations watcher.
   402  			if err := relationsw.Stop(); err != nil {
   403  				return err
   404  			}
   405  			relationsw, err = f.service.WatchRelations()
   406  			if err != nil {
   407  				return err
   408  			}
   409  
   410  			f.upgradeFrom.url = curl
   411  			if err = f.upgradeChanged(); err != nil {
   412  				return err
   413  			}
   414  		case force := <-f.wantForcedUpgrade:
   415  			filterLogger.Debugf("want forced upgrade %v", force)
   416  			f.upgradeFrom.force = force
   417  			if err = f.upgradeChanged(); err != nil {
   418  				return err
   419  			}
   420  		case <-f.wantResolved:
   421  			filterLogger.Debugf("want resolved event")
   422  			if f.resolved != params.ResolvedNone {
   423  				f.outResolved = f.outResolvedOn
   424  			}
   425  		case <-f.clearResolved:
   426  			filterLogger.Debugf("resolved event handled")
   427  			f.outResolved = nil
   428  			if err := f.unit.ClearResolved(); err != nil {
   429  				return err
   430  			}
   431  			if err := f.unitChanged(); err != nil {
   432  				return err
   433  			}
   434  			select {
   435  			case <-f.tomb.Dying():
   436  				return tomb.ErrDying
   437  			case f.didClearResolved <- nothing:
   438  			}
   439  		case <-discardConfig:
   440  			filterLogger.Debugf("discarded config event")
   441  			f.outConfig = nil
   442  		}
   443  	}
   444  }
   445  
   446  // unitChanged responds to changes in the unit.
   447  func (f *filter) unitChanged() error {
   448  	if err := f.unit.Refresh(); err != nil {
   449  		return err
   450  	}
   451  	if f.life != f.unit.Life() {
   452  		switch f.life = f.unit.Life(); f.life {
   453  		case params.Dying:
   454  			filterLogger.Infof("unit is dying")
   455  			close(f.outUnitDying)
   456  			f.outUpgrade = nil
   457  		case params.Dead:
   458  			filterLogger.Infof("unit is dead")
   459  			return worker.ErrTerminateAgent
   460  		}
   461  	}
   462  	resolved, err := f.unit.Resolved()
   463  	if err != nil {
   464  		return err
   465  	}
   466  	if resolved != f.resolved {
   467  		f.resolved = resolved
   468  		if f.resolved != params.ResolvedNone {
   469  			f.outResolved = f.outResolvedOn
   470  		}
   471  	}
   472  	return nil
   473  }
   474  
   475  // serviceChanged responds to changes in the service.
   476  func (f *filter) serviceChanged() error {
   477  	if err := f.service.Refresh(); err != nil {
   478  		return err
   479  	}
   480  	url, force, err := f.service.CharmURL()
   481  	if err != nil {
   482  		return err
   483  	}
   484  	f.upgradeAvailable = serviceCharm{url, force}
   485  	switch f.service.Life() {
   486  	case params.Dying:
   487  		if err := f.unit.Destroy(); err != nil {
   488  			return err
   489  		}
   490  	case params.Dead:
   491  		filterLogger.Infof("service is dead")
   492  		return worker.ErrTerminateAgent
   493  	}
   494  	return f.upgradeChanged()
   495  }
   496  
   497  // upgradeChanged responds to changes in the service or in the
   498  // upgrade requests that defines which charm changes should be
   499  // delivered as upgrades.
   500  func (f *filter) upgradeChanged() (err error) {
   501  	if f.life != params.Alive {
   502  		filterLogger.Debugf("charm check skipped, unit is dying")
   503  		f.outUpgrade = nil
   504  		return nil
   505  	}
   506  	if f.upgradeFrom.url == nil {
   507  		filterLogger.Debugf("charm check skipped, not yet installed.")
   508  		f.outUpgrade = nil
   509  		return nil
   510  	}
   511  	if *f.upgradeAvailable.url != *f.upgradeFrom.url {
   512  		if f.upgradeAvailable.force || !f.upgradeFrom.force {
   513  			filterLogger.Debugf("preparing new upgrade event")
   514  			if f.upgrade == nil || *f.upgrade != *f.upgradeAvailable.url {
   515  				f.upgrade = f.upgradeAvailable.url
   516  			}
   517  			f.outUpgrade = f.outUpgradeOn
   518  			return nil
   519  		}
   520  	}
   521  	filterLogger.Debugf("no new charm event")
   522  	f.outUpgrade = nil
   523  	return nil
   524  }
   525  
   526  // relationsChanged responds to service relation changes.
   527  func (f *filter) relationsChanged(ids []int) {
   528  outer:
   529  	for _, id := range ids {
   530  		for _, existing := range f.relations {
   531  			if id == existing {
   532  				continue outer
   533  			}
   534  		}
   535  		f.relations = append(f.relations, id)
   536  	}
   537  	if len(f.relations) != 0 {
   538  		sort.Ints(f.relations)
   539  		f.outRelations = f.outRelationsOn
   540  	}
   541  }
   542  
   543  // serviceCharm holds information about a charm.
   544  type serviceCharm struct {
   545  	url   *charm.URL
   546  	force bool
   547  }
   548  
   549  // nothing is marginally more pleasant to read than "struct{}{}".
   550  var nothing = struct{}{}