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