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 }