github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/cmd/juju/endpoint_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 jc "github.com/juju/testing/checkers" 8 gc "gopkg.in/check.v1" 9 10 "github.com/juju/juju/cmd/envcmd" 11 "github.com/juju/juju/environs/config" 12 "github.com/juju/juju/environs/configstore" 13 envtesting "github.com/juju/juju/environs/testing" 14 "github.com/juju/juju/instance" 15 "github.com/juju/juju/juju/testing" 16 "github.com/juju/juju/network" 17 "github.com/juju/juju/provider/dummy" 18 coretesting "github.com/juju/juju/testing" 19 ) 20 21 type EndpointSuite struct { 22 testing.JujuConnSuite 23 24 restoreTimeouts func() 25 } 26 27 var _ = gc.Suite(&EndpointSuite{}) 28 29 func (s *EndpointSuite) SetUpSuite(c *gc.C) { 30 // Use very short attempt strategies when getting instance addresses. 31 s.restoreTimeouts = envtesting.PatchAttemptStrategies() 32 s.JujuConnSuite.SetUpSuite(c) 33 } 34 35 func (s *EndpointSuite) TearDownSuite(c *gc.C) { 36 s.JujuConnSuite.TearDownSuite(c) 37 s.restoreTimeouts() 38 } 39 40 func (s *EndpointSuite) TestNoEndpoints(c *gc.C) { 41 // Reset all addresses. 42 s.setCachedAPIAddresses(c) 43 s.setServerAPIAddresses(c) 44 s.assertCachedAddresses(c) 45 46 stdout, stderr, err := s.runCommand(c) 47 c.Assert(err, gc.ErrorMatches, "no API endpoints available") 48 c.Assert(stdout, gc.Equals, "") 49 c.Assert(stderr, gc.Equals, "") 50 51 s.assertCachedAddresses(c) 52 } 53 54 func (s *EndpointSuite) TestCachedAddressesUsedIfAvailable(c *gc.C) { 55 addresses := network.NewHostPorts(1234, 56 "10.0.0.1:1234", 57 "[2001:db8::1]:1234", 58 "0.1.2.3:1234", 59 "[fc00::1]:1234", 60 ) 61 // Set the cached addresses. 62 s.setCachedAPIAddresses(c, addresses...) 63 // Clear instance/state addresses to ensure we can't connect to 64 // the API server. 65 s.setServerAPIAddresses(c) 66 67 testRun := func(i int, envPreferIPv6, bootPreferIPv6 bool) { 68 c.Logf( 69 "\ntest %d: prefer-ipv6 environ=%v, bootstrap=%v", 70 i, envPreferIPv6, bootPreferIPv6, 71 ) 72 s.setPreferIPv6EnvironConfig(c, envPreferIPv6) 73 s.setPreferIPv6BootstrapConfig(c, bootPreferIPv6) 74 75 // Without arguments, verify the first cached address is returned. 76 s.runAndCheckOutput(c, "smart", expectOutput(addresses[0])) 77 s.assertCachedAddresses(c, addresses...) 78 79 // With --all, ensure all are returned. 80 s.runAndCheckOutput(c, "smart", expectOutput(addresses...), "--all") 81 s.assertCachedAddresses(c, addresses...) 82 } 83 84 // Ensure regardless of the prefer-ipv6 value we have the same 85 // result. 86 for i, envPreferIPv6 := range []bool{true, false} { 87 for j, bootPreferIPv6 := range []bool{true, false} { 88 testRun(i+j, envPreferIPv6, bootPreferIPv6) 89 } 90 } 91 } 92 93 func (s *EndpointSuite) TestRefresh(c *gc.C) { 94 testRun := func(i int, address network.HostPort, explicitRefresh bool) { 95 c.Logf("\ntest %d: address=%q, explicitRefresh=%v", i, address, explicitRefresh) 96 97 // Cache the address. 98 s.setCachedAPIAddresses(c, address) 99 s.assertCachedAddresses(c, address) 100 // Clear instance/state addresses to ensure only the cached 101 // one will be used. 102 s.setServerAPIAddresses(c) 103 104 // Ensure we get and cache the first address (i.e. no changes) 105 if explicitRefresh { 106 s.runAndCheckOutput(c, "smart", expectOutput(address), "--refresh") 107 } else { 108 s.runAndCheckOutput(c, "smart", expectOutput(address)) 109 } 110 s.assertCachedAddresses(c, address) 111 } 112 113 // Test both IPv4 and IPv6 endpoints separately, first with 114 // implicit refresh, then explicit. 115 for i, explicitRefresh := range []bool{true, false} { 116 for j, addr := range s.addressesWithAPIPort(c, "localhost", "::1") { 117 testRun(i+j, addr, explicitRefresh) 118 } 119 } 120 } 121 122 func (s *EndpointSuite) TestSortingAndFilteringBeforeCachingRespectsPreferIPv6(c *gc.C) { 123 // Set the instance/state addresses to a mix of IPv4 and IPv6 124 // addresses of all kinds. 125 addresses := s.addressesWithAPIPort(c, 126 // The following two are needed to actually connect to the 127 // test API server. 128 "127.0.0.1", 129 "::1", 130 // Other examples. 131 "192.0.0.0", 132 "2001:db8::1", 133 "169.254.1.2", // link-local - will be removed. 134 "fd00::1", 135 "localhost", // will be put at the top as last successful. 136 "ff01::1", // link-local - will be removed. 137 "fc00::1", 138 "localhost", // will be removed as a duplicate. 139 "0.1.2.3", 140 "127.0.1.1", // removed as a duplicate. 141 "::1", // removed as a duplicate. 142 "10.0.0.1", 143 "8.8.8.8", 144 ) 145 s.setServerAPIAddresses(c, addresses...) 146 147 // Clear cached the address to force a refresh. 148 s.setCachedAPIAddresses(c) 149 s.assertCachedAddresses(c) 150 // Set prefer-ipv6 to true first. 151 s.setPreferIPv6BootstrapConfig(c, true) 152 153 // Build the expected addresses list, after processing. 154 expectAddresses := s.addressesWithAPIPort(c, 155 "localhost", // This is always on top. 156 "2001:db8::1", 157 "0.1.2.3", 158 "192.0.0.0", 159 "8.8.8.8", 160 "fc00::1", 161 "fd00::1", 162 "10.0.0.1", 163 ) 164 s.runAndCheckOutput(c, "smart", expectOutput(expectAddresses...), "--all") 165 s.assertCachedAddresses(c, expectAddresses...) 166 167 // Now run it again with prefer-ipv6: false. 168 // But first reset the cached addresses.. 169 s.setCachedAPIAddresses(c) 170 s.assertCachedAddresses(c) 171 s.setPreferIPv6BootstrapConfig(c, false) 172 173 // Rebuild the expected addresses and rebuild them so IPv4 comes 174 // before IPv6. 175 expectAddresses = s.addressesWithAPIPort(c, 176 "localhost", // This is always on top. 177 "0.1.2.3", 178 "192.0.0.0", 179 "8.8.8.8", 180 "2001:db8::1", 181 "10.0.0.1", 182 "fc00::1", 183 "fd00::1", 184 ) 185 s.runAndCheckOutput(c, "smart", expectOutput(expectAddresses...), "--all") 186 s.assertCachedAddresses(c, expectAddresses...) 187 } 188 189 func (s *EndpointSuite) TestAllFormats(c *gc.C) { 190 addresses := s.addressesWithAPIPort(c, 191 "127.0.0.1", 192 "8.8.8.8", 193 "2001:db8::1", 194 "::1", 195 "10.0.0.1", 196 "fc00::1", 197 ) 198 s.setServerAPIAddresses(c) 199 s.setCachedAPIAddresses(c, addresses...) 200 s.assertCachedAddresses(c, addresses...) 201 202 for i, test := range []struct { 203 about string 204 args []string 205 format string 206 output []network.HostPort 207 }{{ 208 about: "default format (smart), no args", 209 format: "smart", 210 output: addresses[0:1], 211 }, { 212 about: "default format (smart), with --all", 213 args: []string{"--all"}, 214 format: "smart", 215 output: addresses, 216 }, { 217 about: "JSON format, without --all", 218 args: []string{"--format", "json"}, 219 format: "json", 220 output: addresses[0:1], 221 }, { 222 about: "JSON format, with --all", 223 args: []string{"--format", "json", "--all"}, 224 format: "json", 225 output: addresses, 226 }, { 227 about: "YAML format, without --all", 228 args: []string{"--format", "yaml"}, 229 format: "yaml", 230 output: addresses[0:1], 231 }, { 232 about: "YAML format, with --all", 233 args: []string{"--format", "yaml", "--all"}, 234 format: "yaml", 235 output: addresses, 236 }} { 237 c.Logf("\ntest %d: %s", i, test.about) 238 s.runAndCheckOutput(c, test.format, expectOutput(test.output...), test.args...) 239 } 240 } 241 242 // runCommand runs the api-endpoints command with the given arguments 243 // and returns the output and any error. 244 func (s *EndpointSuite) runCommand(c *gc.C, args ...string) (string, string, error) { 245 command := &EndpointCommand{} 246 ctx, err := coretesting.RunCommand(c, envcmd.Wrap(command), args...) 247 if err != nil { 248 return "", "", err 249 } 250 return coretesting.Stdout(ctx), coretesting.Stderr(ctx), nil 251 } 252 253 // runAndCheckOutput runs api-endpoints expecting no error and 254 // compares the output for the given format. 255 func (s *EndpointSuite) runAndCheckOutput(c *gc.C, format string, output []interface{}, args ...string) { 256 stdout, stderr, err := s.runCommand(c, args...) 257 if !c.Check(err, jc.ErrorIsNil) { 258 return 259 } 260 c.Check(stderr, gc.Equals, "") 261 switch format { 262 case "smart": 263 strOutput := "" 264 for _, line := range output { 265 strOutput += line.(string) + "\n" 266 } 267 c.Check(stdout, gc.Equals, strOutput) 268 case "json": 269 c.Check(stdout, jc.JSONEquals, output) 270 case "yaml": 271 c.Check(stdout, jc.YAMLEquals, output) 272 default: 273 c.Fatalf("unexpected format %q", format) 274 } 275 } 276 277 // getStoreInfo returns the current environment's EnvironInfo. 278 func (s *EndpointSuite) getStoreInfo(c *gc.C) configstore.EnvironInfo { 279 env, err := s.State.Environment() 280 c.Assert(err, jc.ErrorIsNil) 281 info, err := s.ConfigStore.ReadInfo(env.Name()) 282 c.Assert(err, jc.ErrorIsNil) 283 return info 284 } 285 286 // setPreferIPv6EnvironConfig sets the "prefer-ipv6" environment 287 // setting to given value. 288 func (s *EndpointSuite) setPreferIPv6EnvironConfig(c *gc.C, value bool) { 289 // Technically, because prefer-ipv6 is an immutable setting, what 290 // follows should be impossible, but the dummy provider doesn't 291 // seem to validate the new config against the current (old) one 292 // when calling SetConfig(). 293 allAttrs := s.Environ.Config().AllAttrs() 294 allAttrs["prefer-ipv6"] = value 295 cfg, err := config.New(config.NoDefaults, allAttrs) 296 c.Assert(err, jc.ErrorIsNil) 297 err = s.Environ.SetConfig(cfg) 298 c.Assert(err, jc.ErrorIsNil) 299 setValue := cfg.AllAttrs()["prefer-ipv6"].(bool) 300 c.Logf("environ config prefer-ipv6 set to %v", setValue) 301 } 302 303 // setPreferIPv6BootstrapConfig sets the "prefer-ipv6" setting to the 304 // given value on the current environment's bootstrap config by 305 // recreating it (the only way to change bootstrap config once set). 306 func (s *EndpointSuite) setPreferIPv6BootstrapConfig(c *gc.C, value bool) { 307 currentInfo := s.getStoreInfo(c) 308 endpoint := currentInfo.APIEndpoint() 309 creds := currentInfo.APICredentials() 310 bootstrapConfig := currentInfo.BootstrapConfig() 311 delete(bootstrapConfig, "prefer-ipv6") 312 313 // The only way to change the bootstrap config is to recreate the 314 // info. 315 err := currentInfo.Destroy() 316 c.Assert(err, jc.ErrorIsNil) 317 newInfo := s.ConfigStore.CreateInfo(s.Environ.Config().Name()) 318 newInfo.SetAPICredentials(creds) 319 newInfo.SetAPIEndpoint(endpoint) 320 newCfg := make(coretesting.Attrs) 321 newCfg["prefer-ipv6"] = value 322 newInfo.SetBootstrapConfig(newCfg.Merge(bootstrapConfig)) 323 err = newInfo.Write() 324 c.Assert(err, jc.ErrorIsNil) 325 setValue := newInfo.BootstrapConfig()["prefer-ipv6"].(bool) 326 c.Logf("bootstrap config prefer-ipv6 set to %v", setValue) 327 } 328 329 // setCachedAPIAddresses sets the given addresses on the cached 330 // EnvironInfo endpoint. APIEndpoint.Hostnames are not touched, 331 // because the interactions between Addresses and Hostnames are 332 // separately tested in juju/api_test.go 333 func (s *EndpointSuite) setCachedAPIAddresses(c *gc.C, addresses ...network.HostPort) { 334 info := s.getStoreInfo(c) 335 endpoint := info.APIEndpoint() 336 endpoint.Addresses = network.HostPortsToStrings(addresses) 337 info.SetAPIEndpoint(endpoint) 338 err := info.Write() 339 c.Assert(err, jc.ErrorIsNil) 340 c.Logf("cached addresses set to %v", info.APIEndpoint().Addresses) 341 } 342 343 // setServerAPIAddresses sets the given addresses on the dummy 344 // bootstrap instance and in state. 345 func (s *EndpointSuite) setServerAPIAddresses(c *gc.C, addresses ...network.HostPort) { 346 insts, err := s.Environ.Instances([]instance.Id{dummy.BootstrapInstanceId}) 347 c.Assert(err, jc.ErrorIsNil) 348 err = s.State.SetAPIHostPorts([][]network.HostPort{addresses}) 349 c.Assert(err, jc.ErrorIsNil) 350 dummy.SetInstanceAddresses(insts[0], network.HostsWithoutPort(addresses)) 351 instAddrs, err := insts[0].Addresses() 352 c.Assert(err, jc.ErrorIsNil) 353 stateAddrs, err := s.State.APIHostPorts() 354 c.Assert(err, jc.ErrorIsNil) 355 c.Logf("instance addresses set to %v", instAddrs) 356 c.Logf("state addresses set to %v", stateAddrs) 357 } 358 359 // addressesWithAPIPort returns the given addresses appending the test 360 // API server listening port to each one. 361 func (s *EndpointSuite) addressesWithAPIPort(c *gc.C, addresses ...string) []network.HostPort { 362 apiPort := s.Environ.Config().APIPort() 363 return network.NewHostPorts(apiPort, addresses...) 364 } 365 366 // assertCachedAddresses ensures the endpoint addresses (not 367 // hostnames) stored in the store match the given ones. 368 // APIEndpoint.Hostnames and APIEndpoint.Addresses interactions are 369 // separately testing in juju/api_test.go. 370 func (s *EndpointSuite) assertCachedAddresses(c *gc.C, addresses ...network.HostPort) { 371 info := s.getStoreInfo(c) 372 strAddresses := network.HostPortsToStrings(addresses) 373 c.Assert(info.APIEndpoint().Addresses, jc.DeepEquals, strAddresses) 374 } 375 376 // expectOutput is a helper used to construct the expected ouput 377 // argument to runAndCheckOutput. 378 func expectOutput(addresses ...network.HostPort) []interface{} { 379 result := make([]interface{}, len(addresses)) 380 for i, addr := range addresses { 381 result[i] = addr.NetAddr() 382 } 383 return result 384 }