github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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/names" 9 "github.com/juju/testing" 10 jc "github.com/juju/testing/checkers" 11 gc "gopkg.in/check.v1" 12 "gopkg.in/juju/charm.v6-unstable" 13 "gopkg.in/juju/charm.v6-unstable/hooks" 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 FixDeployer: func() error { return nil }, 66 StartRetryHookTimer: func() { s.stub.AddCall("StartRetryHookTimer") }, 67 StopRetryHookTimer: func() { s.stub.AddCall("StopRetryHookTimer") }, 68 ShouldRetryHooks: true, 69 Leadership: leadership.NewResolver(), 70 Actions: uniteractions.NewResolver(), 71 Relations: relation.NewRelationsResolver(&dummyRelations{}), 72 Storage: storage.NewResolver(attachments), 73 Commands: nopResolver{}, 74 } 75 76 s.resolver = uniter.NewUniterResolver(s.resolverConfig) 77 } 78 79 // TestStartedNotInstalled tests whether the Started flag overrides the 80 // Installed flag being unset, in the event of an unexpected inconsistency in 81 // local state. 82 func (s *resolverSuite) TestStartedNotInstalled(c *gc.C) { 83 localState := resolver.LocalState{ 84 CharmModifiedVersion: s.charmModifiedVersion, 85 CharmURL: s.charmURL, 86 State: operation.State{ 87 Kind: operation.Continue, 88 Installed: false, 89 Started: true, 90 }, 91 } 92 _, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 93 c.Assert(err, gc.Equals, resolver.ErrNoOperation) 94 } 95 96 // TestNotStartedNotInstalled tests whether the next operation for an 97 // uninstalled local state is an install hook operation. 98 func (s *resolverSuite) TestNotStartedNotInstalled(c *gc.C) { 99 localState := resolver.LocalState{ 100 CharmModifiedVersion: s.charmModifiedVersion, 101 CharmURL: s.charmURL, 102 State: operation.State{ 103 Kind: operation.Continue, 104 Installed: false, 105 Started: false, 106 }, 107 } 108 op, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 109 c.Assert(err, jc.ErrorIsNil) 110 c.Assert(op.String(), gc.Equals, "run install hook") 111 } 112 113 func (s *resolverSuite) TestHookErrorDoesNotStartRetryTimerIfShouldRetryFalse(c *gc.C) { 114 s.resolverConfig.ShouldRetryHooks = false 115 s.resolver = uniter.NewUniterResolver(s.resolverConfig) 116 s.reportHookError = func(hook.Info) error { return nil } 117 localState := resolver.LocalState{ 118 CharmURL: s.charmURL, 119 State: operation.State{ 120 Kind: operation.RunHook, 121 Step: operation.Pending, 122 Installed: true, 123 Started: true, 124 Hook: &hook.Info{ 125 Kind: hooks.ConfigChanged, 126 }, 127 }, 128 } 129 // Run the resolver; we should not attempt a hook retry 130 _, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 131 c.Assert(err, gc.Equals, resolver.ErrNoOperation) 132 s.stub.CheckNoCalls(c) 133 } 134 135 func (s *resolverSuite) TestHookErrorStartRetryTimer(c *gc.C) { 136 s.reportHookError = func(hook.Info) error { return nil } 137 localState := resolver.LocalState{ 138 CharmModifiedVersion: s.charmModifiedVersion, 139 CharmURL: s.charmURL, 140 State: operation.State{ 141 Kind: operation.RunHook, 142 Step: operation.Pending, 143 Installed: true, 144 Started: true, 145 Hook: &hook.Info{ 146 Kind: hooks.ConfigChanged, 147 }, 148 }, 149 } 150 // Run the resolver twice; we should start the hook retry 151 // timer on the first time through, no change on the second. 152 _, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 153 c.Assert(err, gc.Equals, resolver.ErrNoOperation) 154 s.stub.CheckCallNames(c, "StartRetryHookTimer") 155 156 _, err = s.resolver.NextOp(localState, s.remoteState, s.opFactory) 157 c.Assert(err, gc.Equals, resolver.ErrNoOperation) 158 s.stub.CheckCallNames(c, "StartRetryHookTimer") // no change 159 } 160 161 func (s *resolverSuite) TestHookErrorStartRetryTimerAgain(c *gc.C) { 162 s.reportHookError = func(hook.Info) error { return nil } 163 localState := resolver.LocalState{ 164 CharmModifiedVersion: s.charmModifiedVersion, 165 CharmURL: s.charmURL, 166 State: operation.State{ 167 Kind: operation.RunHook, 168 Step: operation.Pending, 169 Installed: true, 170 Started: true, 171 Hook: &hook.Info{ 172 Kind: hooks.ConfigChanged, 173 }, 174 }, 175 } 176 177 _, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 178 c.Assert(err, gc.Equals, resolver.ErrNoOperation) 179 s.stub.CheckCallNames(c, "StartRetryHookTimer") 180 181 s.remoteState.RetryHookVersion = 1 182 op, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 183 c.Assert(err, jc.ErrorIsNil) 184 c.Assert(op.String(), gc.Equals, "run config-changed hook") 185 s.stub.CheckCallNames(c, "StartRetryHookTimer") // no change 186 localState.RetryHookVersion = 1 187 188 _, err = s.resolver.NextOp(localState, s.remoteState, s.opFactory) 189 c.Assert(err, gc.Equals, resolver.ErrNoOperation) 190 s.stub.CheckCallNames(c, "StartRetryHookTimer", "StartRetryHookTimer") 191 } 192 193 func (s *resolverSuite) TestResolvedRetryHooksStopRetryTimer(c *gc.C) { 194 // Resolving a failed hook should stop the retry timer. 195 s.testResolveHookErrorStopRetryTimer(c, params.ResolvedRetryHooks) 196 } 197 198 func (s *resolverSuite) TestResolvedNoHooksStopRetryTimer(c *gc.C) { 199 // Resolving a failed hook should stop the retry timer. 200 s.testResolveHookErrorStopRetryTimer(c, params.ResolvedNoHooks) 201 } 202 203 func (s *resolverSuite) testResolveHookErrorStopRetryTimer(c *gc.C, mode params.ResolvedMode) { 204 s.stub.ResetCalls() 205 s.clearResolved = func() error { return nil } 206 s.reportHookError = func(hook.Info) error { return nil } 207 localState := resolver.LocalState{ 208 CharmModifiedVersion: s.charmModifiedVersion, 209 CharmURL: s.charmURL, 210 State: operation.State{ 211 Kind: operation.RunHook, 212 Step: operation.Pending, 213 Installed: true, 214 Started: true, 215 Hook: &hook.Info{ 216 Kind: hooks.ConfigChanged, 217 }, 218 }, 219 } 220 221 _, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 222 c.Assert(err, gc.Equals, resolver.ErrNoOperation) 223 s.stub.CheckCallNames(c, "StartRetryHookTimer") 224 225 s.remoteState.ResolvedMode = mode 226 _, err = s.resolver.NextOp(localState, s.remoteState, s.opFactory) 227 c.Assert(err, jc.ErrorIsNil) 228 s.stub.CheckCallNames(c, "StartRetryHookTimer", "StopRetryHookTimer") 229 } 230 231 func (s *resolverSuite) TestRunHookStopRetryTimer(c *gc.C) { 232 s.reportHookError = func(hook.Info) error { return nil } 233 localState := resolver.LocalState{ 234 CharmModifiedVersion: s.charmModifiedVersion, 235 CharmURL: s.charmURL, 236 State: operation.State{ 237 Kind: operation.RunHook, 238 Step: operation.Pending, 239 Installed: true, 240 Started: true, 241 Hook: &hook.Info{ 242 Kind: hooks.ConfigChanged, 243 }, 244 }, 245 } 246 247 _, err := s.resolver.NextOp(localState, s.remoteState, s.opFactory) 248 c.Assert(err, gc.Equals, resolver.ErrNoOperation) 249 s.stub.CheckCallNames(c, "StartRetryHookTimer") 250 251 localState.Kind = operation.Continue 252 _, err = s.resolver.NextOp(localState, s.remoteState, s.opFactory) 253 c.Assert(err, gc.Equals, resolver.ErrNoOperation) 254 s.stub.CheckCallNames(c, "StartRetryHookTimer", "StopRetryHookTimer") 255 }