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