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

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package commands
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"fmt"
    10  
    11  	"github.com/juju/cmd"
    12  	"github.com/juju/cmd/cmdtesting"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  	goyaml "gopkg.in/yaml.v2"
    16  
    17  	"github.com/juju/juju/apiserver/common"
    18  	"github.com/juju/juju/apiserver/params"
    19  	"github.com/juju/juju/cmd/modelcmd"
    20  	"github.com/juju/juju/core/constraints"
    21  	"github.com/juju/juju/core/instance"
    22  	"github.com/juju/juju/juju/testing"
    23  	"github.com/juju/juju/network"
    24  	"github.com/juju/juju/state"
    25  	coretesting "github.com/juju/juju/testing"
    26  	"github.com/juju/juju/testing/factory"
    27  )
    28  
    29  type EnableHASuite struct {
    30  	// TODO (cherylj) change this back to a FakeJujuXDGDataHomeSuite to
    31  	// remove the mongo dependency once ensure-availability is
    32  	// moved under a supercommand again.
    33  	testing.JujuConnSuite
    34  	fake *fakeHAClient
    35  }
    36  
    37  // invalidNumServers is a number of controllers that would
    38  // never be generated by the enable-ha command.
    39  const invalidNumServers = -2
    40  
    41  func (s *EnableHASuite) SetUpTest(c *gc.C) {
    42  	s.JujuConnSuite.SetUpTest(c)
    43  
    44  	// Initialize numControllers to an invalid number to validate
    45  	// that enable-ha doesn't call into the API when its
    46  	// pre-checks fail
    47  	s.fake = &fakeHAClient{numControllers: invalidNumServers}
    48  }
    49  
    50  type fakeHAClient struct {
    51  	numControllers int
    52  	cons           constraints.Value
    53  	err            error
    54  	placement      []string
    55  	result         params.ControllersChanges
    56  }
    57  
    58  func (f *fakeHAClient) Close() error {
    59  	return nil
    60  }
    61  
    62  func (f *fakeHAClient) EnableHA(numControllers int, cons constraints.Value, placement []string) (
    63  	params.ControllersChanges, error,
    64  ) {
    65  	f.numControllers = numControllers
    66  	f.cons = cons
    67  	f.placement = placement
    68  
    69  	if f.err != nil {
    70  		return f.result, f.err
    71  	}
    72  
    73  	if numControllers == 1 {
    74  		return f.result, nil
    75  	}
    76  
    77  	// In the real HAClient, specifying a numControllers value of 0
    78  	// indicates that the default value (3) should be used
    79  	if numControllers == 0 {
    80  		numControllers = 3
    81  	}
    82  
    83  	f.result.Maintained = append(f.result.Maintained, "machine-0")
    84  
    85  	for _, p := range placement {
    86  		m, err := instance.ParsePlacement(p)
    87  		if err == nil && m.Scope == instance.MachineScope {
    88  			f.result.Converted = append(f.result.Converted, "machine-"+m.Directive)
    89  		}
    90  	}
    91  
    92  	// We may need to pretend that we added some machines.
    93  	for i := len(f.result.Converted) + 1; i < numControllers; i++ {
    94  		f.result.Added = append(f.result.Added, fmt.Sprintf("machine-%d", i))
    95  	}
    96  
    97  	return f.result, nil
    98  }
    99  
   100  var _ = gc.Suite(&EnableHASuite{})
   101  
   102  func (s *EnableHASuite) runEnableHA(c *gc.C, args ...string) (*cmd.Context, error) {
   103  	command := &enableHACommand{newHAClientFunc: func() (MakeHAClient, error) { return s.fake, nil }}
   104  	return cmdtesting.RunCommand(c, modelcmd.WrapController(command), args...)
   105  }
   106  
   107  func (s *EnableHASuite) TestEnableHA(c *gc.C) {
   108  	ctx, err := s.runEnableHA(c, "-n", "1")
   109  	c.Assert(err, jc.ErrorIsNil)
   110  	c.Assert(cmdtesting.Stdout(ctx), gc.Equals, "\n")
   111  
   112  	c.Assert(s.fake.numControllers, gc.Equals, 1)
   113  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   114  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   115  }
   116  
   117  func (s *EnableHASuite) TestBlockEnableHA(c *gc.C) {
   118  	s.fake.err = common.OperationBlockedError("TestBlockEnableHA")
   119  	_, err := s.runEnableHA(c, "-n", "1")
   120  	coretesting.AssertOperationWasBlocked(c, err, ".*TestBlockEnableHA.*")
   121  }
   122  
   123  func (s *EnableHASuite) TestEnableHAFormatYaml(c *gc.C) {
   124  	expected := map[string][]string{
   125  		"maintained": {"0"},
   126  		"added":      {"1", "2"},
   127  	}
   128  
   129  	ctx, err := s.runEnableHA(c, "-n", "3", "--format", "yaml")
   130  	c.Assert(err, jc.ErrorIsNil)
   131  
   132  	c.Assert(s.fake.numControllers, gc.Equals, 3)
   133  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   134  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   135  
   136  	var result map[string][]string
   137  	err = goyaml.Unmarshal(ctx.Stdout.(*bytes.Buffer).Bytes(), &result)
   138  	c.Assert(err, jc.ErrorIsNil)
   139  	c.Assert(result, gc.DeepEquals, expected)
   140  }
   141  
   142  func (s *EnableHASuite) TestEnableHAFormatJson(c *gc.C) {
   143  	expected := map[string][]string{
   144  		"maintained": {"0"},
   145  		"added":      {"1", "2"},
   146  	}
   147  
   148  	ctx, err := s.runEnableHA(c, "-n", "3", "--format", "json")
   149  	c.Assert(err, jc.ErrorIsNil)
   150  
   151  	c.Assert(s.fake.numControllers, gc.Equals, 3)
   152  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   153  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   154  
   155  	var result map[string][]string
   156  	err = json.Unmarshal(ctx.Stdout.(*bytes.Buffer).Bytes(), &result)
   157  	c.Assert(err, jc.ErrorIsNil)
   158  	c.Assert(result, gc.DeepEquals, expected)
   159  }
   160  
   161  func (s *EnableHASuite) TestEnableHAWithFive(c *gc.C) {
   162  	// Also test with -n 5 to validate numbers other than 1 and 3
   163  	ctx, err := s.runEnableHA(c, "-n", "5")
   164  	c.Assert(err, jc.ErrorIsNil)
   165  	c.Assert(cmdtesting.Stdout(ctx), gc.Equals,
   166  		"maintaining machines: 0\n"+
   167  			"adding machines: 1, 2, 3, 4\n\n")
   168  
   169  	c.Assert(s.fake.numControllers, gc.Equals, 5)
   170  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   171  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   172  }
   173  
   174  func (s *EnableHASuite) TestEnableHAWithConstraints(c *gc.C) {
   175  	ctx, err := s.runEnableHA(c, "--constraints", "mem=4G", "-n", "3")
   176  	c.Assert(err, jc.ErrorIsNil)
   177  	c.Assert(cmdtesting.Stdout(ctx), gc.Equals,
   178  		"maintaining machines: 0\n"+
   179  			"adding machines: 1, 2\n\n")
   180  
   181  	c.Assert(s.fake.numControllers, gc.Equals, 3)
   182  	expectedCons := constraints.MustParse("mem=4G")
   183  	c.Assert(s.fake.cons, gc.DeepEquals, expectedCons)
   184  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   185  }
   186  
   187  func (s *EnableHASuite) TestEnableHAWithPlacement(c *gc.C) {
   188  	ctx, err := s.runEnableHA(c, "--to", "valid", "-n", "3")
   189  	c.Assert(err, jc.ErrorIsNil)
   190  	c.Assert(cmdtesting.Stdout(ctx), gc.Equals,
   191  		"maintaining machines: 0\n"+
   192  			"adding machines: 1, 2\n\n")
   193  
   194  	c.Assert(s.fake.numControllers, gc.Equals, 3)
   195  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   196  	expectedPlacement := []string{"valid"}
   197  	c.Assert(s.fake.placement, gc.DeepEquals, expectedPlacement)
   198  }
   199  
   200  func (s *EnableHASuite) TestEnableHAErrors(c *gc.C) {
   201  	for _, n := range []int{-1, 2} {
   202  		_, err := s.runEnableHA(c, "-n", fmt.Sprint(n))
   203  		c.Assert(err, gc.ErrorMatches, "must specify a number of controllers odd and non-negative")
   204  	}
   205  
   206  	// Verify that enable-ha didn't call into the API
   207  	c.Assert(s.fake.numControllers, gc.Equals, invalidNumServers)
   208  }
   209  
   210  func (s *EnableHASuite) TestEnableHAAllows0(c *gc.C) {
   211  	// If the number of controllers is specified as "0", the API will
   212  	// then use the default number of 3.
   213  	ctx, err := s.runEnableHA(c, "-n", "0")
   214  	c.Assert(err, jc.ErrorIsNil)
   215  	c.Assert(cmdtesting.Stdout(ctx), gc.Equals,
   216  		"maintaining machines: 0\n"+
   217  			"adding machines: 1, 2\n\n")
   218  
   219  	c.Assert(s.fake.numControllers, gc.Equals, 0)
   220  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   221  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   222  }
   223  
   224  func (s *EnableHASuite) TestEnableHADefaultsTo0(c *gc.C) {
   225  	// If the number of controllers is not specified, we pass in 0 to the
   226  	// API.  The API will then use the default number of 3.
   227  	ctx, err := s.runEnableHA(c)
   228  	c.Assert(err, jc.ErrorIsNil)
   229  	c.Assert(cmdtesting.Stdout(ctx), gc.Equals,
   230  		"maintaining machines: 0\n"+
   231  			"adding machines: 1, 2\n\n")
   232  
   233  	c.Assert(s.fake.numControllers, gc.Equals, 0)
   234  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   235  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   236  }
   237  
   238  func (s *EnableHASuite) TestEnableHAEndToEnd(c *gc.C) {
   239  	m := s.Factory.MakeMachine(c, &factory.MachineParams{
   240  		Jobs: []state.MachineJob{state.JobManageModel},
   241  	})
   242  	err := m.SetMachineAddresses(
   243  		network.NewScopedAddress("127.0.0.1", network.ScopeMachineLocal),
   244  		network.NewScopedAddress("cloud-local0.internal", network.ScopeCloudLocal),
   245  		network.NewScopedAddress("fc00::1", network.ScopePublic),
   246  	)
   247  	c.Assert(err, jc.ErrorIsNil)
   248  
   249  	ctx, err := cmdtesting.RunCommand(c, newEnableHACommand(), "-n", "3")
   250  	c.Assert(err, jc.ErrorIsNil)
   251  
   252  	c.Check(cmdtesting.Stdout(ctx), gc.Equals, `
   253  maintaining machines: 0
   254  adding machines: 1, 2
   255  
   256  `[1:])
   257  }
   258  
   259  func (s *EnableHASuite) TestEnableHAToExisting(c *gc.C) {
   260  	ctx, err := s.runEnableHA(c, "--to", "1,2")
   261  	c.Assert(err, jc.ErrorIsNil)
   262  	c.Check(cmdtesting.Stdout(ctx), gc.Equals, `
   263  maintaining machines: 0
   264  converting machines: 1, 2
   265  
   266  `[1:])
   267  
   268  	c.Check(s.fake.numControllers, gc.Equals, 0)
   269  	c.Check(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   270  	c.Check(len(s.fake.placement), gc.Equals, 2)
   271  }
   272  
   273  func (s *EnableHASuite) TestEnableHADisallowsSeries(c *gc.C) {
   274  	// We do not allow --series as an argument. This test ensures it is not
   275  	// inadvertently added back.
   276  	ctx, err := s.runEnableHA(c, "-n", "0", "--series", "xenian")
   277  	c.Assert(err, gc.ErrorMatches, "option provided but not defined: --series")
   278  	c.Assert(cmdtesting.Stdout(ctx), gc.Equals, "")
   279  }