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