github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/envworkermanager/envworkermanager.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package envworkermanager
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/loggo"
     9  	"github.com/juju/names"
    10  	"gopkg.in/mgo.v2"
    11  	"launchpad.net/tomb"
    12  
    13  	cmdutil "github.com/juju/juju/cmd/jujud/util"
    14  	"github.com/juju/juju/state"
    15  	"github.com/juju/juju/worker"
    16  )
    17  
    18  var logger = loggo.GetLogger("juju.worker.envworkermanager")
    19  
    20  // NewEnvWorkerManager returns a Worker which manages the workers which
    21  // need to run on a per environment basis. It takes a function which will
    22  // be called to start workers for a new environment. These workers
    23  // will be killed when an environment goes away.
    24  func NewEnvWorkerManager(
    25  	st InitialState,
    26  	startEnvWorkers func(InitialState, *state.State) (worker.Runner, error),
    27  ) worker.Worker {
    28  	m := &envWorkerManager{
    29  		st:              st,
    30  		startEnvWorkers: startEnvWorkers,
    31  	}
    32  	m.runner = worker.NewRunner(cmdutil.IsFatal, cmdutil.MoreImportant)
    33  	go func() {
    34  		m.tomb.Kill(m.loop())
    35  		m.tomb.Done()
    36  	}()
    37  	return m
    38  }
    39  
    40  // InitialState defines the State functionality used by
    41  // envWorkerManager and/or could be useful to startEnvWorkers
    42  // funcs. It mainly exists to support testing.
    43  type InitialState interface {
    44  	WatchEnvironments() state.StringsWatcher
    45  	ForEnviron(names.EnvironTag) (*state.State, error)
    46  	GetEnvironment(names.EnvironTag) (*state.Environment, error)
    47  	EnvironUUID() string
    48  	Machine(string) (*state.Machine, error)
    49  	MongoSession() *mgo.Session
    50  }
    51  
    52  type envWorkerManager struct {
    53  	runner          worker.Runner
    54  	tomb            tomb.Tomb
    55  	st              InitialState
    56  	startEnvWorkers func(InitialState, *state.State) (worker.Runner, error)
    57  }
    58  
    59  // Kill satisfies the Worker interface.
    60  func (m *envWorkerManager) Kill() {
    61  	m.tomb.Kill(nil)
    62  }
    63  
    64  // Wait satisfies the Worker interface.
    65  func (m *envWorkerManager) Wait() error {
    66  	return m.tomb.Wait()
    67  }
    68  
    69  func (m *envWorkerManager) loop() error {
    70  	w := m.st.WatchEnvironments()
    71  	defer w.Stop()
    72  	for {
    73  		select {
    74  		case uuids := <-w.Changes():
    75  			// One or more environments have changed.
    76  			for _, uuid := range uuids {
    77  				if err := m.envHasChanged(uuid); err != nil {
    78  					return errors.Trace(err)
    79  				}
    80  			}
    81  		case <-m.runner.Dying():
    82  			// The master runner is dying: wait for it to finish dying
    83  			// and report its error (if any).
    84  			return m.runner.Wait()
    85  		case <-m.tomb.Dying():
    86  			// The envWorkerManager has been asked to die: kill the
    87  			// master runner and stop.
    88  			m.runner.Kill()
    89  			m.runner.Wait()
    90  			return tomb.ErrDying
    91  		}
    92  	}
    93  }
    94  
    95  func (m *envWorkerManager) envHasChanged(uuid string) error {
    96  	envTag := names.NewEnvironTag(uuid)
    97  	envAlive, err := m.isEnvAlive(envTag)
    98  	if err != nil {
    99  		return errors.Trace(err)
   100  	}
   101  	if envAlive {
   102  		err = m.envIsAlive(envTag)
   103  	} else {
   104  		err = m.envIsDead(envTag)
   105  	}
   106  	return errors.Trace(err)
   107  }
   108  
   109  func (m *envWorkerManager) envIsAlive(envTag names.EnvironTag) error {
   110  	return m.runner.StartWorker(envTag.Id(), func() (worker.Worker, error) {
   111  		st, err := m.st.ForEnviron(envTag)
   112  		if err != nil {
   113  			return nil, errors.Annotatef(err, "failed to open state for environment %s", envTag.Id())
   114  		}
   115  		closeState := func() {
   116  			err := st.Close()
   117  			if err != nil {
   118  				logger.Errorf("error closing state for env %s: %v", envTag.Id(), err)
   119  			}
   120  		}
   121  
   122  		envRunner, err := m.startEnvWorkers(m.st, st)
   123  		if err != nil {
   124  			closeState()
   125  			return nil, errors.Trace(err)
   126  		}
   127  
   128  		// Close State when the runner for the environment is done.
   129  		go func() {
   130  			envRunner.Wait()
   131  			closeState()
   132  		}()
   133  
   134  		return envRunner, nil
   135  	})
   136  }
   137  
   138  func (m *envWorkerManager) envIsDead(envTag names.EnvironTag) error {
   139  	err := m.runner.StopWorker(envTag.Id())
   140  	return errors.Trace(err)
   141  }
   142  
   143  func (m *envWorkerManager) isEnvAlive(tag names.EnvironTag) (bool, error) {
   144  	env, err := m.st.GetEnvironment(tag)
   145  	if errors.IsNotFound(err) {
   146  		return false, nil
   147  	} else if err != nil {
   148  		return false, errors.Annotatef(err, "error loading environment %s", tag.Id())
   149  	}
   150  	return env.Life() == state.Alive, nil
   151  }