github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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.v5" 11 "gopkg.in/juju/charm.v5/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 state server 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 if d.revert { 55 if err := d.deployer.NotifyRevert(); err != nil { 56 return nil, errors.Trace(err) 57 } 58 } 59 if d.resolved { 60 if err := d.deployer.NotifyResolved(); err != nil { 61 return nil, errors.Trace(err) 62 } 63 } 64 info, err := d.callbacks.GetArchiveInfo(d.charmURL) 65 if err != nil { 66 return nil, errors.Trace(err) 67 } 68 if err := d.deployer.Stage(info, d.abort); err != nil { 69 return nil, errors.Trace(err) 70 } 71 // note: yes, this *should* be in Prepare, not Execute. Before we can safely 72 // write out local state referencing the charm url (by returning the new 73 // State to the Executor, below), we have to register our interest in that 74 // charm on the state server. If we neglected to do so, the operation could 75 // race with a new service-charm-url change on the state server, and lead to 76 // failures on resume in which we try to obtain archive info for a charm that 77 // has already been removed from the state server. 78 if err := d.callbacks.SetCurrentCharm(d.charmURL); err != nil { 79 return nil, errors.Trace(err) 80 } 81 return d.getState(state, Pending), nil 82 } 83 84 // Execute installs or upgrades the prepared charm, and preserves any hook 85 // recorded in the supplied state. 86 // Execute is part of the Operation interface. 87 func (d *deploy) Execute(state State) (*State, error) { 88 if err := d.deployer.Deploy(); err == charm.ErrConflict { 89 return nil, NewDeployConflictError(d.charmURL) 90 } else if err != nil { 91 return nil, errors.Trace(err) 92 } 93 return d.getState(state, Done), nil 94 } 95 96 // Commit restores state for any interrupted hook, or queues an install or 97 // upgrade-charm hook if no hook was interrupted. 98 func (d *deploy) Commit(state State) (*State, error) { 99 if err := d.callbacks.InitializeMetricsCollector(); err != nil { 100 return nil, errors.Trace(err) 101 } 102 change := &stateChange{ 103 Kind: RunHook, 104 } 105 if hookInfo := d.interruptedHook(state); hookInfo != nil { 106 change.Hook = hookInfo 107 change.Step = Pending 108 } else { 109 change.Hook = &hook.Info{Kind: deployHookKinds[d.kind]} 110 change.Step = Queued 111 } 112 return change.apply(state), nil 113 } 114 115 func (d *deploy) checkAlreadyDone(state State) error { 116 if state.Kind != d.kind { 117 return nil 118 } 119 if *state.CharmURL != *d.charmURL { 120 return nil 121 } 122 if state.Step == Done { 123 return ErrSkipExecute 124 } 125 return nil 126 } 127 128 func (d *deploy) getState(state State, step Step) *State { 129 return stateChange{ 130 Kind: d.kind, 131 Step: step, 132 CharmURL: d.charmURL, 133 Hook: d.interruptedHook(state), 134 }.apply(state) 135 } 136 137 func (d *deploy) interruptedHook(state State) *hook.Info { 138 switch state.Kind { 139 case RunHook, Upgrade: 140 return state.Hook 141 } 142 return nil 143 } 144 145 // deployHookKinds determines what kind of hook should be queued after a 146 // given deployment operation. 147 var deployHookKinds = map[Kind]hooks.Kind{ 148 Install: hooks.Install, 149 Upgrade: hooks.UpgradeCharm, 150 }