github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/deployer/deployer.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package deployer
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/loggo"
    11  	"github.com/juju/utils"
    12  	"github.com/juju/utils/set"
    13  	"gopkg.in/juju/names.v2"
    14  
    15  	"github.com/juju/juju/agent"
    16  	apideployer "github.com/juju/juju/api/deployer"
    17  	"github.com/juju/juju/apiserver/params"
    18  	"github.com/juju/juju/status"
    19  	"github.com/juju/juju/watcher"
    20  	"github.com/juju/juju/worker"
    21  )
    22  
    23  var logger = loggo.GetLogger("juju.worker.deployer")
    24  
    25  // Deployer is responsible for deploying and recalling unit agents, according
    26  // to changes in a set of state units; and for the final removal of its agents'
    27  // units from state when they are no longer needed.
    28  type Deployer struct {
    29  	st       *apideployer.State
    30  	ctx      Context
    31  	deployed set.Strings
    32  }
    33  
    34  // Context abstracts away the differences between different unit deployment
    35  // strategies; where a Deployer is responsible for what to deploy, a Context
    36  // is responsible for how to deploy.
    37  type Context interface {
    38  	// DeployUnit causes the agent for the specified unit to be started and run
    39  	// continuously until further notice without further intervention. It will
    40  	// return an error if the agent is already deployed.
    41  	DeployUnit(unitName, initialPassword string) error
    42  
    43  	// RecallUnit causes the agent for the specified unit to be stopped, and
    44  	// the agent's data to be destroyed. It will return an error if the agent
    45  	// was not deployed by the manager.
    46  	RecallUnit(unitName string) error
    47  
    48  	// DeployedUnits returns the names of all units deployed by the manager.
    49  	DeployedUnits() ([]string, error)
    50  
    51  	// AgentConfig returns the agent config for the machine agent that is
    52  	// running the deployer.
    53  	AgentConfig() agent.Config
    54  }
    55  
    56  // NewDeployer returns a Worker that deploys and recalls unit agents
    57  // via ctx, taking a machine id to operate on.
    58  func NewDeployer(st *apideployer.State, ctx Context) (worker.Worker, error) {
    59  	d := &Deployer{
    60  		st:       st,
    61  		ctx:      ctx,
    62  		deployed: make(set.Strings),
    63  	}
    64  	w, err := watcher.NewStringsWorker(watcher.StringsConfig{
    65  		Handler: d,
    66  	})
    67  	if err != nil {
    68  		return nil, errors.Trace(err)
    69  	}
    70  	return w, nil
    71  }
    72  
    73  func (d *Deployer) SetUp() (watcher.StringsWatcher, error) {
    74  	tag := d.ctx.AgentConfig().Tag()
    75  	machineTag, ok := tag.(names.MachineTag)
    76  	if !ok {
    77  		return nil, errors.Errorf("expected names.MachineTag, got %T", tag)
    78  	}
    79  	machine, err := d.st.Machine(machineTag)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	machineUnitsWatcher, err := machine.WatchUnits()
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	deployed, err := d.ctx.DeployedUnits()
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	for _, unitName := range deployed {
    93  		d.deployed.Add(unitName)
    94  		if err := d.changed(unitName); err != nil {
    95  			return nil, err
    96  		}
    97  	}
    98  	return machineUnitsWatcher, nil
    99  }
   100  
   101  func (d *Deployer) Handle(_ <-chan struct{}, unitNames []string) error {
   102  	for _, unitName := range unitNames {
   103  		if err := d.changed(unitName); err != nil {
   104  			return err
   105  		}
   106  	}
   107  	return nil
   108  }
   109  
   110  // changed ensures that the named unit is deployed, recalled, or removed, as
   111  // indicated by its state.
   112  func (d *Deployer) changed(unitName string) error {
   113  	unitTag := names.NewUnitTag(unitName)
   114  	// Determine unit life state, and whether we're responsible for it.
   115  	logger.Infof("checking unit %q", unitName)
   116  	var life params.Life
   117  	unit, err := d.st.Unit(unitTag)
   118  	if params.IsCodeNotFoundOrCodeUnauthorized(err) {
   119  		life = params.Dead
   120  	} else if err != nil {
   121  		return err
   122  	} else {
   123  		life = unit.Life()
   124  	}
   125  	// Deployed units must be removed if they're Dead, or if the deployer
   126  	// is no longer responsible for them.
   127  	if d.deployed.Contains(unitName) {
   128  		if life == params.Dead {
   129  			if err := d.recall(unitName); err != nil {
   130  				return err
   131  			}
   132  		}
   133  	}
   134  	// The only units that should be deployed are those that (1) we are responsible
   135  	// for and (2) are Alive -- if we're responsible for a Dying unit that is not
   136  	// yet deployed, we should remove it immediately rather than undergo the hassle
   137  	// of deploying a unit agent purely so it can set itself to Dead.
   138  	if !d.deployed.Contains(unitName) {
   139  		if life == params.Alive {
   140  			return d.deploy(unit)
   141  		} else if unit != nil {
   142  			return d.remove(unit)
   143  		}
   144  	}
   145  	return nil
   146  }
   147  
   148  // deploy will deploy the supplied unit with the deployer's manager. It will
   149  // panic if it observes inconsistent internal state.
   150  func (d *Deployer) deploy(unit *apideployer.Unit) error {
   151  	unitName := unit.Name()
   152  	if d.deployed.Contains(unit.Name()) {
   153  		panic("must not re-deploy a deployed unit")
   154  	}
   155  	if err := unit.SetStatus(status.Waiting, status.MessageInstallingAgent, nil); err != nil {
   156  		return errors.Trace(err)
   157  	}
   158  	logger.Infof("deploying unit %q", unitName)
   159  	initialPassword, err := utils.RandomPassword()
   160  	if err != nil {
   161  		return err
   162  	}
   163  	if err := unit.SetPassword(initialPassword); err != nil {
   164  		return fmt.Errorf("cannot set password for unit %q: %v", unitName, err)
   165  	}
   166  	if err := d.ctx.DeployUnit(unitName, initialPassword); err != nil {
   167  		return err
   168  	}
   169  	d.deployed.Add(unitName)
   170  	return nil
   171  }
   172  
   173  // recall will recall the named unit with the deployer's manager. It will
   174  // panic if it observes inconsistent internal state.
   175  func (d *Deployer) recall(unitName string) error {
   176  	if !d.deployed.Contains(unitName) {
   177  		panic("must not recall a unit that is not deployed")
   178  	}
   179  	logger.Infof("recalling unit %q", unitName)
   180  	if err := d.ctx.RecallUnit(unitName); err != nil {
   181  		return err
   182  	}
   183  	d.deployed.Remove(unitName)
   184  	return nil
   185  }
   186  
   187  // remove will remove the supplied unit from state. It will panic if it
   188  // observes inconsistent internal state.
   189  func (d *Deployer) remove(unit *apideployer.Unit) error {
   190  	unitName := unit.Name()
   191  	if d.deployed.Contains(unitName) {
   192  		panic("must not remove a deployed unit")
   193  	} else if unit.Life() == params.Alive {
   194  		panic("must not remove an Alive unit")
   195  	}
   196  	logger.Infof("removing unit %q", unitName)
   197  	return unit.Remove()
   198  }
   199  
   200  func (d *Deployer) TearDown() error {
   201  	// Nothing to do here.
   202  	return nil
   203  }