11
gc "gopkg.in/check.v1"
13
"gopkg.in/goose.v1/client"
14
"gopkg.in/goose.v1/errors"
15
"gopkg.in/goose.v1/identity"
16
"gopkg.in/goose.v1/swift"
17
"gopkg.in/goose.v1/testing/httpsuite"
18
"gopkg.in/goose.v1/testservices"
19
"gopkg.in/goose.v1/testservices/identityservice"
20
"gopkg.in/goose.v1/testservices/openstackservice"
23
func registerLocalTests(authModes []identity.AuthMode) {
24
for _, authMode := range authModes {
25
gc.Suite(&localLiveSuite{
31
gc.Suite(&localHTTPSSuite{HTTPSuite: httpsuite.HTTPSuite{UseTLS: true}})
34
// localLiveSuite runs tests from LiveTests using a fake
35
// identity server that runs within the test process itself.
36
type localLiveSuite struct {
38
// The following attributes are for using testing doubles.
40
service testservices.HttpService
43
func (s *localLiveSuite) SetUpSuite(c *gc.C) {
44
c.Logf("Using identity service test double")
45
s.HTTPSuite.SetUpSuite(c)
46
s.cred = &identity.Credentials{
50
Region: "zone1.some region",
55
panic("Invalid authentication method")
56
case identity.AuthKeyPair:
57
// The openstack test service sets up keypair authentication.
58
s.service = openstackservice.New(s.cred, identity.AuthKeyPair)
59
// Add an additional endpoint so region filtering can be properly tested.
60
serviceDef := identityservice.Service{
61
V2: identityservice.V2Service{
64
Endpoints: []identityservice.Endpoint{
65
{PublicURL: "http://nova2", Region: "zone2.RegionOne"},
68
s.service.(*openstackservice.Openstack).Identity.AddService(serviceDef)
69
case identity.AuthUserPass:
70
// The openstack test service sets up userpass authentication.
71
s.service = openstackservice.New(s.cred, identity.AuthUserPass)
72
// Add an additional endpoint so region filtering can be properly tested.
73
serviceDef := identityservice.Service{
74
V2: identityservice.V2Service{
77
Endpoints: []identityservice.Endpoint{
78
{PublicURL: "http://nova2", Region: "zone2.RegionOne"},
81
s.service.(*openstackservice.Openstack).Identity.AddService(serviceDef)
82
case identity.AuthUserPassV3:
83
// The openstack test service sets up userpass authentication.
84
s.service = openstackservice.New(s.cred, identity.AuthUserPass)
85
// Add an additional endpoint so region filtering can be properly tested.
86
serviceDef := identityservice.Service{
87
V3: identityservice.V3Service{
90
Endpoints: identityservice.NewV3Endpoints("", "", "http://nova2", "zone2.RegionOne"),
92
s.service.(*openstackservice.Openstack).Identity.AddService(serviceDef)
94
case identity.AuthLegacy:
95
legacy := identityservice.NewLegacy()
96
legacy.AddUser(s.cred.User, s.cred.Secrets, s.cred.TenantName)
97
legacy.SetManagementURL("http://management.test.invalid/url")
100
s.LiveTests.SetUpSuite(c)
103
func (s *localLiveSuite) TearDownSuite(c *gc.C) {
104
s.LiveTests.TearDownSuite(c)
105
s.HTTPSuite.TearDownSuite(c)
108
func (s *localLiveSuite) SetUpTest(c *gc.C) {
109
s.HTTPSuite.SetUpTest(c)
110
s.service.SetupHTTP(s.Mux)
111
s.LiveTests.SetUpTest(c)
114
func (s *localLiveSuite) TearDownTest(c *gc.C) {
115
s.LiveTests.TearDownTest(c)
116
s.HTTPSuite.TearDownTest(c)
119
// Additional tests to be run against the service double only go here.
121
func (s *localLiveSuite) TestInvalidRegion(c *gc.C) {
122
if s.authMode == identity.AuthLegacy {
123
c.Skip("legacy authentication doesn't use regions")
125
creds := &identity.Credentials{
131
cl := client.NewClient(creds, s.authMode, nil)
132
err := cl.Authenticate()
133
c.Assert(err.Error(), gc.Matches, "(.|\n)*invalid region(.|\n)*")
136
// Test service lookup with inexact region matching.
137
func (s *localLiveSuite) TestInexactRegionMatch(c *gc.C) {
138
if s.authMode == identity.AuthLegacy {
139
c.Skip("legacy authentication doesn't use regions")
141
cl := client.NewClient(s.cred, s.authMode, nil)
142
err := cl.Authenticate()
143
serviceURL, err := cl.MakeServiceURL("compute", []string{})
144
c.Assert(err, gc.IsNil)
145
_, err = url.Parse(serviceURL)
146
c.Assert(err, gc.IsNil)
147
serviceURL, err = cl.MakeServiceURL("object-store", []string{})
148
c.Assert(err, gc.IsNil)
149
_, err = url.Parse(serviceURL)
150
c.Assert(err, gc.IsNil)
153
type fakeAuthenticator struct {
156
// authStart is used as a gate to signal the fake authenticator that it can start.
157
authStart chan struct{}
160
func newAuthenticator(bufsize int) *fakeAuthenticator {
161
return &fakeAuthenticator{
162
authStart: make(chan struct{}, bufsize),
166
func (auth *fakeAuthenticator) Auth(creds *identity.Credentials) (*identity.AuthDetails, error) {
170
// Wait till the test says the authenticator can proceed.
179
tooManyCallers := auth.nrCallers > 1
182
return nil, fmt.Errorf("Too many callers of Auth function")
184
URLs := make(map[string]identity.ServiceURLs)
185
endpoints := make(map[string]string)
186
endpoints["compute"] = "http://localhost"
187
URLs[creds.Region] = endpoints
188
return &identity.AuthDetails{
192
RegionServiceURLs: URLs,
196
func (s *localLiveSuite) TestAuthenticationTimeout(c *gc.C) {
197
cl := client.NewClient(s.cred, s.authMode, nil)
198
defer client.SetAuthenticationTimeout(1 * time.Millisecond)()
199
auth := newAuthenticator(0)
200
client.SetAuthenticator(cl, auth)
203
err = cl.Authenticate()
204
// Wake up the authenticator after we have timed out.
205
auth.authStart <- struct{}{}
206
c.Assert(errors.IsTimeout(err), gc.Equals, true)
209
func (s *localLiveSuite) assertAuthenticationSuccess(c *gc.C) client.Client {
210
cl := client.NewClient(s.cred, s.authMode, nil)
211
cl.SetRequiredServiceTypes([]string{"compute"})
212
defer client.SetAuthenticationTimeout(1 * time.Millisecond)()
213
auth := newAuthenticator(1)
214
client.SetAuthenticator(cl, auth)
216
// Signal that the authenticator can proceed immediately.
217
auth.authStart <- struct{}{}
218
err := cl.Authenticate()
219
c.Assert(err, gc.IsNil)
220
// It completed with no error but check it also ran correctly.
221
c.Assert(cl.IsAuthenticated(), gc.Equals, true)
225
func (s *localLiveSuite) TestAuthenticationSuccess(c *gc.C) {
226
cl := s.assertAuthenticationSuccess(c)
227
URL, err := cl.MakeServiceURL("compute", nil)
228
c.Assert(err, gc.IsNil)
229
c.Assert(URL, gc.Equals, "http://localhost")
232
func (s *localLiveSuite) TestMakeServiceURL(c *gc.C) {
233
cl := s.assertAuthenticationSuccess(c)
234
URL, err := cl.MakeServiceURL("compute", []string{"foo"})
235
c.Assert(err, gc.IsNil)
236
c.Assert(URL, gc.Equals, "http://localhost/foo")
239
func (s *localLiveSuite) TestMakeServiceURLRetainsTrailingSlash(c *gc.C) {
240
cl := s.assertAuthenticationSuccess(c)
241
URL, err := cl.MakeServiceURL("compute", []string{"foo", "bar/"})
242
c.Assert(err, gc.IsNil)
243
c.Assert(URL, gc.Equals, "http://localhost/foo/bar/")
246
func checkAuthentication(cl client.AuthenticatingClient) error {
247
err := cl.Authenticate()
251
URL, err := cl.MakeServiceURL("compute", nil)
255
if URL != "http://localhost" {
256
return fmt.Errorf("Unexpected URL: %s", URL)
261
func (s *localLiveSuite) TestAuthenticationForbidsMultipleCallers(c *gc.C) {
262
if s.authMode == identity.AuthLegacy {
263
c.Skip("legacy authentication")
265
cl := client.NewClient(s.cred, s.authMode, nil)
266
cl.SetRequiredServiceTypes([]string{"compute"})
267
auth := newAuthenticator(2)
268
client.SetAuthenticator(cl, auth)
270
// Signal that the authenticator can proceed immediately.
271
auth.authStart <- struct{}{}
272
auth.authStart <- struct{}{}
273
var allDone sync.WaitGroup
277
err1 = checkAuthentication(cl)
281
err2 = checkAuthentication(cl)
285
c.Assert(err1, gc.IsNil)
286
c.Assert(err2, gc.IsNil)
289
type configurableAuth struct {
290
regionsURLs map[string]identity.ServiceURLs
293
func NewConfigurableAuth(regionsURLData string) *configurableAuth {
294
auth := &configurableAuth{}
295
err := json.Unmarshal([]byte(regionsURLData), &auth.regionsURLs)
302
func (auth *configurableAuth) Auth(creds *identity.Credentials) (*identity.AuthDetails, error) {
303
return &identity.AuthDetails{
307
RegionServiceURLs: auth.regionsURLs,
311
type authRegionTest struct {
317
var missingEndpointMsgf = "(.|\n)*the configured region %q does not allow access to all required services, namely: %s(.|\n)*access to these services is missing: %s"
318
var missingEndpointSuggestRegionMsgf = "(.|\n)*the configured region %q does not allow access to all required services, namely: %s(.|\n)*access to these services is missing: %s(.|\n)*one of these regions may be suitable instead: %s"
319
var invalidRegionMsgf = "(.|\n)*invalid region %q"
321
var authRegionTests = []authRegionTest{
324
`{"a.region.1":{"compute":"http://foo"}}`,
325
fmt.Sprintf(missingEndpointMsgf, "a.region.1", "compute, object-store", "object-store"),
329
`{"a.region.1":{"compute":"http://foo"}}`,
330
fmt.Sprintf(invalidRegionMsgf, "b.region.1"),
334
`{"a.region.1":{"compute":"http://foo"}, "region.1":{"object-store":"http://foobar"}}`,
335
fmt.Sprintf(missingEndpointSuggestRegionMsgf, "b.region.1", "compute, object-store", "compute", "a.region.1"),
339
`{"a.region.1":{"compute":"http://foo"}, "region.1":{"object-store":"http://foobar"}}`,
340
fmt.Sprintf(missingEndpointSuggestRegionMsgf, "region.1", "compute, object-store", "compute", "a.region.1"),
344
func (s *localLiveSuite) TestNonAccessibleServiceType(c *gc.C) {
345
if s.authMode == identity.AuthLegacy {
346
c.Skip("legacy authentication")
348
for _, at := range authRegionTests {
349
s.cred.Region = at.region
350
cl := client.NewClient(s.cred, s.authMode, nil)
351
auth := NewConfigurableAuth(at.regionURLInfo)
352
client.SetAuthenticator(cl, auth)
353
err := cl.Authenticate()
354
c.Assert(err, gc.ErrorMatches, at.errorMsg)
358
type localHTTPSSuite struct {
359
// The following attributes are for using testing doubles.
361
service testservices.HttpService
362
cred *identity.Credentials
365
func (s *localHTTPSSuite) SetUpSuite(c *gc.C) {
366
c.Logf("Using identity service test double")
367
s.HTTPSuite.SetUpSuite(c)
368
c.Assert(s.Server.URL[:8], gc.Equals, "https://")
369
s.cred = &identity.Credentials{
373
Region: "zone1.some region",
374
TenantName: "tenant",
376
// The openstack test service sets up userpass authentication.
377
s.service = openstackservice.New(s.cred, identity.AuthUserPass)
378
// Add an additional endpoint so region filtering can be properly tested.
379
serviceDef := identityservice.Service{
380
V2: identityservice.V2Service{
383
Endpoints: []identityservice.Endpoint{
384
{PublicURL: "https://nova2", Region: "zone2.RegionOne"},
387
s.service.(*openstackservice.Openstack).Identity.AddService(serviceDef)
390
func (s *localHTTPSSuite) TearDownSuite(c *gc.C) {
391
s.HTTPSuite.TearDownSuite(c)
394
func (s *localHTTPSSuite) SetUpTest(c *gc.C) {
395
s.HTTPSuite.SetUpTest(c)
396
s.service.SetupHTTP(s.Mux)
399
func (s *localHTTPSSuite) TearDownTest(c *gc.C) {
400
s.HTTPSuite.TearDownTest(c)
403
func (s *localHTTPSSuite) TestDefaultClientRefusesSelfSigned(c *gc.C) {
404
cl := client.NewClient(s.cred, identity.AuthUserPass, nil)
405
err := cl.Authenticate()
406
c.Assert(err, gc.ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority")
409
func (s *localHTTPSSuite) TestNonValidatingClientAcceptsSelfSigned(c *gc.C) {
410
cl := client.NewNonValidatingClient(s.cred, identity.AuthUserPass, nil)
411
err := cl.Authenticate()
412
c.Assert(err, gc.IsNil)
414
// Requests into this client should be https:// URLs
415
swiftURL, err := cl.MakeServiceURL("object-store", []string{"test_container"})
416
c.Assert(err, gc.IsNil)
417
c.Assert(swiftURL[:8], gc.Equals, "https://")
418
// We use swiftClient.CreateContainer to test a Binary request
419
swiftClient := swift.New(cl)
420
c.Assert(swiftClient.CreateContainer("test_container", swift.Private), gc.IsNil)
422
// And we use List to test the JsonRequest
423
contents, err := swiftClient.List("test_container", "", "", "", 0)
424
c.Assert(err, gc.IsNil)
425
c.Check(contents, gc.DeepEquals, []swift.ContainerContents{})
428
func (s *localHTTPSSuite) setupPublicContainer(c *gc.C) string {
429
// First set up a container that can be read publically
430
authClient := client.NewNonValidatingClient(s.cred, identity.AuthUserPass, nil)
431
authSwift := swift.New(authClient)
432
err := authSwift.CreateContainer("test_container", swift.PublicRead)
433
c.Assert(err, gc.IsNil)
435
baseURL, err := authClient.MakeServiceURL("object-store", nil)
436
c.Assert(err, gc.IsNil)
437
c.Assert(baseURL[:8], gc.Equals, "https://")
441
func (s *localHTTPSSuite) TestDefaultPublicClientRefusesSelfSigned(c *gc.C) {
442
baseURL := s.setupPublicContainer(c)
443
swiftClient := swift.New(client.NewPublicClient(baseURL, nil))
444
contents, err := swiftClient.List("test_container", "", "", "", 0)
445
c.Assert(err, gc.ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority")
446
c.Assert(contents, gc.DeepEquals, []swift.ContainerContents(nil))
449
func (s *localHTTPSSuite) TestNonValidatingPublicClientAcceptsSelfSigned(c *gc.C) {
450
baseURL := s.setupPublicContainer(c)
451
swiftClient := swift.New(client.NewNonValidatingPublicClient(baseURL, nil))
452
contents, err := swiftClient.List("test_container", "", "", "", 0)
453
c.Assert(err, gc.IsNil)
454
c.Assert(contents, gc.DeepEquals, []swift.ContainerContents{})
457
func (s *localHTTPSSuite) TestAuthDiscover(c *gc.C) {
458
cl := client.NewNonValidatingClient(s.cred, identity.AuthUserPass, nil)
459
options, err := cl.IdentityAuthOptions()
460
c.Assert(err, gc.IsNil)
461
c.Assert(options, gc.DeepEquals, identity.AuthOptions{identity.AuthOption{Mode: 3, Endpoint: s.cred.URL + "/v3/"}, identity.AuthOption{Mode: 1, Endpoint: s.cred.URL + "/v2.0/"}})