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  }