github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/uniter/resolver/opfactory.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package resolver 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 "gopkg.in/juju/charm.v6" 10 "gopkg.in/juju/charm.v6/hooks" 11 12 "github.com/juju/juju/core/lxdprofile" 13 "github.com/juju/juju/core/model" 14 "github.com/juju/juju/worker/uniter/hook" 15 "github.com/juju/juju/worker/uniter/operation" 16 "github.com/juju/juju/worker/uniter/remotestate" 17 ) 18 19 var logger = loggo.GetLogger("juju.worker.uniter.resolver") 20 21 // resolverOpFactory wraps an operation.Factory such that skips that affect 22 // local state will, when committed, update the embedded LocalState struct 23 // to reflect the change made by the operation. 24 // 25 // The wrapped operations embed information specific to the remote state 26 // snapshot that was used to create the operation. Thus, remote state changes 27 // observed between the time the operation was created and committed do not 28 // affect the operation; and the local state change will not prevent further 29 // operations from being enqueued to achieve the new remote state. 30 type resolverOpFactory struct { 31 operation.Factory 32 33 LocalState *LocalState 34 RemoteState remotestate.Snapshot 35 } 36 37 func (s *resolverOpFactory) NewRunHook(info hook.Info) (operation.Operation, error) { 38 op, err := s.Factory.NewRunHook(info) 39 if err != nil { 40 return nil, errors.Trace(err) 41 } 42 return s.wrapHookOp(op, info), nil 43 } 44 45 func (s *resolverOpFactory) NewSkipHook(info hook.Info) (operation.Operation, error) { 46 op, err := s.Factory.NewSkipHook(info) 47 if err != nil { 48 return nil, errors.Trace(err) 49 } 50 return s.wrapHookOp(op, info), nil 51 } 52 53 func (s *resolverOpFactory) NewNoOpFinishUpgradeSeries() (operation.Operation, error) { 54 op, err := s.Factory.NewNoOpFinishUpgradeSeries() 55 if err != nil { 56 return nil, errors.Trace(err) 57 } 58 f := func(*operation.State) { 59 s.LocalState.UpgradeSeriesStatus = model.UpgradeSeriesNotStarted 60 } 61 op = onCommitWrapper{op, f} 62 return op, nil 63 } 64 65 // NewFinishUpgradeCharmProfile completes the process of a charm profile, by 66 // setting the local state to a not known state. 67 func (s *resolverOpFactory) NewFinishUpgradeCharmProfile(charmURL *charm.URL) (operation.Operation, error) { 68 op, err := s.Factory.NewFinishUpgradeCharmProfile(charmURL) 69 if err != nil { 70 return nil, errors.Trace(err) 71 } 72 f := func(*operation.State) { 73 s.LocalState.UpgradeCharmProfileStatus = lxdprofile.NotKnownStatus 74 } 75 op = onCommitWrapper{op, f} 76 return op, nil 77 } 78 79 func (s *resolverOpFactory) NewUpgrade(charmURL *charm.URL) (operation.Operation, error) { 80 op, err := s.Factory.NewUpgrade(charmURL) 81 if err != nil { 82 return nil, errors.Trace(err) 83 } 84 return s.wrapUpgradeOp(op, charmURL), nil 85 } 86 87 func (s *resolverOpFactory) NewNoOpUpgrade(charmURL *charm.URL) (operation.Operation, error) { 88 op, err := s.Factory.NewNoOpUpgrade(charmURL) 89 if err != nil { 90 return nil, errors.Trace(err) 91 } 92 return s.wrapUpgradeOp(op, charmURL), nil 93 } 94 95 func (s *resolverOpFactory) NewRevertUpgrade(charmURL *charm.URL) (operation.Operation, error) { 96 op, err := s.Factory.NewRevertUpgrade(charmURL) 97 if err != nil { 98 return nil, errors.Trace(err) 99 } 100 return s.wrapUpgradeOp(op, charmURL), nil 101 } 102 103 func (s *resolverOpFactory) NewResolvedUpgrade(charmURL *charm.URL) (operation.Operation, error) { 104 op, err := s.Factory.NewResolvedUpgrade(charmURL) 105 if err != nil { 106 return nil, errors.Trace(err) 107 } 108 return s.wrapUpgradeOp(op, charmURL), nil 109 } 110 111 func (s *resolverOpFactory) NewAction(id string) (operation.Operation, error) { 112 op, err := s.Factory.NewAction(id) 113 if err != nil { 114 return nil, errors.Trace(err) 115 } 116 f := func(*operation.State) { 117 if s.LocalState.CompletedActions == nil { 118 s.LocalState.CompletedActions = make(map[string]struct{}) 119 } 120 s.LocalState.CompletedActions[id] = struct{}{} 121 s.LocalState.CompletedActions = trimCompletedActions(s.RemoteState.Actions, s.LocalState.CompletedActions) 122 } 123 op = onCommitWrapper{op, f} 124 return op, nil 125 } 126 127 func trimCompletedActions(pendingActions []string, completedActions map[string]struct{}) map[string]struct{} { 128 newCompletedActions := map[string]struct{}{} 129 for _, pendingAction := range pendingActions { 130 if _, ok := completedActions[pendingAction]; ok { 131 newCompletedActions[pendingAction] = struct{}{} 132 } 133 } 134 return newCompletedActions 135 } 136 137 func (s *resolverOpFactory) wrapUpgradeOp(op operation.Operation, charmURL *charm.URL) operation.Operation { 138 charmModifiedVersion := s.RemoteState.CharmModifiedVersion 139 return onCommitWrapper{op, func(*operation.State) { 140 s.LocalState.CharmURL = charmURL 141 s.LocalState.Restart = true 142 s.LocalState.Conflicted = false 143 s.LocalState.CharmModifiedVersion = charmModifiedVersion 144 s.LocalState.UpgradeCharmProfileStatus = lxdprofile.EmptyStatus 145 }} 146 } 147 148 func (s *resolverOpFactory) wrapHookOp(op operation.Operation, info hook.Info) operation.Operation { 149 switch info.Kind { 150 case hooks.PreSeriesUpgrade: 151 op = onPrepareWrapper{op, func() { 152 //on prepare the local status should be made to reflect 153 //that the upgrade process for this united has started. 154 s.LocalState.UpgradeSeriesStatus = s.RemoteState.UpgradeSeriesStatus 155 }} 156 op = onCommitWrapper{op, func(*operation.State) { 157 // on commit, the local status should indicate the hook 158 // has completed. The remote status should already 159 // indicate completion. We sync the states here. 160 s.LocalState.UpgradeSeriesStatus = model.UpgradeSeriesPrepareCompleted 161 }} 162 case hooks.PostSeriesUpgrade: 163 op = onPrepareWrapper{op, func() { 164 s.LocalState.UpgradeSeriesStatus = s.RemoteState.UpgradeSeriesStatus 165 }} 166 op = onCommitWrapper{op, func(*operation.State) { 167 s.LocalState.UpgradeSeriesStatus = model.UpgradeSeriesCompleted 168 }} 169 case hooks.ConfigChanged: 170 configHash := s.RemoteState.ConfigHash 171 trustHash := s.RemoteState.TrustHash 172 addressesHash := s.RemoteState.AddressesHash 173 op = onCommitWrapper{op, func(state *operation.State) { 174 if state != nil { 175 // Assign these on the operation.State so it gets 176 // written into the state file on disk. 177 state.ConfigHash = configHash 178 state.TrustHash = trustHash 179 state.AddressesHash = addressesHash 180 } 181 }} 182 case hooks.LeaderSettingsChanged: 183 v := s.RemoteState.LeaderSettingsVersion 184 op = onCommitWrapper{op, func(*operation.State) { 185 s.LocalState.LeaderSettingsVersion = v 186 }} 187 } 188 189 charmModifiedVersion := s.RemoteState.CharmModifiedVersion 190 updateStatusVersion := s.RemoteState.UpdateStatusVersion 191 op = onCommitWrapper{op, func(*operation.State) { 192 // Update UpdateStatusVersion so that the update-status 193 // hook only fires after the next timer. 194 s.LocalState.UpdateStatusVersion = updateStatusVersion 195 s.LocalState.CharmModifiedVersion = charmModifiedVersion 196 }} 197 198 retryHookVersion := s.RemoteState.RetryHookVersion 199 op = onPrepareWrapper{op, func() { 200 // Update RetryHookVersion so that we don't attempt to 201 // retry a hook more than once between timers signals. 202 // 203 // We need to do this in Prepare, rather than Commit, 204 // in case the retried hook fails. 205 s.LocalState.RetryHookVersion = retryHookVersion 206 }} 207 return op 208 } 209 210 type onCommitWrapper struct { 211 operation.Operation 212 onCommit func(*operation.State) 213 } 214 215 func (op onCommitWrapper) Commit(state operation.State) (*operation.State, error) { 216 st, err := op.Operation.Commit(state) 217 if err != nil { 218 return nil, err 219 } 220 op.onCommit(st) 221 return st, nil 222 } 223 224 type onPrepareWrapper struct { 225 operation.Operation 226 onPrepare func() 227 } 228 229 func (op onPrepareWrapper) Prepare(state operation.State) (*operation.State, error) { 230 st, err := op.Operation.Prepare(state) 231 if err != nil { 232 return nil, err 233 } 234 op.onPrepare() 235 return st, nil 236 }