github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/uniter/resolver_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package uniter_test 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/testing" 9 jc "github.com/juju/testing/checkers" 10 gc "gopkg.in/check.v1" 11 "gopkg.in/juju/charm.v6-unstable" 12 "gopkg.in/juju/charm.v6-unstable/hooks" 13 "gopkg.in/juju/names.v2" 14 15 "github.com/juju/juju/apiserver/params" 16 "github.com/juju/juju/worker/uniter" 17 uniteractions "github.com/juju/juju/worker/uniter/actions" 18 "github.com/juju/juju/worker/uniter/hook" 19 "github.com/juju/juju/worker/uniter/leadership" 20 "github.com/juju/juju/worker/uniter/operation" 21 "github.com/juju/juju/worker/uniter/relation" 22 "github.com/juju/juju/worker/uniter/remotestate" 23 "github.com/juju/juju/worker/uniter/resolver" 24 "github.com/juju/juju/worker/uniter/storage" 25 ) 26 27 type resolverSuite struct { 28 stub testing.Stub 29 charmModifiedVersion int 30 charmURL *charm.URL 31 remoteState remotestate.Snapshot 32 opFactory operation.Factory 33 resolver resolver.Resolver 34 resolverConfig uniter.ResolverConfig 35 36 clearResolved func() error 37 reportHookError func(hook.Info) error 38 } 39 40 var _ = gc.Suite(&resolverSuite{}) 41 42 func (s *resolverSuite) SetUpTest(c *gc.C) { 43 s.stub = testing.Stub{} 44 s.charmURL = charm.MustParseURL("cs:precise/mysql-2") 45 s.remoteState = remotestate.Snapshot{ 46 CharmModifiedVersion: s.charmModifiedVersion, 47 CharmURL: s.charmURL, 48 } 49 s.opFactory = operation.NewFactory(operation.FactoryParams{}) 50 51 attachments, err := storage.NewAttachments(&dummyStorageAccessor{}, names.NewUnitTag("u/0"), c.MkDir(), nil) 52 c.Assert(err, jc.ErrorIsNil) 53 54 s.clearResolved = func() error { 55 return errors.New("unexpected resolved") 56 } 57 58 s.reportHookError = func(hook.Info) error { 59 return errors.New("unexpected report hook error") 60 } 61 62 s.resolverConfig = uniter.ResolverConfig{ 63 ClearResolved: func() error { return s.clearResolved() }, 64 ReportHookError: func(info hook.Info) error { return s.reportHookError(info) }, 65 StartRetryHookTimer: func() { s.stub.AddCall("StartRetryHookTimer") }, 66 StopRetryHookTimer: func() { s.stub.AddCall("StopRetryHookTimer") }, 67 ShouldRetryHooks: true, 68 Leadership: leadership.NewResolver(), 69 Actions: uniteractions.NewResolver(), 70 Relations: relation.NewRelationsResolver(&dummyRelations{}), 71 Storage: storage.NewResolver(attachments), 72 Commands: nopResolver{}, 73 } 74 75 s.resolver = uniter.NewUniterResolver(s.resolverConfig) 76 } 77 78 // TestStartedNotInstalled tests whether the Started flag overrides the 79 // Installed flag being unset, in the event of an unexpected inconsistency in 80 // local state. 81 func (s *resolverSuite) TestStartedNotInstalled(c *gc.C) { 82 localState := resolver.LocalState{ 83 CharmModifiedVersion: s.charmModifiedVersion, 84 CharmURL: s.charmURL, 85 State: operation.State{ 86 Kind: operation.Continue, 87 Installed: false, 88 Started: true, 89 }, 90 } 91 _, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 92 c.Assert(err, gc.Equals, resolver.ErrNoOperation) 93 } 94 95 // TestNotStartedNotInstalled tests whether the next operation for an 96 // uninstalled local state is an install hook operation. 97 func (s *resolverSuite) TestNotStartedNotInstalled(c *gc.C) { 98 localState := resolver.LocalState{ 99 CharmModifiedVersion: s.charmModifiedVersion, 100 CharmURL: s.charmURL, 101 State: operation.State{ 102 Kind: operation.Continue, 103 Installed: false, 104 Started: false, 105 }, 106 } 107 op, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 108 c.Assert(err, jc.ErrorIsNil) 109 c.Assert(op.String(), gc.Equals, "run install hook") 110 } 111 112 func (s *resolverSuite) TestHookErrorDoesNotStartRetryTimerIfShouldRetryFalse(c *gc.C) { 113 s.resolverConfig.ShouldRetryHooks = false 114 s.resolver = uniter.NewUniterResolver(s.resolverConfig) 115 s.reportHookError = func(hook.Info) error { return nil } 116 localState := resolver.LocalState{ 117 CharmURL: s.charmURL, 118 State: operation.State{ 119 Kind: operation.RunHook, 120 Step: operation.Pending, 121 Installed: true, 122 Started: true, 123 Hook: &hook.Info{ 124 Kind: hooks.ConfigChanged, 125 }, 126 }, 127 } 128 // Run the resolver; we should not attempt a hook retry 129 _, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 130 c.Assert(err, gc.Equals, resolver.ErrNoOperation) 131 s.stub.CheckNoCalls(c) 132 } 133 134 func (s *resolverSuite) TestHookErrorStartRetryTimer(c *gc.C) { 135 s.reportHookError = func(hook.Info) error { return nil } 136 localState := resolver.LocalState{ 137 CharmModifiedVersion: s.charmModifiedVersion, 138 CharmURL: s.charmURL, 139 State: operation.State{ 140 Kind: operation.RunHook, 141 Step: operation.Pending, 142 Installed: true, 143 Started: true, 144 Hook: &hook.Info{ 145 Kind: hooks.ConfigChanged, 146 }, 147 }, 148 } 149 // Run the resolver twice; we should start the hook retry 150 // timer on the first time through, no change on the second. 151 _, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 152 c.Assert(err, gc.Equals, resolver.ErrNoOperation) 153 s.stub.CheckCallNames(c, "StartRetryHookTimer") 154 155 _, err = s.resolver.NextOp(localState, s.remoteState, s.opFactory) 156 c.Assert(err, gc.Equals, resolver.ErrNoOperation) 157 s.stub.CheckCallNames(c, "StartRetryHookTimer") // no change 158 } 159 160 func (s *resolverSuite) TestHookErrorStartRetryTimerAgain(c *gc.C) { 161 s.reportHookError = func(hook.Info) error { return nil } 162 localState := resolver.LocalState{ 163 CharmModifiedVersion: s.charmModifiedVersion, 164 CharmURL: s.charmURL, 165 State: operation.State{ 166 Kind: operation.RunHook, 167 Step: operation.Pending, 168 Installed: true, 169 Started: true, 170 Hook: &hook.Info{ 171 Kind: hooks.ConfigChanged, 172 }, 173 }, 174 } 175 176 _, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 177 c.Assert(err, gc.Equals, resolver.ErrNoOperation) 178 s.stub.CheckCallNames(c, "StartRetryHookTimer") 179 180 s.remoteState.RetryHookVersion = 1 181 op, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 182 c.Assert(err, jc.ErrorIsNil) 183 c.Assert(op.String(), gc.Equals, "run config-changed hook") 184 s.stub.CheckCallNames(c, "StartRetryHookTimer") // no change 185 localState.RetryHookVersion = 1 186 187 _, err = s.resolver.NextOp(localState, s.remoteState, s.opFactory) 188 c.Assert(err, gc.Equals, resolver.ErrNoOperation) 189 s.stub.CheckCallNames(c, "StartRetryHookTimer", "StartRetryHookTimer") 190 } 191 192 func (s *resolverSuite) TestResolvedRetryHooksStopRetryTimer(c *gc.C) { 193 // Resolving a failed hook should stop the retry timer. 194 s.testResolveHookErrorStopRetryTimer(c, params.ResolvedRetryHooks) 195 } 196 197 func (s *resolverSuite) TestResolvedNoHooksStopRetryTimer(c *gc.C) { 198 // Resolving a failed hook should stop the retry timer. 199 s.testResolveHookErrorStopRetryTimer(c, params.ResolvedNoHooks) 200 } 201 202 func (s *resolverSuite) testResolveHookErrorStopRetryTimer(c *gc.C, mode params.ResolvedMode) { 203 s.stub.ResetCalls() 204 s.clearResolved = func() error { return nil } 205 s.reportHookError = func(hook.Info) error { return nil } 206 localState := resolver.LocalState{ 207 CharmModifiedVersion: s.charmModifiedVersion, 208 CharmURL: s.charmURL, 209 State: operation.State{ 210 Kind: operation.RunHook, 211 Step: operation.Pending, 212 Installed: true, 213 Started: true, 214 Hook: &hook.Info{ 215 Kind: hooks.ConfigChanged, 216 }, 217 }, 218 } 219 220 _, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 221 c.Assert(err, gc.Equals, resolver.ErrNoOperation) 222 s.stub.CheckCallNames(c, "StartRetryHookTimer") 223 224 s.remoteState.ResolvedMode = mode 225 _, err = s.resolver.NextOp(localState, s.remoteState, s.opFactory) 226 c.Assert(err, jc.ErrorIsNil) 227 s.stub.CheckCallNames(c, "StartRetryHookTimer", "StopRetryHookTimer") 228 } 229 230 func (s *resolverSuite) TestRunHookStopRetryTimer(c *gc.C) { 231 s.reportHookError = func(hook.Info) error { return nil } 232 localState := resolver.LocalState{ 233 CharmModifiedVersion: s.charmModifiedVersion, 234 CharmURL: s.charmURL, 235 State: operation.State{ 236 Kind: operation.RunHook, 237 Step: operation.Pending, 238 Installed: true, 239 Started: true, 240 Hook: &hook.Info{ 241 Kind: hooks.ConfigChanged, 242 }, 243 }, 244 } 245 246 _, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 247 c.Assert(err, gc.Equals, resolver.ErrNoOperation) 248 s.stub.CheckCallNames(c, "StartRetryHookTimer") 249 250 localState.Kind = operation.Continue 251 _, err = s.resolver.NextOp(localState, s.remoteState, s.opFactory) 252 c.Assert(err, gc.Equals, resolver.ErrNoOperation) 253 s.stub.CheckCallNames(c, "StartRetryHookTimer", "StopRetryHookTimer") 254 }