github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/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 a worker which
    21  // needs to run on a per environment basis. It takes a function which will
    22  // be called to start a worker for a new environment. This worker
    23  // will be killed when an environment goes away.
    24  func NewEnvWorkerManager(
    25  	st InitialState,
    26  	startEnvWorker func(InitialState, *state.State) (worker.Worker, error),
    27  ) worker.Worker {
    28  	m := &envWorkerManager{
    29  		st:             st,
    30  		startEnvWorker: startEnvWorker,
    31  	}
    32  	m.runner = worker.NewRunner(cmdutil.IsFatal, cmdutil.MoreImportant)
    33  	go func() {
    34  		defer m.tomb.Done()
    35  		m.tomb.Kill(m.loop())
    36  	}()
    37  	return m
    38  }
    39  
    40  // InitialState defines the State functionality used by
    41  // envWorkerManager and/or could be useful to startEnvWorker
    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  	startEnvWorker func(InitialState, *state.State) (worker.Worker, 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  	go func() {
    71  		// When the runner stops, make sure we stop the envWorker as well
    72  		m.tomb.Kill(m.runner.Wait())
    73  	}()
    74  	defer func() {
    75  		// When we return, make sure that we kill
    76  		// the runner and wait for it.
    77  		m.runner.Kill()
    78  		m.tomb.Kill(m.runner.Wait())
    79  	}()
    80  	w := m.st.WatchEnvironments()
    81  	defer w.Stop()
    82  	for {
    83  		select {
    84  		case uuids := <-w.Changes():
    85  			// One or more environments have changed.
    86  			for _, uuid := range uuids {
    87  				if err := m.envHasChanged(uuid); err != nil {
    88  					return errors.Trace(err)
    89  				}
    90  			}
    91  		case <-m.tomb.Dying():
    92  			return tomb.ErrDying
    93  		}
    94  	}
    95  }
    96  
    97  func (m *envWorkerManager) envHasChanged(uuid string) error {
    98  	envTag := names.NewEnvironTag(uuid)
    99  	envAlive, err := m.isEnvAlive(envTag)
   100  	if err != nil {
   101  		return errors.Trace(err)
   102  	}
   103  	if envAlive {
   104  		err = m.envIsAlive(envTag)
   105  	} else {
   106  		err = m.envIsDead(envTag)
   107  	}
   108  	return errors.Trace(err)
   109  }
   110  
   111  func (m *envWorkerManager) envIsAlive(envTag names.EnvironTag) error {
   112  	return m.runner.StartWorker(envTag.Id(), func() (worker.Worker, error) {
   113  		st, err := m.st.ForEnviron(envTag)
   114  		if err != nil {
   115  			return nil, errors.Annotatef(err, "failed to open state for environment %s", envTag.Id())
   116  		}
   117  		closeState := func() {
   118  			err := st.Close()
   119  			if err != nil {
   120  				logger.Errorf("error closing state for env %s: %v", envTag.Id(), err)
   121  			}
   122  		}
   123  
   124  		envRunner, err := m.startEnvWorker(m.st, st)
   125  		if err != nil {
   126  			closeState()
   127  			return nil, errors.Trace(err)
   128  		}
   129  
   130  		// Close State when the runner for the environment is done.
   131  		go func() {
   132  			envRunner.Wait()
   133  			closeState()
   134  		}()
   135  
   136  		return envRunner, nil
   137  	})
   138  }
   139  
   140  func (m *envWorkerManager) envIsDead(envTag names.EnvironTag) error {
   141  	err := m.runner.StopWorker(envTag.Id())
   142  	return errors.Trace(err)
   143  }
   144  
   145  func (m *envWorkerManager) isEnvAlive(tag names.EnvironTag) (bool, error) {
   146  	env, err := m.st.GetEnvironment(tag)
   147  	if errors.IsNotFound(err) {
   148  		return false, nil
   149  	} else if err != nil {
   150  		return false, errors.Annotatef(err, "error loading environment %s", tag.Id())
   151  	}
   152  	return env.Life() == state.Alive, nil
   153  }