github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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-unstable" 10 "gopkg.in/juju/charm.v6-unstable/hooks" 11 12 "github.com/juju/juju/worker/uniter/hook" 13 "github.com/juju/juju/worker/uniter/operation" 14 "github.com/juju/juju/worker/uniter/remotestate" 15 ) 16 17 var logger = loggo.GetLogger("juju.worker.uniter.resolver") 18 19 // resolverOpFactory wraps an operation.Factory such that skips that affect 20 // local state will, when committed, update the embedded LocalState struct 21 // to reflect the change made by the operation. 22 // 23 // The wrapped operations embed information specific to the remote state 24 // snapshot that was used to create the operation. Thus, remote state changes 25 // observed between the time the operation was created and committed do not 26 // affect the operation; and the local state change will not prevent further 27 // operations from being enqueued to achieve the new remote state. 28 type resolverOpFactory struct { 29 operation.Factory 30 31 LocalState *LocalState 32 RemoteState remotestate.Snapshot 33 } 34 35 func (s *resolverOpFactory) NewRunHook(info hook.Info) (operation.Operation, error) { 36 op, err := s.Factory.NewRunHook(info) 37 if err != nil { 38 return nil, errors.Trace(err) 39 } 40 return s.wrapHookOp(op, info), nil 41 } 42 43 func (s *resolverOpFactory) NewSkipHook(info hook.Info) (operation.Operation, error) { 44 op, err := s.Factory.NewSkipHook(info) 45 if err != nil { 46 return nil, errors.Trace(err) 47 } 48 return s.wrapHookOp(op, info), nil 49 } 50 51 func (s *resolverOpFactory) NewUpgrade(charmURL *charm.URL) (operation.Operation, error) { 52 op, err := s.Factory.NewUpgrade(charmURL) 53 if err != nil { 54 return nil, errors.Trace(err) 55 } 56 return s.wrapUpgradeOp(op, charmURL), nil 57 } 58 59 func (s *resolverOpFactory) NewRevertUpgrade(charmURL *charm.URL) (operation.Operation, error) { 60 op, err := s.Factory.NewRevertUpgrade(charmURL) 61 if err != nil { 62 return nil, errors.Trace(err) 63 } 64 return s.wrapUpgradeOp(op, charmURL), nil 65 } 66 67 func (s *resolverOpFactory) NewResolvedUpgrade(charmURL *charm.URL) (operation.Operation, error) { 68 op, err := s.Factory.NewResolvedUpgrade(charmURL) 69 if err != nil { 70 return nil, errors.Trace(err) 71 } 72 return s.wrapUpgradeOp(op, charmURL), nil 73 } 74 75 func (s *resolverOpFactory) NewAction(id string) (operation.Operation, error) { 76 op, err := s.Factory.NewAction(id) 77 if err != nil { 78 return nil, errors.Trace(err) 79 } 80 f := func() { 81 if s.LocalState.CompletedActions == nil { 82 s.LocalState.CompletedActions = make(map[string]struct{}) 83 } 84 s.LocalState.CompletedActions[id] = struct{}{} 85 s.LocalState.CompletedActions = trimCompletedActions(s.RemoteState.Actions, s.LocalState.CompletedActions) 86 } 87 op = onCommitWrapper{op, f} 88 return op, nil 89 } 90 91 func trimCompletedActions(pendingActions []string, completedActions map[string]struct{}) map[string]struct{} { 92 newCompletedActions := map[string]struct{}{} 93 for _, pendingAction := range pendingActions { 94 if _, ok := completedActions[pendingAction]; ok { 95 newCompletedActions[pendingAction] = struct{}{} 96 } 97 } 98 return newCompletedActions 99 } 100 101 func (s *resolverOpFactory) wrapUpgradeOp(op operation.Operation, charmURL *charm.URL) operation.Operation { 102 charmModifiedVersion := s.RemoteState.CharmModifiedVersion 103 return onCommitWrapper{op, func() { 104 s.LocalState.CharmURL = charmURL 105 s.LocalState.Restart = true 106 s.LocalState.Conflicted = false 107 s.LocalState.CharmModifiedVersion = charmModifiedVersion 108 }} 109 } 110 111 func (s *resolverOpFactory) wrapHookOp(op operation.Operation, info hook.Info) operation.Operation { 112 switch info.Kind { 113 case hooks.ConfigChanged: 114 v := s.RemoteState.ConfigVersion 115 op = onCommitWrapper{op, func() { 116 s.LocalState.ConfigVersion = v 117 }} 118 case hooks.LeaderSettingsChanged: 119 v := s.RemoteState.LeaderSettingsVersion 120 op = onCommitWrapper{op, func() { 121 s.LocalState.LeaderSettingsVersion = v 122 }} 123 } 124 125 charmModifiedVersion := s.RemoteState.CharmModifiedVersion 126 updateStatusVersion := s.RemoteState.UpdateStatusVersion 127 op = onCommitWrapper{op, func() { 128 // Update UpdateStatusVersion so that the update-status 129 // hook only fires after the next timer. 130 s.LocalState.UpdateStatusVersion = updateStatusVersion 131 s.LocalState.CharmModifiedVersion = charmModifiedVersion 132 }} 133 134 retryHookVersion := s.RemoteState.RetryHookVersion 135 op = onPrepareWrapper{op, func() { 136 // Update RetryHookVersion so that we don't attempt to 137 // retry a hook more than once between timers signals. 138 // 139 // We need to do this in Prepare, rather than Commit, 140 // in case the retried hook fails. 141 s.LocalState.RetryHookVersion = retryHookVersion 142 }} 143 return op 144 } 145 146 type onCommitWrapper struct { 147 operation.Operation 148 onCommit func() 149 } 150 151 func (op onCommitWrapper) Commit(state operation.State) (*operation.State, error) { 152 st, err := op.Operation.Commit(state) 153 if err != nil { 154 return nil, err 155 } 156 op.onCommit() 157 return st, nil 158 } 159 160 type onPrepareWrapper struct { 161 operation.Operation 162 onPrepare func() 163 } 164 165 func (op onPrepareWrapper) Prepare(state operation.State) (*operation.State, error) { 166 st, err := op.Operation.Prepare(state) 167 if err != nil { 168 return nil, err 169 } 170 op.onPrepare() 171 return st, nil 172 }