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 }