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