github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/common/controller_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common
     5  
     6  import (
     7  	"io"
     8  	"time"
     9  
    10  	"github.com/juju/cmd"
    11  	"github.com/juju/cmd/cmdtesting"
    12  	"github.com/juju/errors"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/api"
    17  	"github.com/juju/juju/apiserver/params"
    18  	jujucmd "github.com/juju/juju/cmd"
    19  	"github.com/juju/juju/cmd/modelcmd"
    20  	"github.com/juju/juju/jujuclient/jujuclienttesting"
    21  	"github.com/juju/juju/rpc"
    22  	"github.com/juju/juju/testing"
    23  	"github.com/juju/juju/version"
    24  )
    25  
    26  var _ = gc.Suite(&controllerSuite{})
    27  
    28  type controllerSuite struct {
    29  	testing.BaseSuite
    30  	mockBlockClient *mockBlockClient
    31  }
    32  
    33  func (s *controllerSuite) SetUpTest(c *gc.C) {
    34  	s.mockBlockClient = &mockBlockClient{}
    35  	s.PatchValue(&blockAPI, func(*modelcmd.ModelCommandBase) (listBlocksAPI, error) {
    36  		err := s.mockBlockClient.loginError
    37  		if err != nil {
    38  			s.mockBlockClient.loginError = nil
    39  			return nil, err
    40  		}
    41  		if s.mockBlockClient.discoveringSpacesError > 0 {
    42  			s.mockBlockClient.discoveringSpacesError -= 1
    43  			return nil, errors.New("spaces are still being discovered")
    44  		}
    45  		return s.mockBlockClient, nil
    46  	})
    47  }
    48  
    49  type mockBlockClient struct {
    50  	retryCount             int
    51  	numRetries             int
    52  	discoveringSpacesError int
    53  	loginError             error
    54  }
    55  
    56  var errOther = errors.New("other error")
    57  
    58  func (c *mockBlockClient) List() ([]params.Block, error) {
    59  	c.retryCount += 1
    60  	if c.retryCount == 5 {
    61  		return nil, &rpc.RequestError{Message: params.CodeUpgradeInProgress, Code: params.CodeUpgradeInProgress}
    62  	}
    63  	if c.numRetries < 0 {
    64  		return nil, errOther
    65  	}
    66  	if c.retryCount < c.numRetries {
    67  		return nil, &rpc.RequestError{Message: params.CodeUpgradeInProgress, Code: params.CodeUpgradeInProgress}
    68  	}
    69  	return []params.Block{}, nil
    70  }
    71  
    72  func (c *mockBlockClient) Close() error {
    73  	return nil
    74  }
    75  
    76  func (s *controllerSuite) TestWaitForAgentAPIReadyRetries(c *gc.C) {
    77  	s.PatchValue(&bootstrapReadyPollDelay, 1*time.Millisecond)
    78  	s.PatchValue(&bootstrapReadyPollCount, 5)
    79  	defaultSeriesVersion := version.Current
    80  	// Force a dev version by having a non zero build number.
    81  	// This is because we have not uploaded any tools and auto
    82  	// upload is only enabled for dev versions.
    83  	defaultSeriesVersion.Build = 1234
    84  	s.PatchValue(&version.Current, defaultSeriesVersion)
    85  	for _, t := range []struct {
    86  		numRetries int
    87  		err        error
    88  	}{
    89  		{0, nil}, // agent ready immediately
    90  		{2, nil}, // agent ready after 2 polls
    91  		{6, &rpc.RequestError{
    92  			Message: params.CodeUpgradeInProgress,
    93  			Code:    params.CodeUpgradeInProgress,
    94  		}}, // agent ready after 6 polls but that's too long
    95  		{-1, errOther}, // another error is returned
    96  	} {
    97  		s.mockBlockClient.numRetries = t.numRetries
    98  		s.mockBlockClient.retryCount = 0
    99  		runInCommand(c, func(ctx *cmd.Context, base *modelcmd.ModelCommandBase) {
   100  			err := WaitForAgentInitialisation(ctx, base, "controller", "default")
   101  			c.Check(errors.Cause(err), gc.DeepEquals, t.err)
   102  		})
   103  		expectedRetries := t.numRetries
   104  		if t.numRetries <= 0 {
   105  			expectedRetries = 1
   106  		}
   107  		// Only retry maximum of bootstrapReadyPollCount times.
   108  		if expectedRetries > 5 {
   109  			expectedRetries = 5
   110  		}
   111  		c.Check(s.mockBlockClient.retryCount, gc.Equals, expectedRetries)
   112  	}
   113  }
   114  
   115  func (s *controllerSuite) TestWaitForAgentAPIReadyWaitsForSpaceDiscovery(c *gc.C) {
   116  	s.mockBlockClient.discoveringSpacesError = 2
   117  
   118  	runInCommand(c, func(ctx *cmd.Context, base *modelcmd.ModelCommandBase) {
   119  		err := WaitForAgentInitialisation(ctx, base, "controller", "default")
   120  		c.Check(err, jc.ErrorIsNil)
   121  	})
   122  	c.Assert(s.mockBlockClient.discoveringSpacesError, gc.Equals, 0)
   123  }
   124  
   125  func (s *controllerSuite) TestWaitForAgentAPIReadyRetriesWithOpenEOFErr(c *gc.C) {
   126  	s.mockBlockClient.numRetries = 0
   127  	s.mockBlockClient.retryCount = 0
   128  	s.mockBlockClient.loginError = io.EOF
   129  
   130  	runInCommand(c, func(ctx *cmd.Context, base *modelcmd.ModelCommandBase) {
   131  		err := WaitForAgentInitialisation(ctx, base, "controller", "default")
   132  		c.Check(err, jc.ErrorIsNil)
   133  	})
   134  	c.Check(s.mockBlockClient.retryCount, gc.Equals, 1)
   135  }
   136  
   137  func (s *controllerSuite) TestWaitForAgentAPIReadyStopsRetriesWithOpenErr(c *gc.C) {
   138  	s.mockBlockClient.numRetries = 0
   139  	s.mockBlockClient.retryCount = 0
   140  	s.mockBlockClient.loginError = errors.NewUnauthorized(nil, "")
   141  	runInCommand(c, func(ctx *cmd.Context, base *modelcmd.ModelCommandBase) {
   142  		err := WaitForAgentInitialisation(ctx, base, "controller", "default")
   143  		c.Check(err, jc.Satisfies, errors.IsUnauthorized)
   144  	})
   145  	c.Check(s.mockBlockClient.retryCount, gc.Equals, 0)
   146  }
   147  
   148  func runInCommand(c *gc.C, run func(ctx *cmd.Context, base *modelcmd.ModelCommandBase)) {
   149  	cmd := &testCommand{
   150  		run: run,
   151  	}
   152  	cmd.SetClientStore(jujuclienttesting.MinimalStore())
   153  	cmd.SetAPIOpen(func(*api.Info, api.DialOpts) (api.Connection, error) {
   154  		return nil, errors.New("no API available")
   155  	})
   156  
   157  	_, err := cmdtesting.RunCommand(c, modelcmd.Wrap(cmd))
   158  	c.Assert(err, jc.ErrorIsNil)
   159  }
   160  
   161  type testCommand struct {
   162  	modelcmd.ModelCommandBase
   163  	run func(ctx *cmd.Context, base *modelcmd.ModelCommandBase)
   164  }
   165  
   166  func (c *testCommand) Run(ctx *cmd.Context) error {
   167  	c.run(ctx, &c.ModelCommandBase)
   168  	return nil
   169  }
   170  
   171  func (c *testCommand) Info() *cmd.Info {
   172  	return jujucmd.Info(&cmd.Info{
   173  		Name: "test",
   174  	})
   175  }