github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/cmd/juju/environment/ensureavailability_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package environment_test
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"fmt"
    10  	"strings"
    11  
    12  	"github.com/juju/cmd"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  	goyaml "gopkg.in/yaml.v1"
    16  
    17  	"github.com/juju/juju/apiserver/params"
    18  	"github.com/juju/juju/cmd/envcmd"
    19  	"github.com/juju/juju/cmd/juju/environment"
    20  	"github.com/juju/juju/constraints"
    21  	coretesting "github.com/juju/juju/testing"
    22  )
    23  
    24  type EnsureAvailabilitySuite struct {
    25  	coretesting.FakeJujuHomeSuite
    26  	fake *fakeHAClient
    27  }
    28  
    29  // Initialize numStateServers to an invalid number to validate
    30  // that ensure-availability doesn't call into the API when its
    31  // pre-checks fail
    32  const invalidNumServers = -2
    33  
    34  func (s *EnsureAvailabilitySuite) SetUpTest(c *gc.C) {
    35  	s.FakeJujuHomeSuite.SetUpTest(c)
    36  	s.fake = &fakeHAClient{numStateServers: invalidNumServers}
    37  }
    38  
    39  type fakeHAClient struct {
    40  	numStateServers int
    41  	cons            constraints.Value
    42  	err             error
    43  	series          string
    44  	placement       []string
    45  	result          params.StateServersChanges
    46  }
    47  
    48  func (f *fakeHAClient) Close() error {
    49  	return nil
    50  }
    51  
    52  func (f *fakeHAClient) EnsureAvailability(numStateServers int, cons constraints.Value,
    53  	series string, placement []string) (params.StateServersChanges, error) {
    54  
    55  	f.numStateServers = numStateServers
    56  	f.cons = cons
    57  	f.series = series
    58  	f.placement = placement
    59  
    60  	if f.err != nil {
    61  		return f.result, f.err
    62  	}
    63  
    64  	if numStateServers == 1 {
    65  		return f.result, nil
    66  	}
    67  
    68  	// In the real HAClient, specifying a numStateServers value of 0
    69  	// indicates that the default value (3) should be used
    70  	if numStateServers == 0 {
    71  		numStateServers = 3
    72  	}
    73  
    74  	// If numStateServers > 1, we need to pretend that we added some machines
    75  	f.result.Maintained = append(f.result.Maintained, "machine-0")
    76  	for i := 1; i < numStateServers; i++ {
    77  		f.result.Added = append(f.result.Added, fmt.Sprintf("machine-%d", i))
    78  	}
    79  
    80  	return f.result, nil
    81  }
    82  
    83  var _ = gc.Suite(&EnsureAvailabilitySuite{})
    84  
    85  func (s *EnsureAvailabilitySuite) runEnsureAvailability(c *gc.C, args ...string) (*cmd.Context, error) {
    86  	command := environment.NewEnsureAvailabilityCommand(s.fake)
    87  	return coretesting.RunCommand(c, envcmd.Wrap(command), args...)
    88  }
    89  
    90  func (s *EnsureAvailabilitySuite) TestEnsureAvailability(c *gc.C) {
    91  	ctx, err := s.runEnsureAvailability(c, "-n", "1")
    92  	c.Assert(err, jc.ErrorIsNil)
    93  	c.Assert(coretesting.Stdout(ctx), gc.Equals, "")
    94  
    95  	c.Assert(s.fake.numStateServers, gc.Equals, 1)
    96  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
    97  	c.Assert(s.fake.series, gc.Equals, "")
    98  	c.Assert(len(s.fake.placement), gc.Equals, 0)
    99  }
   100  
   101  func (s *EnsureAvailabilitySuite) TestBlockEnsureAvailability(c *gc.C) {
   102  	s.fake.err = &params.Error{Code: params.CodeOperationBlocked}
   103  	_, err := s.runEnsureAvailability(c, "-n", "1")
   104  	c.Assert(err, gc.ErrorMatches, cmd.ErrSilent.Error())
   105  
   106  	// msg is logged
   107  	stripped := strings.Replace(c.GetTestLog(), "\n", "", -1)
   108  	c.Check(stripped, gc.Matches, ".*To unblock changes.*")
   109  }
   110  
   111  func (s *EnsureAvailabilitySuite) TestEnsureAvailabilityPlacementError(c *gc.C) {
   112  	_, err := s.runEnsureAvailability(c, "-n", "1", "--to", "1")
   113  	c.Assert(err, gc.ErrorMatches, `unsupported ensure-availability placement directive "1"`)
   114  }
   115  
   116  func (s *EnsureAvailabilitySuite) TestEnsureAvailabilityFormatYaml(c *gc.C) {
   117  	expected := map[string][]string{
   118  		"maintained": {"0"},
   119  		"added":      {"1", "2"},
   120  	}
   121  
   122  	ctx, err := s.runEnsureAvailability(c, "-n", "3", "--format", "yaml")
   123  	c.Assert(err, jc.ErrorIsNil)
   124  
   125  	c.Assert(s.fake.numStateServers, gc.Equals, 3)
   126  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   127  	c.Assert(s.fake.series, gc.Equals, "")
   128  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   129  
   130  	var result map[string][]string
   131  	err = goyaml.Unmarshal(ctx.Stdout.(*bytes.Buffer).Bytes(), &result)
   132  	c.Assert(err, jc.ErrorIsNil)
   133  	c.Assert(result, gc.DeepEquals, expected)
   134  }
   135  
   136  func (s *EnsureAvailabilitySuite) TestEnsureAvailabilityFormatJson(c *gc.C) {
   137  	expected := map[string][]string{
   138  		"maintained": {"0"},
   139  		"added":      {"1", "2"},
   140  	}
   141  
   142  	ctx, err := s.runEnsureAvailability(c, "-n", "3", "--format", "json")
   143  	c.Assert(err, jc.ErrorIsNil)
   144  
   145  	c.Assert(s.fake.numStateServers, gc.Equals, 3)
   146  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   147  	c.Assert(s.fake.series, gc.Equals, "")
   148  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   149  
   150  	var result map[string][]string
   151  	err = json.Unmarshal(ctx.Stdout.(*bytes.Buffer).Bytes(), &result)
   152  	c.Assert(err, jc.ErrorIsNil)
   153  	c.Assert(result, gc.DeepEquals, expected)
   154  }
   155  
   156  func (s *EnsureAvailabilitySuite) TestEnsureAvailabilityWithSeries(c *gc.C) {
   157  	// Also test with -n 5 to validate numbers other than 1 and 3
   158  	ctx, err := s.runEnsureAvailability(c, "--series", "series", "-n", "5")
   159  	c.Assert(err, jc.ErrorIsNil)
   160  	c.Assert(coretesting.Stdout(ctx), gc.Equals,
   161  		"maintaining machines: 0\n"+
   162  			"adding machines: 1, 2, 3, 4\n\n")
   163  
   164  	c.Assert(s.fake.numStateServers, gc.Equals, 5)
   165  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   166  	c.Assert(s.fake.series, gc.Equals, "series")
   167  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   168  }
   169  
   170  func (s *EnsureAvailabilitySuite) TestEnsureAvailabilityWithConstraints(c *gc.C) {
   171  	ctx, err := s.runEnsureAvailability(c, "--constraints", "mem=4G", "-n", "3")
   172  	c.Assert(err, jc.ErrorIsNil)
   173  	c.Assert(coretesting.Stdout(ctx), gc.Equals,
   174  		"maintaining machines: 0\n"+
   175  			"adding machines: 1, 2\n\n")
   176  
   177  	c.Assert(s.fake.numStateServers, gc.Equals, 3)
   178  	expectedCons := constraints.MustParse("mem=4G")
   179  	c.Assert(s.fake.cons, gc.DeepEquals, expectedCons)
   180  	c.Assert(s.fake.series, gc.Equals, "")
   181  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   182  }
   183  
   184  func (s *EnsureAvailabilitySuite) TestEnsureAvailabilityWithPlacement(c *gc.C) {
   185  	ctx, err := s.runEnsureAvailability(c, "--to", "valid", "-n", "3")
   186  	c.Assert(err, jc.ErrorIsNil)
   187  	c.Assert(coretesting.Stdout(ctx), gc.Equals,
   188  		"maintaining machines: 0\n"+
   189  			"adding machines: 1, 2\n\n")
   190  
   191  	c.Assert(s.fake.numStateServers, gc.Equals, 3)
   192  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   193  	c.Assert(s.fake.series, gc.Equals, "")
   194  	expectedPlacement := []string{"valid"}
   195  	c.Assert(s.fake.placement, gc.DeepEquals, expectedPlacement)
   196  }
   197  
   198  func (s *EnsureAvailabilitySuite) TestEnsureAvailabilityErrors(c *gc.C) {
   199  	for _, n := range []int{-1, 2} {
   200  		_, err := s.runEnsureAvailability(c, "-n", fmt.Sprint(n))
   201  		c.Assert(err, gc.ErrorMatches, "must specify a number of state servers odd and non-negative")
   202  	}
   203  
   204  	// Verify that ensure-availability didn't call into the API
   205  	c.Assert(s.fake.numStateServers, gc.Equals, invalidNumServers)
   206  }
   207  
   208  func (s *EnsureAvailabilitySuite) TestEnsureAvailabilityAllows0(c *gc.C) {
   209  	// If the number of state servers is specified as "0", the API will
   210  	// then use the default number of 3.
   211  	ctx, err := s.runEnsureAvailability(c, "-n", "0")
   212  	c.Assert(err, jc.ErrorIsNil)
   213  	c.Assert(coretesting.Stdout(ctx), gc.Equals,
   214  		"maintaining machines: 0\n"+
   215  			"adding machines: 1, 2\n\n")
   216  
   217  	c.Assert(s.fake.numStateServers, gc.Equals, 0)
   218  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   219  	c.Assert(s.fake.series, gc.Equals, "")
   220  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   221  }
   222  
   223  func (s *EnsureAvailabilitySuite) TestEnsureAvailabilityDefaultsTo0(c *gc.C) {
   224  	// If the number of state servers is not specified, we pass in 0 to the
   225  	// API.  The API will then use the default number of 3.
   226  	ctx, err := s.runEnsureAvailability(c)
   227  	c.Assert(err, jc.ErrorIsNil)
   228  	c.Assert(coretesting.Stdout(ctx), gc.Equals,
   229  		"maintaining machines: 0\n"+
   230  			"adding machines: 1, 2\n\n")
   231  
   232  	c.Assert(s.fake.numStateServers, gc.Equals, 0)
   233  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   234  	c.Assert(s.fake.series, gc.Equals, "")
   235  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   236  }