github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/uniter/operation/deploy.go (about)

     1  // Copyright 2014-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package operation
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  	corecharm "gopkg.in/juju/charm.v4"
    11  	"gopkg.in/juju/charm.v4/hooks"
    12  
    13  	"github.com/juju/juju/worker/uniter/charm"
    14  	"github.com/juju/juju/worker/uniter/hook"
    15  )
    16  
    17  // deploy implements charm install and charm upgrade operations.
    18  type deploy struct {
    19  	kind     Kind
    20  	charmURL *corecharm.URL
    21  	revert   bool
    22  	resolved bool
    23  
    24  	callbacks Callbacks
    25  	deployer  charm.Deployer
    26  	abort     <-chan struct{}
    27  }
    28  
    29  // String is part of the Operation interface.
    30  func (d *deploy) String() string {
    31  	verb := "upgrade to"
    32  	prefix := ""
    33  	switch {
    34  	case d.kind == Install:
    35  		verb = "install"
    36  	case d.revert:
    37  		prefix = "switch "
    38  	case d.resolved:
    39  		prefix = "continue "
    40  	}
    41  	return fmt.Sprintf("%s%s %s", prefix, verb, d.charmURL)
    42  }
    43  
    44  // Prepare downloads and verifies the charm, and informs the state server
    45  // that the unit will be using it. If the supplied state indicates that a
    46  // hook was pending, that hook is recorded in the returned state.
    47  // Prepare is part of the Operation interface.
    48  func (d *deploy) Prepare(state State) (*State, error) {
    49  	if err := d.checkAlreadyDone(state); err != nil {
    50  		return nil, errors.Trace(err)
    51  	}
    52  	if d.revert {
    53  		if err := d.deployer.NotifyRevert(); err != nil {
    54  			return nil, errors.Trace(err)
    55  		}
    56  	}
    57  	if d.resolved {
    58  		if err := d.deployer.NotifyResolved(); err != nil {
    59  			return nil, errors.Trace(err)
    60  		}
    61  	}
    62  	info, err := d.callbacks.GetArchiveInfo(d.charmURL)
    63  	if err != nil {
    64  		return nil, errors.Trace(err)
    65  	}
    66  	if err := d.deployer.Stage(info, d.abort); err != nil {
    67  		return nil, errors.Trace(err)
    68  	}
    69  	// note: yes, this *should* be in Prepare, not Execute. Before we can safely
    70  	// write out local state referencing the charm url (by returning the new
    71  	// State to the Executor, below), we have to register our interest in that
    72  	// charm on the state server. If we neglected to do so, the operation could
    73  	// race with a new service-charm-url change on the state server, and lead to
    74  	// failures on resume in which we try to obtain archive info for a charm that
    75  	// has already been removed from the state server.
    76  	if err := d.callbacks.SetCurrentCharm(d.charmURL); err != nil {
    77  		return nil, errors.Trace(err)
    78  	}
    79  	return d.getState(state, Pending), nil
    80  }
    81  
    82  // Execute installs or upgrades the prepared charm, and preserves any hook
    83  // recorded in the supplied state.
    84  // Execute is part of the Operation interface.
    85  func (d *deploy) Execute(state State) (*State, error) {
    86  	if err := d.deployer.Deploy(); err != nil {
    87  		return nil, err
    88  	}
    89  	return d.getState(state, Done), nil
    90  }
    91  
    92  // Commit restores state for any interrupted hook, or queues an install or
    93  // upgrade-charm hook if no hook was interrupted.
    94  func (d *deploy) Commit(state State) (*State, error) {
    95  	if err := d.callbacks.InitializeMetricsCollector(); err != nil {
    96  		return nil, errors.Trace(err)
    97  	}
    98  	change := &stateChange{
    99  		Kind: RunHook,
   100  	}
   101  	if hookInfo := d.interruptedHook(state); hookInfo != nil {
   102  		change.Hook = hookInfo
   103  		change.Step = Pending
   104  	} else {
   105  		change.Hook = &hook.Info{Kind: deployHookKinds[d.kind]}
   106  		change.Step = Queued
   107  	}
   108  	return change.apply(state), nil
   109  }
   110  
   111  func (d *deploy) checkAlreadyDone(state State) error {
   112  	if state.Kind != d.kind {
   113  		return nil
   114  	}
   115  	if *state.CharmURL != *d.charmURL {
   116  		return nil
   117  	}
   118  	if state.Step == Done {
   119  		return ErrSkipExecute
   120  	}
   121  	return nil
   122  }
   123  
   124  func (d *deploy) getState(state State, step Step) *State {
   125  	return stateChange{
   126  		Kind:     d.kind,
   127  		Step:     step,
   128  		CharmURL: d.charmURL,
   129  		Hook:     d.interruptedHook(state),
   130  	}.apply(state)
   131  }
   132  
   133  func (d *deploy) interruptedHook(state State) *hook.Info {
   134  	switch state.Kind {
   135  	case RunHook, Upgrade:
   136  		return state.Hook
   137  	}
   138  	return nil
   139  }
   140  
   141  // deployHookKinds determines what kind of hook should be queued after a
   142  // given deployment operation.
   143  var deployHookKinds = map[Kind]hooks.Kind{
   144  	Install: hooks.Install,
   145  	Upgrade: hooks.UpgradeCharm,
   146  }