6
. "launchpad.net/gocheck"
7
"launchpad.net/goose/client"
8
"launchpad.net/goose/errors"
9
"launchpad.net/goose/identity"
10
"launchpad.net/goose/testing/httpsuite"
11
"launchpad.net/goose/testservices"
12
"launchpad.net/goose/testservices/identityservice"
13
"launchpad.net/goose/testservices/openstackservice"
20
func registerLocalTests(authModes []identity.AuthMode) {
21
for _, authMode := range authModes {
22
Suite(&localLiveSuite{
30
// localLiveSuite runs tests from LiveTests using a fake
31
// identity server that runs within the test process itself.
32
type localLiveSuite struct {
34
// The following attributes are for using testing doubles.
36
service testservices.HttpService
39
func (s *localLiveSuite) SetUpSuite(c *C) {
40
c.Logf("Using identity service test double")
41
s.HTTPSuite.SetUpSuite(c)
42
s.cred = &identity.Credentials{
46
Region: "zone1.some region",
51
panic("Invalid authentication method")
52
case identity.AuthUserPass:
53
// The openstack test service sets up userpass authentication.
54
s.service = openstackservice.New(s.cred)
55
// Add an additional endpoint so region filtering can be properly tested.
56
serviceDef := identityservice.Service{"nova", "compute", []identityservice.Endpoint{
57
identityservice.Endpoint{PublicURL: "http://nova2", Region: "zone2.RegionOne"},
59
s.service.(*openstackservice.Openstack).Identity.(*identityservice.UserPass).AddService(serviceDef)
61
case identity.AuthLegacy:
62
legacy := identityservice.NewLegacy()
63
legacy.AddUser(s.cred.User, s.cred.Secrets, s.cred.TenantName)
64
legacy.SetManagementURL("http://management.test.invalid/url")
67
s.LiveTests.SetUpSuite(c)
70
func (s *localLiveSuite) TearDownSuite(c *C) {
71
s.LiveTests.TearDownSuite(c)
72
s.HTTPSuite.TearDownSuite(c)
75
func (s *localLiveSuite) SetUpTest(c *C) {
76
s.HTTPSuite.SetUpTest(c)
77
s.service.SetupHTTP(s.Mux)
78
s.LiveTests.SetUpTest(c)
81
func (s *localLiveSuite) TearDownTest(c *C) {
82
s.LiveTests.TearDownTest(c)
83
s.HTTPSuite.TearDownTest(c)
86
// Additional tests to be run against the service double only go here.
88
func (s *localLiveSuite) TestInvalidRegion(c *C) {
89
if s.authMode == identity.AuthLegacy {
90
c.Skip("legacy authentication doesn't use regions")
92
creds := &identity.Credentials{
98
cl := client.NewClient(creds, s.authMode, nil)
99
err := cl.Authenticate()
100
c.Assert(err.Error(), Matches, "(.|\n)*invalid region(.|\n)*")
103
// Test service lookup with inexact region matching.
104
func (s *localLiveSuite) TestInexactRegionMatch(c *C) {
105
if s.authMode == identity.AuthLegacy {
106
c.Skip("legacy authentication doesn't use regions")
108
cl := client.NewClient(s.cred, s.authMode, nil)
109
err := cl.Authenticate()
110
serviceURL, err := cl.MakeServiceURL("compute", []string{})
112
_, err = url.Parse(serviceURL)
114
serviceURL, err = cl.MakeServiceURL("object-store", []string{})
116
_, err = url.Parse(serviceURL)
120
type fakeAuthenticator struct {
123
// authStart is used as a gate to signal the fake authenticator that it can start.
124
authStart chan struct{}
127
func newAuthenticator(bufsize int) *fakeAuthenticator {
128
return &fakeAuthenticator{
129
authStart: make(chan struct{}, bufsize),
133
func (auth *fakeAuthenticator) Auth(creds *identity.Credentials) (*identity.AuthDetails, error) {
137
// Wait till the test says the authenticator can proceed.
146
tooManyCallers := auth.nrCallers > 1
149
return nil, fmt.Errorf("Too many callers of Auth function")
151
URLs := make(map[string]identity.ServiceURLs)
152
endpoints := make(map[string]string)
153
endpoints["compute"] = "http://localhost"
154
URLs[creds.Region] = endpoints
155
return &identity.AuthDetails{
159
RegionServiceURLs: URLs,
163
func (s *localLiveSuite) TestAuthenticationTimeout(c *C) {
164
cl := client.NewClient(s.cred, s.authMode, nil)
165
defer client.SetAuthenticationTimeout(1 * time.Millisecond)()
166
auth := newAuthenticator(0)
167
client.SetAuthenticator(cl, auth)
170
err = cl.Authenticate()
171
// Wake up the authenticator after we have timed out.
172
auth.authStart <- struct{}{}
173
c.Assert(errors.IsTimeout(err), Equals, true)
176
func (s *localLiveSuite) assertAuthenticationSuccess(c *C) client.Client {
177
cl := client.NewClient(s.cred, s.authMode, nil)
178
cl.SetRequiredServiceTypes([]string{"compute"})
179
defer client.SetAuthenticationTimeout(1 * time.Millisecond)()
180
auth := newAuthenticator(1)
181
client.SetAuthenticator(cl, auth)
183
// Signal that the authenticator can proceed immediately.
184
auth.authStart <- struct{}{}
185
err := cl.Authenticate()
187
// It completed with no error but check it also ran correctly.
188
c.Assert(cl.IsAuthenticated(), Equals, true)
192
func (s *localLiveSuite) TestAuthenticationSuccess(c *C) {
193
cl := s.assertAuthenticationSuccess(c)
194
URL, err := cl.MakeServiceURL("compute", nil)
196
c.Assert(URL, Equals, "http://localhost")
199
func (s *localLiveSuite) TestMakeServiceURL(c *C) {
200
cl := s.assertAuthenticationSuccess(c)
201
URL, err := cl.MakeServiceURL("compute", []string{"foo"})
203
c.Assert(URL, Equals, "http://localhost/foo")
206
func (s *localLiveSuite) TestMakeServiceURLRetainsTrailingSlash(c *C) {
207
cl := s.assertAuthenticationSuccess(c)
208
URL, err := cl.MakeServiceURL("compute", []string{"foo", "bar/"})
210
c.Assert(URL, Equals, "http://localhost/foo/bar/")
213
func checkAuthentication(cl client.AuthenticatingClient) error {
214
err := cl.Authenticate()
218
URL, err := cl.MakeServiceURL("compute", nil)
222
if URL != "http://localhost" {
223
return fmt.Errorf("Unexpected URL: %s", URL)
228
func (s *localLiveSuite) TestAuthenticationForbidsMultipleCallers(c *C) {
229
if s.authMode == identity.AuthLegacy {
230
c.Skip("legacy authentication")
232
cl := client.NewClient(s.cred, s.authMode, nil)
233
cl.SetRequiredServiceTypes([]string{"compute"})
234
auth := newAuthenticator(2)
235
client.SetAuthenticator(cl, auth)
237
// Signal that the authenticator can proceed immediately.
238
auth.authStart <- struct{}{}
239
auth.authStart <- struct{}{}
240
var allDone sync.WaitGroup
244
err1 = checkAuthentication(cl)
248
err2 = checkAuthentication(cl)
252
c.Assert(err1, IsNil)
253
c.Assert(err2, IsNil)
256
type configurableAuth struct {
257
regionsURLs map[string]identity.ServiceURLs
260
func NewConfigurableAuth(regionsURLData string) *configurableAuth {
261
auth := &configurableAuth{}
262
err := json.Unmarshal([]byte(regionsURLData), &auth.regionsURLs)
269
func (auth *configurableAuth) Auth(creds *identity.Credentials) (*identity.AuthDetails, error) {
270
return &identity.AuthDetails{
274
RegionServiceURLs: auth.regionsURLs,
278
type authRegionTest struct {
284
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"
285
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"
286
var invalidRegionMsgf = "(.|\n)*invalid region %q"
288
var authRegionTests = []authRegionTest{
291
`{"a.region.1":{"compute":"http://foo"}}`,
292
fmt.Sprintf(missingEndpointMsgf, "a.region.1", "compute, object-store", "object-store"),
296
`{"a.region.1":{"compute":"http://foo"}}`,
297
fmt.Sprintf(invalidRegionMsgf, "b.region.1"),
301
`{"a.region.1":{"compute":"http://foo"}, "region.1":{"object-store":"http://foobar"}}`,
302
fmt.Sprintf(missingEndpointSuggestRegionMsgf, "b.region.1", "compute, object-store", "compute", "a.region.1"),
306
`{"a.region.1":{"compute":"http://foo"}, "region.1":{"object-store":"http://foobar"}}`,
307
fmt.Sprintf(missingEndpointSuggestRegionMsgf, "region.1", "compute, object-store", "compute", "a.region.1"),
311
func (s *localLiveSuite) TestNonAccessibleServiceType(c *C) {
312
if s.authMode == identity.AuthLegacy {
313
c.Skip("legacy authentication")
315
for _, at := range authRegionTests {
316
s.cred.Region = at.region
317
cl := client.NewClient(s.cred, s.authMode, nil)
318
auth := NewConfigurableAuth(at.regionURLInfo)
319
client.SetAuthenticator(cl, auth)
320
err := cl.Authenticate()
321
c.Assert(err, ErrorMatches, at.errorMsg)