github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/worker/provisioner/provisioner.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package provisioner
     5  
     6  import (
     7  	"sync"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/loggo"
    11  	"github.com/juju/names"
    12  	"launchpad.net/tomb"
    13  
    14  	"github.com/juju/juju/agent"
    15  	apiprovisioner "github.com/juju/juju/api/provisioner"
    16  	apiwatcher "github.com/juju/juju/api/watcher"
    17  	"github.com/juju/juju/environmentserver/authentication"
    18  	"github.com/juju/juju/environs"
    19  	"github.com/juju/juju/environs/config"
    20  	"github.com/juju/juju/instance"
    21  	"github.com/juju/juju/state/watcher"
    22  	"github.com/juju/juju/utils"
    23  	"github.com/juju/juju/worker"
    24  )
    25  
    26  var logger = loggo.GetLogger("juju.provisioner")
    27  
    28  // Ensure our structs implement the required Provisioner interface.
    29  var _ Provisioner = (*environProvisioner)(nil)
    30  var _ Provisioner = (*containerProvisioner)(nil)
    31  
    32  // Provisioner represents a running provisioner worker.
    33  type Provisioner interface {
    34  	worker.Worker
    35  	Stop() error
    36  	getMachineWatcher() (apiwatcher.StringsWatcher, error)
    37  	getRetryWatcher() (apiwatcher.NotifyWatcher, error)
    38  }
    39  
    40  // environProvisioner represents a running provisioning worker for machine nodes
    41  // belonging to an environment.
    42  type environProvisioner struct {
    43  	provisioner
    44  	environ environs.Environ
    45  	configObserver
    46  }
    47  
    48  // containerProvisioner represents a running provisioning worker for containers
    49  // hosted on a machine.
    50  type containerProvisioner struct {
    51  	provisioner
    52  	containerType instance.ContainerType
    53  	machine       *apiprovisioner.Machine
    54  	configObserver
    55  }
    56  
    57  // provisioner providers common behaviour for a running provisioning worker.
    58  type provisioner struct {
    59  	Provisioner
    60  	st          *apiprovisioner.State
    61  	agentConfig agent.Config
    62  	broker      environs.InstanceBroker
    63  	toolsFinder ToolsFinder
    64  	tomb        tomb.Tomb
    65  }
    66  
    67  // configObserver is implemented so that tests can see
    68  // when the environment configuration changes.
    69  type configObserver struct {
    70  	sync.Mutex
    71  	observer chan<- *config.Config
    72  }
    73  
    74  // notify notifies the observer of a configuration change.
    75  func (o *configObserver) notify(cfg *config.Config) {
    76  	o.Lock()
    77  	if o.observer != nil {
    78  		o.observer <- cfg
    79  	}
    80  	o.Unlock()
    81  }
    82  
    83  // Err returns the reason why the provisioner has stopped or tomb.ErrStillAlive
    84  // when it is still alive.
    85  func (p *provisioner) Err() (reason error) {
    86  	return p.tomb.Err()
    87  }
    88  
    89  // Kill implements worker.Worker.Kill.
    90  func (p *provisioner) Kill() {
    91  	p.tomb.Kill(nil)
    92  }
    93  
    94  // Wait implements worker.Worker.Wait.
    95  func (p *provisioner) Wait() error {
    96  	return p.tomb.Wait()
    97  }
    98  
    99  // Stop stops the provisioner and returns any error encountered while
   100  // provisioning.
   101  func (p *provisioner) Stop() error {
   102  	p.tomb.Kill(nil)
   103  	return p.tomb.Wait()
   104  }
   105  
   106  // getToolsFinder returns a ToolsFinder for the provided State.
   107  // This exists for mocking.
   108  var getToolsFinder = func(st *apiprovisioner.State) ToolsFinder {
   109  	return st
   110  }
   111  
   112  // getStartTask creates a new worker for the provisioner,
   113  func (p *provisioner) getStartTask(harvestMode config.HarvestMode) (ProvisionerTask, error) {
   114  	auth, err := authentication.NewAPIAuthenticator(p.st)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  	// Start responding to changes in machines, and to any further updates
   119  	// to the environment config.
   120  	machineWatcher, err := p.getMachineWatcher()
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  	retryWatcher, err := p.getRetryWatcher()
   125  	if err != nil && !errors.IsNotImplemented(err) {
   126  		return nil, err
   127  	}
   128  	tag := p.agentConfig.Tag()
   129  	machineTag, ok := tag.(names.MachineTag)
   130  	if !ok {
   131  		errors.Errorf("expacted names.MachineTag, got %T", tag)
   132  	}
   133  
   134  	envCfg, err := p.st.EnvironConfig()
   135  	if err != nil {
   136  		return nil, errors.Annotate(err, "could not retrieve the environment config.")
   137  	}
   138  
   139  	secureServerConnection := false
   140  	if info, ok := p.agentConfig.StateServingInfo(); ok {
   141  		secureServerConnection = info.CAPrivateKey != ""
   142  	}
   143  	task := NewProvisionerTask(
   144  		machineTag,
   145  		harvestMode,
   146  		p.st,
   147  		p.toolsFinder,
   148  		machineWatcher,
   149  		retryWatcher,
   150  		p.broker,
   151  		auth,
   152  		envCfg.ImageStream(),
   153  		secureServerConnection,
   154  	)
   155  	return task, nil
   156  }
   157  
   158  // NewEnvironProvisioner returns a new Provisioner for an environment.
   159  // When new machines are added to the state, it allocates instances
   160  // from the environment and allocates them to the new machines.
   161  func NewEnvironProvisioner(st *apiprovisioner.State, agentConfig agent.Config) Provisioner {
   162  	p := &environProvisioner{
   163  		provisioner: provisioner{
   164  			st:          st,
   165  			agentConfig: agentConfig,
   166  			toolsFinder: getToolsFinder(st),
   167  		},
   168  	}
   169  	p.Provisioner = p
   170  	logger.Tracef("Starting environ provisioner for %q", p.agentConfig.Tag())
   171  	go func() {
   172  		defer p.tomb.Done()
   173  		p.tomb.Kill(errors.Cause(p.loop()))
   174  	}()
   175  	return p
   176  }
   177  
   178  func (p *environProvisioner) loop() error {
   179  	var environConfigChanges <-chan struct{}
   180  	environWatcher, err := p.st.WatchForEnvironConfigChanges()
   181  	if err != nil {
   182  		return utils.LoggedErrorStack(errors.Trace(err))
   183  	}
   184  	environConfigChanges = environWatcher.Changes()
   185  	defer watcher.Stop(environWatcher, &p.tomb)
   186  
   187  	p.environ, err = worker.WaitForEnviron(environWatcher, p.st, p.tomb.Dying())
   188  	if err != nil {
   189  		return utils.LoggedErrorStack(errors.Trace(err))
   190  	}
   191  	p.broker = p.environ
   192  
   193  	harvestMode := p.environ.Config().ProvisionerHarvestMode()
   194  	task, err := p.getStartTask(harvestMode)
   195  	if err != nil {
   196  		return utils.LoggedErrorStack(errors.Trace(err))
   197  	}
   198  	defer watcher.Stop(task, &p.tomb)
   199  
   200  	for {
   201  		select {
   202  		case <-p.tomb.Dying():
   203  			return tomb.ErrDying
   204  		case <-task.Dying():
   205  			err := task.Err()
   206  			logger.Errorf("environ provisioner died: %v", err)
   207  			return err
   208  		case _, ok := <-environConfigChanges:
   209  			if !ok {
   210  				return watcher.EnsureErr(environWatcher)
   211  			}
   212  			environConfig, err := p.st.EnvironConfig()
   213  			if err != nil {
   214  				logger.Errorf("cannot load environment configuration: %v", err)
   215  				return err
   216  			}
   217  			if err := p.setConfig(environConfig); err != nil {
   218  				logger.Errorf("loaded invalid environment configuration: %v", err)
   219  			}
   220  			task.SetHarvestMode(environConfig.ProvisionerHarvestMode())
   221  		}
   222  	}
   223  }
   224  
   225  func (p *environProvisioner) getMachineWatcher() (apiwatcher.StringsWatcher, error) {
   226  	return p.st.WatchEnvironMachines()
   227  }
   228  
   229  func (p *environProvisioner) getRetryWatcher() (apiwatcher.NotifyWatcher, error) {
   230  	return p.st.WatchMachineErrorRetry()
   231  }
   232  
   233  // setConfig updates the environment configuration and notifies
   234  // the config observer.
   235  func (p *environProvisioner) setConfig(environConfig *config.Config) error {
   236  	if err := p.environ.SetConfig(environConfig); err != nil {
   237  		return err
   238  	}
   239  	p.configObserver.notify(environConfig)
   240  	return nil
   241  }
   242  
   243  // NewContainerProvisioner returns a new Provisioner. When new machines
   244  // are added to the state, it allocates instances from the environment
   245  // and allocates them to the new machines.
   246  func NewContainerProvisioner(
   247  	containerType instance.ContainerType,
   248  	st *apiprovisioner.State,
   249  	agentConfig agent.Config,
   250  	broker environs.InstanceBroker,
   251  	toolsFinder ToolsFinder,
   252  ) Provisioner {
   253  
   254  	p := &containerProvisioner{
   255  		provisioner: provisioner{
   256  			st:          st,
   257  			agentConfig: agentConfig,
   258  			broker:      broker,
   259  			toolsFinder: toolsFinder,
   260  		},
   261  		containerType: containerType,
   262  	}
   263  	p.Provisioner = p
   264  	logger.Tracef("Starting %s provisioner for %q", p.containerType, p.agentConfig.Tag())
   265  	go func() {
   266  		defer p.tomb.Done()
   267  		p.tomb.Kill(p.loop())
   268  	}()
   269  	return p
   270  }
   271  
   272  func (p *containerProvisioner) loop() error {
   273  	var environConfigChanges <-chan struct{}
   274  	environWatcher, err := p.st.WatchForEnvironConfigChanges()
   275  	if err != nil {
   276  		return err
   277  	}
   278  	environConfigChanges = environWatcher.Changes()
   279  	defer watcher.Stop(environWatcher, &p.tomb)
   280  
   281  	config, err := p.st.EnvironConfig()
   282  	if err != nil {
   283  		return err
   284  	}
   285  	harvestMode := config.ProvisionerHarvestMode()
   286  
   287  	task, err := p.getStartTask(harvestMode)
   288  	if err != nil {
   289  		return err
   290  	}
   291  	defer watcher.Stop(task, &p.tomb)
   292  
   293  	for {
   294  		select {
   295  		case <-p.tomb.Dying():
   296  			return tomb.ErrDying
   297  		case <-task.Dying():
   298  			err := task.Err()
   299  			logger.Errorf("%s provisioner died: %v", p.containerType, err)
   300  			return err
   301  		case _, ok := <-environConfigChanges:
   302  			if !ok {
   303  				return watcher.EnsureErr(environWatcher)
   304  			}
   305  			environConfig, err := p.st.EnvironConfig()
   306  			if err != nil {
   307  				logger.Errorf("cannot load environment configuration: %v", err)
   308  				return err
   309  			}
   310  			p.configObserver.notify(environConfig)
   311  			task.SetHarvestMode(environConfig.ProvisionerHarvestMode())
   312  		}
   313  	}
   314  }
   315  
   316  func (p *containerProvisioner) getMachine() (*apiprovisioner.Machine, error) {
   317  	if p.machine == nil {
   318  		tag := p.agentConfig.Tag()
   319  		machineTag, ok := tag.(names.MachineTag)
   320  		if !ok {
   321  			return nil, errors.Errorf("expected names.MachineTag, got %T", tag)
   322  		}
   323  		var err error
   324  		if p.machine, err = p.st.Machine(machineTag); err != nil {
   325  			logger.Errorf("%s is not in state", machineTag)
   326  			return nil, err
   327  		}
   328  	}
   329  	return p.machine, nil
   330  }
   331  
   332  func (p *containerProvisioner) getMachineWatcher() (apiwatcher.StringsWatcher, error) {
   333  	machine, err := p.getMachine()
   334  	if err != nil {
   335  		return nil, err
   336  	}
   337  	return machine.WatchContainers(p.containerType)
   338  }
   339  
   340  func (p *containerProvisioner) getRetryWatcher() (apiwatcher.NotifyWatcher, error) {
   341  	return nil, errors.NotImplementedf("getRetryWatcher")
   342  }