github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/subnet/list_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package subnet_test
     5  
     6  import (
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/names"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/cmd/juju/subnet"
    17  	coretesting "github.com/juju/juju/testing"
    18  )
    19  
    20  type ListSuite struct {
    21  	BaseSubnetSuite
    22  }
    23  
    24  var _ = gc.Suite(&ListSuite{})
    25  
    26  func (s *ListSuite) SetUpTest(c *gc.C) {
    27  	s.BaseSubnetSuite.SetUpTest(c)
    28  	s.command, _ = subnet.NewListCommandForTest(s.api)
    29  	c.Assert(s.command, gc.NotNil)
    30  }
    31  
    32  func (s *ListSuite) TestInit(c *gc.C) {
    33  	for i, test := range []struct {
    34  		about        string
    35  		args         []string
    36  		expectSpace  string
    37  		expectZone   string
    38  		expectFormat string
    39  		expectErr    string
    40  	}{{
    41  		about:        "too many arguments",
    42  		args:         s.Strings("foo", "bar"),
    43  		expectErr:    `unrecognized args: \["foo" "bar"\]`,
    44  		expectFormat: "yaml",
    45  	}, {
    46  		about:        "invalid space name",
    47  		args:         s.Strings("--space", "%inv$alid"),
    48  		expectErr:    `"%inv\$alid" is not a valid space name`,
    49  		expectFormat: "yaml",
    50  	}, {
    51  		about:        "valid space name",
    52  		args:         s.Strings("--space", "my-space"),
    53  		expectSpace:  "my-space",
    54  		expectFormat: "yaml",
    55  	}, {
    56  		about:        "both space and zone given",
    57  		args:         s.Strings("--zone", "zone1", "--space", "my-space"),
    58  		expectSpace:  "my-space",
    59  		expectZone:   "zone1",
    60  		expectFormat: "yaml",
    61  	}, {
    62  		about:        "invalid format",
    63  		args:         s.Strings("--format", "foo"),
    64  		expectErr:    `invalid value "foo" for flag --format: unknown format "foo"`,
    65  		expectFormat: "yaml",
    66  	}, {
    67  		about:        "invalid format (value is case-sensitive)",
    68  		args:         s.Strings("--format", "JSON"),
    69  		expectErr:    `invalid value "JSON" for flag --format: unknown format "JSON"`,
    70  		expectFormat: "yaml",
    71  	}, {
    72  		about:        "json format",
    73  		args:         s.Strings("--format", "json"),
    74  		expectFormat: "json",
    75  	}, {
    76  		about:        "yaml format",
    77  		args:         s.Strings("--format", "yaml"),
    78  		expectFormat: "yaml",
    79  	}, {
    80  		// --output and -o are tested separately in TestOutputFormats.
    81  		about:        "both --output and -o specified (latter overrides former)",
    82  		args:         s.Strings("--output", "foo", "-o", "bar"),
    83  		expectFormat: "yaml",
    84  	}} {
    85  		c.Logf("test #%d: %s", i, test.about)
    86  		// Create a new instance of the subcommand for each test, but
    87  		// since we're not running the command no need to use
    88  		// modelcmd.Wrap().
    89  		wrappedCommand, command := subnet.NewListCommandForTest(s.api)
    90  		err := coretesting.InitCommand(wrappedCommand, test.args)
    91  		if test.expectErr != "" {
    92  			c.Check(err, gc.ErrorMatches, test.expectErr)
    93  		} else {
    94  			c.Check(err, jc.ErrorIsNil)
    95  		}
    96  		c.Check(command.SpaceName, gc.Equals, test.expectSpace)
    97  		c.Check(command.ZoneName, gc.Equals, test.expectZone)
    98  		c.Check(command.Out.Name(), gc.Equals, test.expectFormat)
    99  
   100  		// No API calls should be recorded at this stage.
   101  		s.api.CheckCallNames(c)
   102  	}
   103  }
   104  
   105  func (s *ListSuite) TestOutputFormats(c *gc.C) {
   106  	outDir := c.MkDir()
   107  	expectedYAML := `
   108  subnets:
   109    10.10.0.0/16:
   110      type: ipv4
   111      status: terminating
   112      space: vlan-42
   113      zones:
   114      - zone1
   115    10.20.0.0/24:
   116      type: ipv4
   117      provider-id: subnet-foo
   118      status: in-use
   119      space: public
   120      zones:
   121      - zone1
   122      - zone2
   123    2001:db8::/32:
   124      type: ipv6
   125      provider-id: subnet-bar
   126      status: terminating
   127      space: dmz
   128      zones:
   129      - zone2
   130  `[1:]
   131  	expectedJSON := `{"subnets":{` +
   132  		`"10.10.0.0/16":{` +
   133  		`"type":"ipv4",` +
   134  		`"status":"terminating",` +
   135  		`"space":"vlan-42",` +
   136  		`"zones":["zone1"]},` +
   137  
   138  		`"10.20.0.0/24":{` +
   139  		`"type":"ipv4",` +
   140  		`"provider-id":"subnet-foo",` +
   141  		`"status":"in-use",` +
   142  		`"space":"public",` +
   143  		`"zones":["zone1","zone2"]},` +
   144  
   145  		`"2001:db8::/32":{` +
   146  		`"type":"ipv6",` +
   147  		`"provider-id":"subnet-bar",` +
   148  		`"status":"terminating",` +
   149  		`"space":"dmz",` +
   150  		`"zones":["zone2"]}}}
   151  `
   152  
   153  	assertAPICalls := func() {
   154  		// Verify the API calls and reset the recorded calls.
   155  		s.api.CheckCallNames(c, "ListSubnets", "Close")
   156  		s.api.ResetCalls()
   157  	}
   158  	makeArgs := func(format string, extraArgs ...string) []string {
   159  		args := s.Strings(extraArgs...)
   160  		if format != "" {
   161  			args = append(args, "--format", format)
   162  		}
   163  		return args
   164  	}
   165  	assertOutput := func(format, expected string) {
   166  		outFile := filepath.Join(outDir, "output")
   167  		c.Assert(outFile, jc.DoesNotExist)
   168  		defer os.Remove(outFile)
   169  		// Check -o works.
   170  		args := makeArgs(format, "-o", outFile)
   171  		s.AssertRunSucceeds(c, "", "", args...)
   172  		assertAPICalls()
   173  
   174  		data, err := ioutil.ReadFile(outFile)
   175  		c.Assert(err, jc.ErrorIsNil)
   176  		c.Assert(string(data), gc.Equals, expected)
   177  
   178  		// Check the last output argument takes precedence when both
   179  		// -o and --output are given (and also that --output works the
   180  		// same as -o).
   181  		outFile1 := filepath.Join(outDir, "output1")
   182  		c.Assert(outFile1, jc.DoesNotExist)
   183  		defer os.Remove(outFile1)
   184  		outFile2 := filepath.Join(outDir, "output2")
   185  		c.Assert(outFile2, jc.DoesNotExist)
   186  		defer os.Remove(outFile2)
   187  		// Write something in outFile2 to verify its contents are
   188  		// overwritten.
   189  		err = ioutil.WriteFile(outFile2, []byte("some contents"), 0644)
   190  		c.Assert(err, jc.ErrorIsNil)
   191  
   192  		args = makeArgs(format, "-o", outFile1, "--output", outFile2)
   193  		s.AssertRunSucceeds(c, "", "", args...)
   194  		// Check only the last output file was used, and the output
   195  		// file was overwritten.
   196  		c.Assert(outFile1, jc.DoesNotExist)
   197  		data, err = ioutil.ReadFile(outFile2)
   198  		c.Assert(err, jc.ErrorIsNil)
   199  		c.Assert(string(data), gc.Equals, expected)
   200  		assertAPICalls()
   201  
   202  		// Finally, check without --output.
   203  		args = makeArgs(format)
   204  		s.AssertRunSucceeds(c, "", expected, args...)
   205  		assertAPICalls()
   206  	}
   207  
   208  	for i, test := range []struct {
   209  		format   string
   210  		expected string
   211  	}{
   212  		{"", expectedYAML}, // default format is YAML
   213  		{"yaml", expectedYAML},
   214  		{"json", expectedJSON},
   215  	} {
   216  		c.Logf("test #%d: format %q", i, test.format)
   217  		assertOutput(test.format, test.expected)
   218  	}
   219  }
   220  
   221  func (s *ListSuite) TestRunWhenNoneMatchSucceeds(c *gc.C) {
   222  	// Simulate no subnets are using the "default" space.
   223  	s.api.Subnets = s.api.Subnets[0:0]
   224  
   225  	s.AssertRunSucceeds(c,
   226  		`no subnets found matching requested criteria\n`,
   227  		"", // empty stdout.
   228  		"--space", "default",
   229  	)
   230  
   231  	s.api.CheckCallNames(c, "ListSubnets", "Close")
   232  	tag := names.NewSpaceTag("default")
   233  	s.api.CheckCall(c, 0, "ListSubnets", &tag, "")
   234  }
   235  
   236  func (s *ListSuite) TestRunWhenNoSubnetsExistSucceeds(c *gc.C) {
   237  	s.api.Subnets = s.api.Subnets[0:0]
   238  
   239  	s.AssertRunSucceeds(c,
   240  		`no subnets to display\n`,
   241  		"", // empty stdout.
   242  	)
   243  
   244  	s.api.CheckCallNames(c, "ListSubnets", "Close")
   245  	s.api.CheckCall(c, 0, "ListSubnets", nil, "")
   246  }
   247  
   248  func (s *ListSuite) TestRunWithFilteringSucceeds(c *gc.C) {
   249  	// Simulate one subnet is using the "public" space or "zone1".
   250  	s.api.Subnets = s.api.Subnets[0:1]
   251  
   252  	expected := `
   253  subnets:
   254    10.20.0.0/24:
   255      type: ipv4
   256      provider-id: subnet-foo
   257      status: in-use
   258      space: public
   259      zones:
   260      - zone1
   261      - zone2
   262  `[1:]
   263  
   264  	// Filter by space name first.
   265  	s.AssertRunSucceeds(c,
   266  		"", // empty stderr.
   267  		expected,
   268  		"--space", "public",
   269  	)
   270  
   271  	s.api.CheckCallNames(c, "ListSubnets", "Close")
   272  	tag := names.NewSpaceTag("public")
   273  	s.api.CheckCall(c, 0, "ListSubnets", &tag, "")
   274  	s.api.ResetCalls()
   275  
   276  	// Now filter by zone.
   277  	s.AssertRunSucceeds(c,
   278  		"", // empty stderr.
   279  		expected,
   280  		"--zone", "zone1",
   281  	)
   282  
   283  	s.api.CheckCallNames(c, "ListSubnets", "Close")
   284  	s.api.CheckCall(c, 0, "ListSubnets", nil, "zone1")
   285  	s.api.ResetCalls()
   286  
   287  	// Finally, filter by both space and zone.
   288  	s.AssertRunSucceeds(c,
   289  		"", // empty stderr.
   290  		expected,
   291  		"--zone", "zone1", "--space", "public",
   292  	)
   293  
   294  	s.api.CheckCallNames(c, "ListSubnets", "Close")
   295  	tag = names.NewSpaceTag("public")
   296  	s.api.CheckCall(c, 0, "ListSubnets", &tag, "zone1")
   297  }
   298  
   299  func (s *ListSuite) TestRunWhenListSubnetFails(c *gc.C) {
   300  	s.api.SetErrors(errors.NotSupportedf("foo"))
   301  
   302  	// Ensure the error cause is preserved.
   303  	err := s.AssertRunFails(c, "cannot list subnets: foo not supported")
   304  	c.Assert(err, jc.Satisfies, errors.IsNotSupported)
   305  
   306  	s.api.CheckCallNames(c, "ListSubnets", "Close")
   307  	s.api.CheckCall(c, 0, "ListSubnets", nil, "")
   308  }
   309  
   310  func (s *ListSuite) TestRunWhenASubnetHasInvalidCIDRFails(c *gc.C) {
   311  	// This cannot happen in practice, as CIDRs are validated before
   312  	// adding a subnet, but this test ensures 100% coverage.
   313  	s.api.Subnets = s.api.Subnets[0:1]
   314  	s.api.Subnets[0].CIDR = "invalid"
   315  
   316  	s.AssertRunFails(c, `subnet "invalid" has invalid CIDR`)
   317  
   318  	s.api.CheckCallNames(c, "ListSubnets", "Close")
   319  	s.api.CheckCall(c, 0, "ListSubnets", nil, "")
   320  }
   321  
   322  func (s *ListSuite) TestRunWhenASubnetHasInvalidSpaceFails(c *gc.C) {
   323  	// This cannot happen in practice, as space names are validated
   324  	// before adding a subnet, but this test ensures 100% coverage.
   325  	s.api.Subnets = s.api.Subnets[0:1]
   326  	s.api.Subnets[0].SpaceTag = "foo"
   327  
   328  	s.AssertRunFails(c, `subnet "10.20.0.0/24" has invalid space: "foo" is not a valid tag`)
   329  
   330  	s.api.CheckCallNames(c, "ListSubnets", "Close")
   331  	s.api.CheckCall(c, 0, "ListSubnets", nil, "")
   332  }