github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/openstack/provider_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package openstack 5 6 import ( 7 "fmt" 8 "net/http" 9 "net/http/httptest" 10 11 gitjujutesting "github.com/juju/testing" 12 jc "github.com/juju/testing/checkers" 13 "github.com/juju/utils" 14 gc "gopkg.in/check.v1" 15 "gopkg.in/goose.v2/identity" 16 "gopkg.in/goose.v2/neutron" 17 "gopkg.in/goose.v2/nova" 18 "gopkg.in/yaml.v2" 19 20 "github.com/juju/juju/cloud" 21 "github.com/juju/juju/environs" 22 "github.com/juju/juju/environs/context" 23 "github.com/juju/juju/network" 24 ) 25 26 // localTests contains tests which do not require a live service or test double to run. 27 type localTests struct { 28 gitjujutesting.IsolationSuite 29 } 30 31 var _ = gc.Suite(&localTests{}) 32 33 // ported from lp:juju/juju/providers/openstack/tests/test_machine.py 34 var addressTests = []struct { 35 summary string 36 floatingIP string 37 private []nova.IPAddress 38 public []nova.IPAddress 39 networks []string 40 expected string 41 failure error 42 }{{ 43 summary: "missing", 44 expected: "", 45 }, { 46 summary: "empty", 47 private: []nova.IPAddress{}, 48 networks: []string{"private"}, 49 expected: "", 50 }, { 51 summary: "private IPv4 only", 52 private: []nova.IPAddress{{4, "192.168.0.1", "fixed"}}, 53 networks: []string{"private"}, 54 expected: "192.168.0.1", 55 }, { 56 summary: "private IPv6 only", 57 private: []nova.IPAddress{{6, "fc00::1", "fixed"}}, 58 networks: []string{"private"}, 59 expected: "fc00::1", 60 }, { 61 summary: "private only, both IPv4 and IPv6", 62 private: []nova.IPAddress{{4, "192.168.0.1", "fixed"}, {6, "fc00::1", "fixed"}}, 63 networks: []string{"private"}, 64 expected: "192.168.0.1", 65 }, { 66 summary: "private IPv4 plus (what HP cloud used to do)", 67 private: []nova.IPAddress{{4, "10.0.0.1", "fixed"}, {4, "8.8.4.4", "fixed"}}, 68 networks: []string{"private"}, 69 expected: "8.8.4.4", 70 }, { 71 summary: "public IPv4 only", 72 public: []nova.IPAddress{{4, "8.8.8.8", "floating"}}, 73 networks: []string{"", "public"}, 74 expected: "8.8.8.8", 75 }, { 76 summary: "public IPv6 only", 77 public: []nova.IPAddress{{6, "2001:db8::1", "floating"}}, 78 networks: []string{"", "public"}, 79 expected: "2001:db8::1", 80 }, { 81 summary: "public only, both IPv4 and IPv6", 82 public: []nova.IPAddress{{4, "8.8.8.8", "floating"}, {6, "2001:db8::1", "floating"}}, 83 networks: []string{"", "public"}, 84 expected: "8.8.8.8", 85 }, { 86 summary: "public and private both IPv4", 87 private: []nova.IPAddress{{4, "10.0.0.4", "fixed"}}, 88 public: []nova.IPAddress{{4, "8.8.4.4", "floating"}}, 89 networks: []string{"private", "public"}, 90 expected: "8.8.4.4", 91 }, { 92 summary: "public and private both IPv6", 93 private: []nova.IPAddress{{6, "fc00::1", "fixed"}}, 94 public: []nova.IPAddress{{6, "2001:db8::1", "floating"}}, 95 networks: []string{"private", "public"}, 96 expected: "2001:db8::1", 97 }, { 98 summary: "public, private, and localhost IPv4", 99 private: []nova.IPAddress{{4, "127.0.0.4", "fixed"}, {4, "192.168.0.1", "fixed"}}, 100 public: []nova.IPAddress{{4, "8.8.8.8", "floating"}}, 101 networks: []string{"private", "public"}, 102 expected: "8.8.8.8", 103 }, { 104 summary: "public, private, and localhost IPv6", 105 private: []nova.IPAddress{{6, "::1", "fixed"}, {6, "fc00::1", "fixed"}}, 106 public: []nova.IPAddress{{6, "2001:db8::1", "floating"}}, 107 networks: []string{"private", "public"}, 108 expected: "2001:db8::1", 109 }, { 110 summary: "public, private, and localhost - both IPv4 and IPv6", 111 private: []nova.IPAddress{{4, "127.0.0.4", "fixed"}, {4, "192.168.0.1", "fixed"}, {6, "::1", "fixed"}, {6, "fc00::1", "fixed"}}, 112 public: []nova.IPAddress{{4, "8.8.8.8", "floating"}, {6, "2001:db8::1", "floating"}}, 113 networks: []string{"private", "public"}, 114 expected: "8.8.8.8", 115 }, { 116 summary: "custom only IPv4", 117 private: []nova.IPAddress{{4, "192.168.0.1", "fixed"}}, 118 networks: []string{"special"}, 119 expected: "192.168.0.1", 120 }, { 121 summary: "custom only IPv6", 122 private: []nova.IPAddress{{6, "fc00::1", "fixed"}}, 123 networks: []string{"special"}, 124 expected: "fc00::1", 125 }, { 126 summary: "custom only - both IPv4 and IPv6", 127 private: []nova.IPAddress{{4, "192.168.0.1", "fixed"}, {6, "fc00::1", "fixed"}}, 128 networks: []string{"special"}, 129 expected: "192.168.0.1", 130 }, { 131 summary: "custom and public IPv4", 132 private: []nova.IPAddress{{4, "172.16.0.1", "fixed"}}, 133 public: []nova.IPAddress{{4, "8.8.8.8", "floating"}}, 134 networks: []string{"special", "public"}, 135 expected: "8.8.8.8", 136 }, { 137 summary: "custom and public IPv6", 138 private: []nova.IPAddress{{6, "fc00::1", "fixed"}}, 139 public: []nova.IPAddress{{6, "2001:db8::1", "floating"}}, 140 networks: []string{"special", "public"}, 141 expected: "2001:db8::1", 142 }, { 143 summary: "custom and public - both IPv4 and IPv6", 144 private: []nova.IPAddress{{4, "172.16.0.1", "fixed"}, {6, "fc00::1", "fixed"}}, 145 public: []nova.IPAddress{{4, "8.8.8.8", "floating"}, {6, "2001:db8::1", "floating"}}, 146 networks: []string{"special", "public"}, 147 expected: "8.8.8.8", 148 }, { 149 summary: "floating and public, same address", 150 floatingIP: "8.8.8.8", 151 public: []nova.IPAddress{{4, "8.8.8.8", "floating"}}, 152 networks: []string{"", "public"}, 153 expected: "8.8.8.8", 154 }, { 155 summary: "floating and public, different address", 156 floatingIP: "8.8.4.4", 157 public: []nova.IPAddress{{4, "8.8.8.8", "floating"}}, 158 networks: []string{"", "public"}, 159 expected: "8.8.4.4", 160 }, { 161 summary: "floating and private", 162 floatingIP: "8.8.4.4", 163 private: []nova.IPAddress{{4, "10.0.0.1", "fixed"}}, 164 networks: []string{"private"}, 165 expected: "8.8.4.4", 166 }, { 167 summary: "floating, custom and public", 168 floatingIP: "8.8.4.4", 169 private: []nova.IPAddress{{4, "172.16.0.1", "fixed"}}, 170 public: []nova.IPAddress{{4, "8.8.8.8", "floating"}}, 171 networks: []string{"special", "public"}, 172 expected: "8.8.4.4", 173 }} 174 175 func (t *localTests) TestGetServerAddresses(c *gc.C) { 176 for i, t := range addressTests { 177 c.Logf("#%d. %s -> %s (%v)", i, t.summary, t.expected, t.failure) 178 addresses := make(map[string][]nova.IPAddress) 179 if t.private != nil { 180 if len(t.networks) < 1 { 181 addresses["private"] = t.private 182 } else { 183 addresses[t.networks[0]] = t.private 184 } 185 } 186 if t.public != nil { 187 if len(t.networks) < 2 { 188 addresses["public"] = t.public 189 } else { 190 addresses[t.networks[1]] = t.public 191 } 192 } 193 addr := InstanceAddress(t.floatingIP, addresses) 194 c.Check(addr, gc.Equals, t.expected) 195 } 196 } 197 198 func (*localTests) TestPortsToRuleInfo(c *gc.C) { 199 groupId := "groupid" 200 testCases := []struct { 201 about string 202 rules []network.IngressRule 203 expected []neutron.RuleInfoV2 204 }{{ 205 about: "single port", 206 rules: []network.IngressRule{network.MustNewIngressRule("tcp", 80, 80)}, 207 expected: []neutron.RuleInfoV2{{ 208 Direction: "ingress", 209 IPProtocol: "tcp", 210 PortRangeMin: 80, 211 PortRangeMax: 80, 212 RemoteIPPrefix: "0.0.0.0/0", 213 ParentGroupId: groupId, 214 }}, 215 }, { 216 about: "multiple ports", 217 rules: []network.IngressRule{network.MustNewIngressRule("tcp", 80, 82)}, 218 expected: []neutron.RuleInfoV2{{ 219 Direction: "ingress", 220 IPProtocol: "tcp", 221 PortRangeMin: 80, 222 PortRangeMax: 82, 223 RemoteIPPrefix: "0.0.0.0/0", 224 ParentGroupId: groupId, 225 }}, 226 }, { 227 about: "multiple port ranges", 228 rules: []network.IngressRule{ 229 network.MustNewIngressRule("tcp", 80, 82), 230 network.MustNewIngressRule("tcp", 100, 120), 231 }, 232 expected: []neutron.RuleInfoV2{{ 233 Direction: "ingress", 234 IPProtocol: "tcp", 235 PortRangeMin: 80, 236 PortRangeMax: 82, 237 RemoteIPPrefix: "0.0.0.0/0", 238 ParentGroupId: groupId, 239 }, { 240 Direction: "ingress", 241 IPProtocol: "tcp", 242 PortRangeMin: 100, 243 PortRangeMax: 120, 244 RemoteIPPrefix: "0.0.0.0/0", 245 ParentGroupId: groupId, 246 }}, 247 }, { 248 about: "source range", 249 rules: []network.IngressRule{network.MustNewIngressRule( 250 "tcp", 80, 100, "192.168.1.0/24", "0.0.0.0/0")}, 251 expected: []neutron.RuleInfoV2{{ 252 Direction: "ingress", 253 IPProtocol: "tcp", 254 PortRangeMin: 80, 255 PortRangeMax: 100, 256 RemoteIPPrefix: "192.168.1.0/24", 257 ParentGroupId: groupId, 258 }, { 259 Direction: "ingress", 260 IPProtocol: "tcp", 261 PortRangeMin: 80, 262 PortRangeMax: 100, 263 RemoteIPPrefix: "0.0.0.0/0", 264 ParentGroupId: groupId, 265 }}, 266 }} 267 268 for i, t := range testCases { 269 c.Logf("test %d: %s", i, t.about) 270 rules := PortsToRuleInfo(groupId, t.rules) 271 c.Check(len(rules), gc.Equals, len(t.expected)) 272 c.Check(rules, gc.DeepEquals, t.expected) 273 } 274 } 275 276 func (*localTests) TestSecGroupMatchesIngressRule(c *gc.C) { 277 proto_tcp := "tcp" 278 proto_udp := "udp" 279 port_80 := 80 280 port_85 := 85 281 282 testCases := []struct { 283 about string 284 rule network.IngressRule 285 secGroupRule neutron.SecurityGroupRuleV2 286 expected bool 287 }{{ 288 about: "single port", 289 rule: network.MustNewIngressRule(proto_tcp, 80, 80), 290 secGroupRule: neutron.SecurityGroupRuleV2{ 291 IPProtocol: &proto_tcp, 292 PortRangeMin: &port_80, 293 PortRangeMax: &port_80, 294 }, 295 expected: true, 296 }, { 297 about: "multiple port", 298 rule: network.MustNewIngressRule(proto_tcp, 80, 85), 299 secGroupRule: neutron.SecurityGroupRuleV2{ 300 IPProtocol: &proto_tcp, 301 PortRangeMin: &port_80, 302 PortRangeMax: &port_85, 303 }, 304 expected: true, 305 }, { 306 about: "nil rule components", 307 rule: network.MustNewIngressRule(proto_tcp, 80, 85), 308 secGroupRule: neutron.SecurityGroupRuleV2{ 309 IPProtocol: nil, 310 PortRangeMin: nil, 311 PortRangeMax: nil, 312 }, 313 expected: false, 314 }, { 315 about: "nil rule component: PortRangeMin", 316 rule: network.MustNewIngressRule(proto_tcp, 80, 85, "0.0.0.0/0", "192.168.1.0/24"), 317 secGroupRule: neutron.SecurityGroupRuleV2{ 318 IPProtocol: &proto_tcp, 319 PortRangeMin: nil, 320 PortRangeMax: &port_85, 321 RemoteIPPrefix: "192.168.100.0/24", 322 }, 323 expected: false, 324 }, { 325 about: "nil rule component: PortRangeMax", 326 rule: network.MustNewIngressRule(proto_tcp, 80, 85, "0.0.0.0/0", "192.168.1.0/24"), 327 secGroupRule: neutron.SecurityGroupRuleV2{ 328 IPProtocol: &proto_tcp, 329 PortRangeMin: &port_85, 330 PortRangeMax: nil, 331 RemoteIPPrefix: "192.168.100.0/24", 332 }, 333 expected: false, 334 }, { 335 about: "mismatched port range and rule", 336 rule: network.MustNewIngressRule(proto_tcp, 80, 85), 337 secGroupRule: neutron.SecurityGroupRuleV2{ 338 IPProtocol: &proto_udp, 339 PortRangeMin: &port_80, 340 PortRangeMax: &port_80, 341 }, 342 expected: false, 343 }, { 344 about: "default RemoteIPPrefix", 345 rule: network.MustNewIngressRule(proto_tcp, 80, 85), 346 secGroupRule: neutron.SecurityGroupRuleV2{ 347 IPProtocol: &proto_tcp, 348 PortRangeMin: &port_80, 349 PortRangeMax: &port_85, 350 RemoteIPPrefix: "0.0.0.0/0", 351 }, 352 expected: true, 353 }, { 354 about: "matching RemoteIPPrefix", 355 rule: network.MustNewIngressRule(proto_tcp, 80, 85, "0.0.0.0/0", "192.168.1.0/24"), 356 secGroupRule: neutron.SecurityGroupRuleV2{ 357 IPProtocol: &proto_tcp, 358 PortRangeMin: &port_80, 359 PortRangeMax: &port_85, 360 RemoteIPPrefix: "192.168.1.0/24", 361 }, 362 expected: true, 363 }, { 364 about: "non-matching RemoteIPPrefix", 365 rule: network.MustNewIngressRule(proto_tcp, 80, 85, "0.0.0.0/0", "192.168.1.0/24"), 366 secGroupRule: neutron.SecurityGroupRuleV2{ 367 IPProtocol: &proto_tcp, 368 PortRangeMin: &port_80, 369 PortRangeMax: &port_85, 370 RemoteIPPrefix: "192.168.100.0/24", 371 }, 372 expected: false, 373 }} 374 for i, t := range testCases { 375 c.Logf("test %d: %s", i, t.about) 376 c.Check(SecGroupMatchesIngressRule(t.secGroupRule, t.rule), gc.Equals, t.expected) 377 } 378 } 379 380 func (s *localTests) TestDetectRegionsNoRegionName(c *gc.C) { 381 _, err := s.detectRegions(c) 382 c.Assert(err, gc.ErrorMatches, "OS_REGION_NAME environment variable not set") 383 } 384 385 func (s *localTests) TestDetectRegionsNoAuthURL(c *gc.C) { 386 s.PatchEnvironment("OS_REGION_NAME", "oceania") 387 _, err := s.detectRegions(c) 388 c.Assert(err, gc.ErrorMatches, "OS_AUTH_URL environment variable not set") 389 } 390 391 func (s *localTests) TestDetectRegions(c *gc.C) { 392 s.PatchEnvironment("OS_REGION_NAME", "oceania") 393 s.PatchEnvironment("OS_AUTH_URL", "http://keystone.internal") 394 regions, err := s.detectRegions(c) 395 c.Assert(err, jc.ErrorIsNil) 396 c.Assert(regions, jc.DeepEquals, []cloud.Region{ 397 {Name: "oceania", Endpoint: "http://keystone.internal"}, 398 }) 399 } 400 401 func (s *localTests) detectRegions(c *gc.C) ([]cloud.Region, error) { 402 provider, err := environs.Provider("openstack") 403 c.Assert(err, jc.ErrorIsNil) 404 c.Assert(provider, gc.Implements, new(environs.CloudRegionDetector)) 405 return provider.(environs.CloudRegionDetector).DetectRegions() 406 } 407 408 func (s *localTests) TestSchema(c *gc.C) { 409 y := []byte(` 410 auth-types: [userpass, access-key] 411 endpoint: http://foo.com/openstack 412 regions: 413 one: 414 endpoint: http://foo.com/bar 415 two: 416 endpoint: http://foo2.com/bar2 417 `[1:]) 418 var v interface{} 419 err := yaml.Unmarshal(y, &v) 420 c.Assert(err, jc.ErrorIsNil) 421 v, err = utils.ConformYAML(v) 422 c.Assert(err, jc.ErrorIsNil) 423 424 p, err := environs.Provider("openstack") 425 err = p.CloudSchema().Validate(v) 426 c.Assert(err, jc.ErrorIsNil) 427 } 428 429 func (localTests) TestPingInvalidHost(c *gc.C) { 430 tests := []string{ 431 "foo.com", 432 "http://IHopeNoOneEverBuysThisVerySpecificJujuDomainName.com", 433 "http://IHopeNoOneEverBuysThisVerySpecificJujuDomainName:77", 434 } 435 436 p, err := environs.Provider("openstack") 437 c.Assert(err, jc.ErrorIsNil) 438 callCtx := context.NewCloudCallContext() 439 for _, t := range tests { 440 err = p.Ping(callCtx, t) 441 if err == nil { 442 c.Errorf("ping %q: expected error, but got nil.", t) 443 continue 444 } 445 expected := "No Openstack server running at " + t 446 if err.Error() != expected { 447 c.Errorf("ping %q: expected %q got %v", t, expected, err) 448 } 449 } 450 } 451 func (localTests) TestPingNoEndpoint(c *gc.C) { 452 server := httptest.NewServer(http.HandlerFunc(http.NotFound)) 453 defer server.Close() 454 p, err := environs.Provider("openstack") 455 c.Assert(err, jc.ErrorIsNil) 456 err = p.Ping(context.NewCloudCallContext(), server.URL) 457 c.Assert(err, gc.ErrorMatches, "No Openstack server running at "+server.URL) 458 } 459 460 func (localTests) TestPingInvalidResponse(c *gc.C) { 461 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 462 fmt.Fprint(w, "Hi!") 463 })) 464 defer server.Close() 465 p, err := environs.Provider("openstack") 466 c.Assert(err, jc.ErrorIsNil) 467 err = p.Ping(context.NewCloudCallContext(), server.URL) 468 c.Assert(err, gc.ErrorMatches, "No Openstack server running at "+server.URL) 469 } 470 471 func (localTests) TestPingOKCACertificate(c *gc.C) { 472 server := httptest.NewTLSServer(handlerFunc) 473 defer server.Close() 474 pingOk(c, server) 475 } 476 477 func (localTests) TestPingOK(c *gc.C) { 478 server := httptest.NewServer(handlerFunc) 479 defer server.Close() 480 pingOk(c, server) 481 } 482 483 func pingOk(c *gc.C, server *httptest.Server) { 484 p, err := environs.Provider("openstack") 485 c.Assert(err, jc.ErrorIsNil) 486 err = p.Ping(context.NewCloudCallContext(), server.URL) 487 c.Assert(err, jc.ErrorIsNil) 488 } 489 490 var handlerFunc = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 491 // This line is critical, the openstack provider will reject the message 492 // if you return 200 like a mere mortal. 493 w.WriteHeader(http.StatusMultipleChoices) 494 fmt.Fprint(w, ` 495 { 496 "versions": { 497 "values": [ 498 { 499 "status": "stable", 500 "updated": "2013-03-06T00:00:00Z", 501 "media-types": [ 502 { 503 "base": "application/json", 504 "type": "application/vnd.openstack.identity-v3+json" 505 }, 506 { 507 "base": "application/xml", 508 "type": "application/vnd.openstack.identity-v3+xml" 509 } 510 ], 511 "id": "v3.0", 512 "links": [ 513 { 514 "href": "http://10.24.0.177:5000/v3/", 515 "rel": "self" 516 } 517 ] 518 }, 519 { 520 "status": "stable", 521 "updated": "2014-04-17T00:00:00Z", 522 "media-types": [ 523 { 524 "base": "application/json", 525 "type": "application/vnd.openstack.identity-v2.0+json" 526 }, 527 { 528 "base": "application/xml", 529 "type": "application/vnd.openstack.identity-v2.0+xml" 530 } 531 ], 532 "id": "v2.0", 533 "links": [ 534 { 535 "href": "http://10.24.0.177:5000/v2.0/", 536 "rel": "self" 537 }, 538 { 539 "href": "http://docs.openstack.org/api/openstack-identity-service/2.0/content/", 540 "type": "text/html", 541 "rel": "describedby" 542 }, 543 { 544 "href": "http://docs.openstack.org/api/openstack-identity-service/2.0/identity-dev-guide-2.0.pdf", 545 "type": "application/pdf", 546 "rel": "describedby" 547 } 548 ] 549 } 550 ] 551 } 552 } 553 `) 554 }) 555 556 type providerUnitTests struct { 557 gitjujutesting.IsolationSuite 558 } 559 560 var _ = gc.Suite(&providerUnitTests{}) 561 562 func checkIdentityClientVersionInvalid(c *gc.C, url string) { 563 _, err := identityClientVersion(url) 564 c.Check(err, gc.ErrorMatches, fmt.Sprintf("version part of identity url %s not valid", url)) 565 } 566 567 func checkIdentityClientVersion(c *gc.C, url string, expversion int) { 568 version, err := identityClientVersion(url) 569 c.Assert(err, jc.ErrorIsNil) 570 c.Check(version, gc.Equals, expversion) 571 } 572 func (s *providerUnitTests) TestIdentityClientVersion_BadURLErrors(c *gc.C) { 573 checkIdentityClientVersionInvalid(c, "https://keystone.internal/a") 574 checkIdentityClientVersionInvalid(c, "https://keystone.internal/v") 575 checkIdentityClientVersionInvalid(c, "https://keystone.internal/V") 576 checkIdentityClientVersionInvalid(c, "https://keystone.internal/V/") 577 checkIdentityClientVersionInvalid(c, "https://keystone.internal/100") 578 checkIdentityClientVersionInvalid(c, "https://keystone.internal/vot") 579 checkIdentityClientVersionInvalid(c, "https://keystone.internal/identity/vot") 580 checkIdentityClientVersionInvalid(c, "https://keystone.internal/identity/2") 581 582 _, err := identityClientVersion("abc123") 583 c.Check(err, gc.ErrorMatches, `url abc123 is malformed`) 584 } 585 586 func (s *providerUnitTests) TestIdentityClientVersion_ParsesGoodURL(c *gc.C) { 587 checkIdentityClientVersion(c, "https://keystone.internal/v2.0", 2) 588 checkIdentityClientVersion(c, "https://keystone.internal/v3.0/", 3) 589 checkIdentityClientVersion(c, "https://keystone.internal/v2/", 2) 590 checkIdentityClientVersion(c, "https://keystone.internal/V2/", 2) 591 checkIdentityClientVersion(c, "https://keystone.internal/internal/V2/", 2) 592 checkIdentityClientVersion(c, "https://keystone.internal/internal/v3.0/", 3) 593 checkIdentityClientVersion(c, "https://keystone.internal/internal/v3.2///", 3) 594 checkIdentityClientVersion(c, "https://keystone.internal", -1) 595 checkIdentityClientVersion(c, "https://keystone.internal/", -1) 596 } 597 598 func (s *providerUnitTests) TestNewCredentialsWithVersion3(c *gc.C) { 599 creds := cloud.NewCredential(cloud.UserPassAuthType, map[string]string{ 600 "version": "3", 601 "username": "user", 602 "password": "secret", 603 "tenant-name": "someTenant", 604 "tenant-id": "someID", 605 }) 606 clouldSpec := environs.CloudSpec{ 607 Type: "openstack", 608 Region: "openstack_region", 609 Name: "openstack", 610 Endpoint: "http://endpoint", 611 Credential: &creds, 612 } 613 cred, authmode, err := newCredentials(clouldSpec) 614 c.Assert(err, jc.ErrorIsNil) 615 c.Check(cred, gc.Equals, identity.Credentials{ 616 URL: "http://endpoint", 617 User: "user", 618 Secrets: "secret", 619 Region: "openstack_region", 620 TenantName: "someTenant", 621 TenantID: "someID", 622 Version: 3, 623 Domain: "", 624 UserDomain: "", 625 ProjectDomain: "", 626 }) 627 c.Check(authmode, gc.Equals, identity.AuthUserPassV3) 628 } 629 630 func (s *providerUnitTests) TestNewCredentialsWithFaultVersion(c *gc.C) { 631 creds := cloud.NewCredential(cloud.UserPassAuthType, map[string]string{ 632 "version": "abc", 633 "username": "user", 634 "password": "secret", 635 "tenant-name": "someTenant", 636 "tenant-id": "someID", 637 }) 638 clouldSpec := environs.CloudSpec{ 639 Type: "openstack", 640 Region: "openstack_region", 641 Name: "openstack", 642 Endpoint: "http://endpoint", 643 Credential: &creds, 644 } 645 _, _, err := newCredentials(clouldSpec) 646 c.Assert(err, gc.ErrorMatches, 647 "cred.Version is not a valid integer type : strconv.Atoi: parsing \"abc\": invalid syntax") 648 } 649 650 func (s *providerUnitTests) TestNewCredentialsWithoutVersion(c *gc.C) { 651 creds := cloud.NewCredential(cloud.UserPassAuthType, map[string]string{ 652 "username": "user", 653 "password": "secret", 654 "tenant-name": "someTenant", 655 "tenant-id": "someID", 656 }) 657 clouldSpec := environs.CloudSpec{ 658 Type: "openstack", 659 Region: "openstack_region", 660 Name: "openstack", 661 Endpoint: "http://endpoint", 662 Credential: &creds, 663 } 664 cred, authmode, err := newCredentials(clouldSpec) 665 c.Assert(err, jc.ErrorIsNil) 666 c.Check(cred, gc.Equals, identity.Credentials{ 667 URL: "http://endpoint", 668 User: "user", 669 Secrets: "secret", 670 Region: "openstack_region", 671 TenantName: "someTenant", 672 TenantID: "someID", 673 Domain: "", 674 UserDomain: "", 675 ProjectDomain: "", 676 }) 677 c.Check(authmode, gc.Equals, identity.AuthUserPass) 678 } 679 680 func (s *providerUnitTests) TestNewCredentialsWithFaultVersionandProjectDomainName(c *gc.C) { 681 creds := cloud.NewCredential(cloud.UserPassAuthType, map[string]string{ 682 "version": "abc", 683 "username": "user", 684 "password": "secret", 685 "tenant-name": "someTenant", 686 "tenant-id": "someID", 687 "project-domain-name": "openstack_projectdomain", 688 }) 689 clouldSpec := environs.CloudSpec{ 690 Type: "openstack", 691 Region: "openstack_region", 692 Name: "openstack", 693 Endpoint: "http://endpoint", 694 Credential: &creds, 695 } 696 _, _, err := newCredentials(clouldSpec) 697 c.Assert(err, gc.NotNil) 698 c.Assert(err, gc.ErrorMatches, 699 "cred.Version is not a valid integer type : strconv.Atoi: parsing \"abc\": invalid syntax") 700 } 701 func (s *providerUnitTests) TestNewCredentialsWithoutVersionwithProjectDomain(c *gc.C) { 702 creds := cloud.NewCredential(cloud.UserPassAuthType, map[string]string{ 703 "username": "user", 704 "password": "secret", 705 "tenant-name": "someTenant", 706 "tenant-id": "someID", 707 "project-domain-name": "openstack_projectdomain", 708 }) 709 clouldSpec := environs.CloudSpec{ 710 Type: "openstack", 711 Region: "openstack_region", 712 Name: "openstack", 713 Endpoint: "http://endpoint", 714 Credential: &creds, 715 } 716 cred, authmode, err := newCredentials(clouldSpec) 717 c.Assert(err, jc.ErrorIsNil) 718 c.Check(cred, gc.Equals, identity.Credentials{ 719 URL: "http://endpoint", 720 User: "user", 721 Secrets: "secret", 722 Region: "openstack_region", 723 TenantName: "someTenant", 724 TenantID: "someID", 725 Domain: "", 726 UserDomain: "", 727 ProjectDomain: "openstack_projectdomain", 728 }) 729 c.Check(authmode, gc.Equals, identity.AuthUserPassV3) 730 } 731 732 func (s *providerUnitTests) TestNewCredentialsWithoutVersionwithUserDomain(c *gc.C) { 733 creds := cloud.NewCredential(cloud.UserPassAuthType, map[string]string{ 734 "username": "user", 735 "password": "secret", 736 "tenant-name": "someTenant", 737 "tenant-id": "someID", 738 "user-domain-name": "openstack_userdomain", 739 }) 740 clouldSpec := environs.CloudSpec{ 741 Type: "openstack", 742 Region: "openstack_region", 743 Name: "openstack", 744 Endpoint: "http://endpoint", 745 Credential: &creds, 746 } 747 cred, authmode, err := newCredentials(clouldSpec) 748 c.Assert(err, jc.ErrorIsNil) 749 c.Check(cred, gc.Equals, identity.Credentials{ 750 URL: "http://endpoint", 751 User: "user", 752 Secrets: "secret", 753 Region: "openstack_region", 754 TenantName: "someTenant", 755 TenantID: "someID", 756 Version: 0, 757 Domain: "", 758 UserDomain: "openstack_userdomain", 759 ProjectDomain: "", 760 }) 761 c.Check(authmode, gc.Equals, identity.AuthUserPassV3) 762 } 763 764 func (s *providerUnitTests) TestNewCredentialsWithVersion2(c *gc.C) { 765 creds := cloud.NewCredential(cloud.UserPassAuthType, map[string]string{ 766 "version": "2", 767 "username": "user", 768 "password": "secret", 769 "tenant-name": "someTenant", 770 "tenant-id": "someID", 771 }) 772 clouldSpec := environs.CloudSpec{ 773 Type: "openstack", 774 Region: "openstack_region", 775 Name: "openstack", 776 Endpoint: "http://endpoint", 777 Credential: &creds, 778 } 779 cred, authmode, err := newCredentials(clouldSpec) 780 c.Assert(err, jc.ErrorIsNil) 781 c.Check(cred, gc.Equals, identity.Credentials{ 782 URL: "http://endpoint", 783 User: "user", 784 Secrets: "secret", 785 Region: "openstack_region", 786 TenantName: "someTenant", 787 TenantID: "someID", 788 Version: 2, 789 Domain: "", 790 UserDomain: "", 791 ProjectDomain: "", 792 }) 793 c.Check(authmode, gc.Equals, identity.AuthUserPass) 794 } 795 796 func (s *providerUnitTests) TestNewCredentialsWithVersion2AndDomain(c *gc.C) { 797 creds := cloud.NewCredential(cloud.UserPassAuthType, map[string]string{ 798 "version": "2", 799 "username": "user", 800 "password": "secret", 801 "tenant-name": "someTenant", 802 "tenant-id": "someID", 803 "project-domain-name": "openstack_projectdomain", 804 }) 805 clouldSpec := environs.CloudSpec{ 806 Type: "openstack", 807 Region: "openstack_region", 808 Name: "openstack", 809 Endpoint: "http://endpoint", 810 Credential: &creds, 811 } 812 cred, authmode, err := newCredentials(clouldSpec) 813 c.Assert(err, jc.ErrorIsNil) 814 c.Check(cred, gc.Equals, identity.Credentials{ 815 URL: "http://endpoint", 816 User: "user", 817 Secrets: "secret", 818 Region: "openstack_region", 819 TenantName: "someTenant", 820 TenantID: "someID", 821 Version: 2, 822 Domain: "", 823 UserDomain: "", 824 ProjectDomain: "openstack_projectdomain", 825 }) 826 c.Check(authmode, gc.Equals, identity.AuthUserPass) 827 }