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 }