github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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 return onCommitWrapper{op, func() { 103 s.LocalState.CharmURL = charmURL 104 s.LocalState.Restart = true 105 s.LocalState.Conflicted = false 106 }} 107 } 108 109 func (s *resolverOpFactory) wrapHookOp(op operation.Operation, info hook.Info) operation.Operation { 110 switch info.Kind { 111 case hooks.ConfigChanged: 112 v := s.RemoteState.ConfigVersion 113 op = onCommitWrapper{op, func() { 114 s.LocalState.ConfigVersion = v 115 }} 116 case hooks.LeaderSettingsChanged: 117 v := s.RemoteState.LeaderSettingsVersion 118 op = onCommitWrapper{op, func() { 119 s.LocalState.LeaderSettingsVersion = v 120 }} 121 } 122 // No matter what has finished running, we reset the UpdateStatusVersion so that 123 // the update-status hook only fires after the next timer. 124 v := s.RemoteState.UpdateStatusVersion 125 return onCommitWrapper{op, func() { 126 s.LocalState.UpdateStatusVersion = v 127 }} 128 } 129 130 type onCommitWrapper struct { 131 operation.Operation 132 f func() 133 } 134 135 func (op onCommitWrapper) Commit(state operation.State) (*operation.State, error) { 136 st, err := op.Operation.Commit(state) 137 if err != nil { 138 return nil, err 139 } 140 onCommit(op) 141 return st, nil 142 } 143 144 func onCommit(op operation.Operation) { 145 if wrapper, ok := op.(onCommitWrapper); ok { 146 wrapper.f() 147 onCommit(wrapper.Operation) 148 } 149 }