Merge lp:~wallyworld/goose/rate-limit-retry-tests into lp:~gophers/goose/trunk

Proposed by Ian Booth
Status: Merged
Merged at revision: 55
Proposed branch: lp:~wallyworld/goose/rate-limit-retry-tests
Merge into: lp:~gophers/goose/trunk
Prerequisite: lp:~wallyworld/goose/control-test-doubles
Diff against target: 382 lines (+138/-51) (has conflicts)
7 files modified
http/client.go (+14/-8)
identity/userpass.go (+6/-0)
nova/live_test.go (+36/-0)
nova/local_test.go (+50/-13)
testservices/novaservice/service.go (+10/-14)
testservices/novaservice/service_http.go (+17/-16)
testservices/service.go (+5/-0)
Text conflict in identity/userpass.go
To merge this branch: bzr merge lp:~wallyworld/goose/rate-limit-retry-tests
Reviewer Review Type Date Requested Status
The Go Language Gophers Pending
Review via email: mp+144849@code.launchpad.net

Description of the change

Rewrite rate limit retry tests

This branch uses the new service double control hooks introduced in the previous branch to rewrite the tests which check that rate limit exceeded retries are handled properly.
The kludge used previously to induce a retry error has been removed, and now an additional test can also be easily added to check the behaviour if too many rate limit retry responses are received.

So that the tests run fast, I've allowed for the Retry-After header value to be a float (even though a real instance only assigns an int). This means the tests can specify a really
short retry time (I used 1ms).

I've also re-added a rate limit retry test for use with the live instance, but improved it so that it exists as soon as the first rate limit exceeded response is received.

https://codereview.appspot.com/7200049/

To post a comment you must log in.
Revision history for this message
Ian Booth (wallyworld) wrote :

Reviewers: mp+144849_code.launchpad.net,

Message:
Please take a look.

Description:
Rewrite rate limit retry tests

This branch uses the new service double control hooks introduced in the
previous branch to rewrite the tests which check that rate limit
exceeded retries are handled properly.
The kludge used previously to induce a retry error has been removed, and
now an additional test can also be easily added to check the behaviour
if too many rate limit retry responses are received.

So that the tests run fast, I've allowed for the Retry-After header
value to be a float (even though a real instance only assigns an int).
This means the tests can specify a really
short retry time (I used 1ms).

I've also re-added a rate limit retry test for use with the live
instance, but improved it so that it exists as soon as the first rate
limit exceeded response is received.

https://code.launchpad.net/~wallyworld/goose/rate-limit-retry-tests/+merge/144849

Requires:
https://code.launchpad.net/~wallyworld/goose/control-test-doubles/+merge/144838

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/7200049/

Affected files:
   A [revision details]
   M http/client.go
   M identity/userpass.go
   M nova/live_test.go
   M nova/local_test.go
   M testservices/novaservice/service.go
   M testservices/novaservice/service_http.go
   M testservices/service.go

Revision history for this message
John A Meinel (jameinel) wrote :

Overall LGTM.

https://codereview.appspot.com/7200049/diff/1/http/client.go
File http/client.go (right):

https://codereview.appspot.com/7200049/diff/1/http/client.go#newcode195
http/client.go:195: for i := 0; i <= c.maxRetries; i++ {
<= ? Doesn't that mean we will try 4 times instead of 3?

https://codereview.appspot.com/7200049/diff/1/identity/userpass.go
File identity/userpass.go (right):

https://codereview.appspot.com/7200049/diff/1/identity/userpass.go#newcode109
identity/userpass.go:109: }
I think I have a more complete fix for this:
https://code.launchpad.net/~jameinel/goose/handle-missing-endpoint/+merge/145098

https://codereview.appspot.com/7200049/diff/1/nova/live_test.go
File nova/live_test.go (right):

https://codereview.appspot.com/7200049/diff/1/nova/live_test.go#newcode452
nova/live_test.go:452: return
Is it possible to use your hooks to check for a retry request, rather
than reading the output string? (I realize this probably didn't exist
before, but it seems like a really good use of it.)

https://codereview.appspot.com/7200049/diff/1/testservices/novaservice/service_http.go
File testservices/novaservice/service_http.go (right):

https://codereview.appspot.com/7200049/diff/1/testservices/novaservice/service_http.go#newcode216
testservices/novaservice/service_http.go:216: // Retry-After is usually
an int but we don't want the tests taking too long to run.
Maybe: RFC says that Retry-After should be an int, but we don't want to
wait an entire second during the test suite.

https://codereview.appspot.com/7200049/

55. By Ian Booth

Merge upstream and resolve conflicts

56. By Ian Booth

Code review tweaks

Revision history for this message
Ian Booth (wallyworld) wrote :

Please take a look.

https://codereview.appspot.com/7200049/diff/1/http/client.go
File http/client.go (right):

https://codereview.appspot.com/7200049/diff/1/http/client.go#newcode195
http/client.go:195: for i := 0; i <= c.maxRetries; i++ {
On 2013/01/28 06:44:57, jameinel wrote:
> <= ? Doesn't that mean we will try 4 times instead of 3?

The logic was confusing retries with total attempts. Using "retries"
terminology, the client sends the first request and retries X times.
With "sendAttempts" terminology, the client tries "sendAttempts"
requests in total.

I've change the variable and constant to be maxSendAttempts to remove
the ambiguity.

https://codereview.appspot.com/7200049/diff/1/identity/userpass.go
File identity/userpass.go (right):

https://codereview.appspot.com/7200049/diff/1/identity/userpass.go#newcode109
identity/userpass.go:109: }
On 2013/01/28 06:44:57, jameinel wrote:
> I think I have a more complete fix for this:

https://code.launchpad.net/%7Ejameinel/goose/handle-missing-endpoint/+merge/145098

I had a quick look, the mp was missing the pre-req branch so was very
large. Looked ok at first glance. Perhaps we can land this branch now as
is and follow up with yours?

https://codereview.appspot.com/7200049/diff/1/nova/live_test.go
File nova/live_test.go (right):

https://codereview.appspot.com/7200049/diff/1/nova/live_test.go#newcode452
nova/live_test.go:452: return
On 2013/01/28 06:44:57, jameinel wrote:
> Is it possible to use your hooks to check for a retry request, rather
than
> reading the output string? (I realize this probably didn't exist
before, but it
> seems like a really good use of it.)

Possibly, but the hook stuff has been set up to work with the test
doubles (manipulates the state of the test service back end) rather than
a real live instance. The nova client implementation would need to be
retooled to implement the ServiceControl interface. Perhaps this can be
done in a followup branch.

https://codereview.appspot.com/7200049/diff/1/testservices/novaservice/service_http.go
File testservices/novaservice/service_http.go (right):

https://codereview.appspot.com/7200049/diff/1/testservices/novaservice/service_http.go#newcode216
testservices/novaservice/service_http.go:216: // Retry-After is usually
an int but we don't want the tests taking too long to run.
On 2013/01/28 06:44:57, jameinel wrote:
> Maybe: RFC says that Retry-After should be an int, but we don't want
to wait an
> entire second during the test suite.

Done.

https://codereview.appspot.com/7200049/

Revision history for this message
John A Meinel (jameinel) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

...
> https://code.launchpad.net/%7Ejameinel/goose/handle-missing-endpoint/+merge/145098
>
> I had a quick look, the mp was missing the pre-req branch so was
> very large. Looked ok at first glance. Perhaps we can land this
> branch now as is and follow up with yours?

Yeah, goose-bot's trunk wasn't up to date with lp:goose, and which I
based my patch on. I tried to re-push so this should be cleaned up.

As for your patch, I think it is an accurate fix, but my concern is
just a more general thing. When we encounter edge cases the code
wasn't covering (a service that is only exported in one region), we
really need a test case for it, because those are the sorts of things
we are going to forget about in 6 months when we go tweaking things
for HP vs Canonistack vs Rackspace, etc.

>
> https://codereview.appspot.com/7200049/diff/1/nova/live_test.go
> File nova/live_test.go (right):
>
> https://codereview.appspot.com/7200049/diff/1/nova/live_test.go#newcode452
>
>
nova/live_test.go:452: return
> On 2013/01/28 06:44:57, jameinel wrote:
>> Is it possible to use your hooks to check for a retry request,
>> rather
> than
>> reading the output string? (I realize this probably didn't exist
> before, but it
>> seems like a really good use of it.)
>
> Possibly, but the hook stuff has been set up to work with the test
> doubles (manipulates the state of the test service back end) rather
> than a real live instance. The nova client implementation would
> need to be retooled to implement the ServiceControl interface.
> Perhaps this can be done in a followup branch.

I guess it is hooking the client-side stuff, vs hooking the service
side stuff.

>
> https://codereview.appspot.com/7200049/diff/1/testservices/novaservice/service_http.go
>
>
File testservices/novaservice/service_http.go (right):
>
> https://codereview.appspot.com/7200049/diff/1/testservices/novaservice/service_http.go#newcode216
>
>
testservices/novaservice/service_http.go:216: // Retry-After is usually
> an int but we don't want the tests taking too long to run. On
> 2013/01/28 06:44:57, jameinel wrote:
>> Maybe: RFC says that Retry-After should be an int, but we don't
>> want
> to wait an
>> entire second during the test suite.
>
> Done.
>
> https://codereview.appspot.com/7200049/
>

LGTM.

John
=:->

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (Cygwin)
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iEYEARECAAYFAlEHUQ0ACgkQJdeBCYSNAAMvGQCfRQtVLUas22F84lPzovlpzzZK
p0IAoItwzHDxMx7CSrVW7Q6X7sYFrNfA
=dice
-----END PGP SIGNATURE-----

Revision history for this message
John A Meinel (jameinel) wrote :
Revision history for this message
Ian Booth (wallyworld) wrote :

*** Submitted:

Rewrite rate limit retry tests

This branch uses the new service double control hooks introduced in the
previous branch to rewrite the tests which check that rate limit
exceeded retries are handled properly.
The kludge used previously to induce a retry error has been removed, and
now an additional test can also be easily added to check the behaviour
if too many rate limit retry responses are received.

So that the tests run fast, I've allowed for the Retry-After header
value to be a float (even though a real instance only assigns an int).
This means the tests can specify a really
short retry time (I used 1ms).

I've also re-added a rate limit retry test for use with the live
instance, but improved it so that it exists as soon as the first rate
limit exceeded response is received.

R=jameinel
CC=
https://codereview.appspot.com/7200049

https://codereview.appspot.com/7200049/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'http/client.go'
--- http/client.go 2012-12-21 04:10:14 +0000
+++ http/client.go 2013-01-29 01:46:23 +0000
@@ -25,9 +25,9 @@
2525
26type Client struct {26type Client struct {
27 http.Client27 http.Client
28 logger *log.Logger28 logger *log.Logger
29 authToken string29 authToken string
30 maxRetries int30 maxSendAttempts int
31}31}
3232
33type ErrorResponse struct {33type ErrorResponse struct {
@@ -55,11 +55,17 @@
55 RespReader io.ReadCloser55 RespReader io.ReadCloser
56}56}
5757
58const (
59 // The maximum number of times to try sending a request before we give up
60 // (assuming any unsuccessful attempts can be sensibly tried again).
61 MaxSendAttempts = 3
62)
63
58func New(httpClient http.Client, logger *log.Logger, token string) *Client {64func New(httpClient http.Client, logger *log.Logger, token string) *Client {
59 if logger == nil {65 if logger == nil {
60 logger = log.New(os.Stderr, "", log.LstdFlags)66 logger = log.New(os.Stderr, "", log.LstdFlags)
61 }67 }
62 return &Client{httpClient, logger, token, 3}68 return &Client{httpClient, logger, token, MaxSendAttempts}
63}69}
6470
65// JsonRequest JSON encodes and sends the supplied object (if any) to the specified URL.71// JsonRequest JSON encodes and sends the supplied object (if any) to the specified URL.
@@ -187,7 +193,7 @@
187}193}
188194
189func (c *Client) sendRateLimitedRequest(method, URL string, headers http.Header, reqData []byte) (resp *http.Response, err error) {195func (c *Client) sendRateLimitedRequest(method, URL string, headers http.Header, reqData []byte) (resp *http.Response, err error) {
190 for i := 0; i < c.maxRetries; i++ {196 for i := 0; i < c.maxSendAttempts; i++ {
191 var reqReader io.Reader197 var reqReader io.Reader
192 if reqData != nil {198 if reqData != nil {
193 reqReader = bytes.NewReader(reqData)199 reqReader = bytes.NewReader(reqData)
@@ -209,17 +215,17 @@
209 if resp.StatusCode != http.StatusRequestEntityTooLarge {215 if resp.StatusCode != http.StatusRequestEntityTooLarge {
210 return resp, nil216 return resp, nil
211 }217 }
212 retryAfter, err := strconv.Atoi(resp.Header.Get("Retry-After"))218 retryAfter, err := strconv.ParseFloat(resp.Header.Get("Retry-After"), 32)
213 if err != nil {219 if err != nil {
214 return nil, errors.Newf(err, URL, "Invalid Retry-After header")220 return nil, errors.Newf(err, URL, "Invalid Retry-After header")
215 }221 }
216 if retryAfter == 0 {222 if retryAfter == 0 {
217 return nil, errors.Newf(err, URL, "Resource limit exeeded at URL %s.", URL)223 return nil, errors.Newf(err, URL, "Resource limit exeeded at URL %s.", URL)
218 }224 }
219 c.logger.Printf("Too many requests, retrying in %d seconds.", retryAfter)225 c.logger.Printf("Too many requests, retrying in %dms.", int(retryAfter*1000))
220 time.Sleep(time.Duration(retryAfter) * time.Second)226 time.Sleep(time.Duration(retryAfter) * time.Second)
221 }227 }
222 return nil, errors.Newf(err, URL, "Maximum number of retries (%d) reached sending request to %s.", c.maxRetries, URL)228 return nil, errors.Newf(err, URL, "Maximum number of attempts (%d) reached sending request to %s.", c.maxSendAttempts, URL)
223}229}
224230
225type HttpError struct {231type HttpError struct {
226232
=== modified file 'identity/userpass.go'
--- identity/userpass.go 2013-01-27 13:37:53 +0000
+++ identity/userpass.go 2013-01-29 01:46:23 +0000
@@ -105,10 +105,16 @@
105 service.Endpoints = append(service.Endpoints[:i], service.Endpoints[i+1:]...)105 service.Endpoints = append(service.Endpoints[:i], service.Endpoints[i+1:]...)
106 }106 }
107 }107 }
108<<<<<<< TREE
108 if len(service.Endpoints) == 0 {109 if len(service.Endpoints) == 0 {
109 fmt.Fprintf(os.Stderr, "Found no endpoints for %v\n", service.Type)110 fmt.Fprintf(os.Stderr, "Found no endpoints for %v\n", service.Type)
110 }111 }
111 details.ServiceURLs[service.Type] = service.Endpoints[0].PublicURL112 details.ServiceURLs[service.Type] = service.Endpoints[0].PublicURL
113=======
114 if len(service.Endpoints) > 0 {
115 details.ServiceURLs[service.Type] = service.Endpoints[0].PublicURL
116 }
117>>>>>>> MERGE-SOURCE
112 }118 }
113119
114 return details, nil120 return details, nil
115121
=== modified file 'nova/live_test.go'
--- nova/live_test.go 2013-01-21 11:18:33 +0000
+++ nova/live_test.go 2013-01-29 01:46:23 +0000
@@ -1,11 +1,14 @@
1package nova_test1package nova_test
22
3import (3import (
4 "bytes"
4 . "launchpad.net/gocheck"5 . "launchpad.net/gocheck"
5 "launchpad.net/goose/client"6 "launchpad.net/goose/client"
6 "launchpad.net/goose/errors"7 "launchpad.net/goose/errors"
7 "launchpad.net/goose/identity"8 "launchpad.net/goose/identity"
8 "launchpad.net/goose/nova"9 "launchpad.net/goose/nova"
10 "log"
11 "strings"
9 "time"12 "time"
10)13)
1114
@@ -419,3 +422,36 @@
419 c.Check(fip.FixedIP, IsNil)422 c.Check(fip.FixedIP, IsNil)
420 c.Check(fip.InstanceId, IsNil)423 c.Check(fip.InstanceId, IsNil)
421}424}
425
426// TestRateLimitRetry checks that when we make too many requests and receive a Retry-After response, the retry
427// occurs and the request ultimately succeeds.
428func (s *LiveTests) TestRateLimitRetry(c *C) {
429 // Capture the logged output so we can check for retry messages.
430 var logout bytes.Buffer
431 logger := log.New(&logout, "", log.LstdFlags)
432 client := client.NewClient(s.cred, identity.AuthUserPass, logger)
433 nova := nova.New(client)
434 // Delete the artifact if it already exists.
435 testGroup, err := nova.SecurityGroupByName("test_group")
436 if err != nil {
437 c.Assert(errors.IsNotFound(err), Equals, true)
438 } else {
439 nova.DeleteSecurityGroup(testGroup.Id)
440 c.Assert(err, IsNil)
441 }
442 // Create some artifacts a number of times in succession and ensure each time is successful,
443 // even with retries being required. As soon as we see a retry message, the test has passed
444 // and we exit.
445 for i := 0; i < 50; i++ {
446 testGroup, err = nova.CreateSecurityGroup("test_group", "test")
447 c.Assert(err, IsNil)
448 nova.DeleteSecurityGroup(testGroup.Id)
449 c.Assert(err, IsNil)
450 output := logout.String()
451 if strings.Contains(output, "Too many requests, retrying in") == true {
452 return
453 }
454 }
455 // No retry message logged so test has failed.
456 c.Fail()
457}
422458
=== modified file 'nova/local_test.go'
--- nova/local_test.go 2013-01-29 01:46:22 +0000
+++ nova/local_test.go 2013-01-29 01:46:23 +0000
@@ -2,11 +2,14 @@
22
3import (3import (
4 "bytes"4 "bytes"
5 "fmt"
5 . "launchpad.net/gocheck"6 . "launchpad.net/gocheck"
6 "launchpad.net/goose/client"7 "launchpad.net/goose/client"
7 "launchpad.net/goose/errors"8 "launchpad.net/goose/errors"
9 goosehttp "launchpad.net/goose/http"
8 "launchpad.net/goose/identity"10 "launchpad.net/goose/identity"
9 "launchpad.net/goose/nova"11 "launchpad.net/goose/nova"
12 "launchpad.net/goose/testservices"
10 "launchpad.net/goose/testservices/openstackservice"13 "launchpad.net/goose/testservices/openstackservice"
11 "log"14 "log"
12 "net/http"15 "net/http"
@@ -23,9 +26,12 @@
23type localLiveSuite struct {26type localLiveSuite struct {
24 LiveTests27 LiveTests
25 // The following attributes are for using testing doubles.28 // The following attributes are for using testing doubles.
26 Server *httptest.Server29 Server *httptest.Server
27 Mux *http.ServeMux30 Mux *http.ServeMux
28 oldHandler http.Handler31 oldHandler http.Handler
32 openstack *openstackservice.Openstack
33 retryErrorCount int // The current retry error count.
34 retryErrorCountToSend int // The number of retry errors to send.
29}35}
3036
31func (s *localLiveSuite) SetUpSuite(c *C) {37func (s *localLiveSuite) SetUpSuite(c *C) {
@@ -45,8 +51,8 @@
45 Region: "some region",51 Region: "some region",
46 TenantName: "tenant",52 TenantName: "tenant",
47 }53 }
48 openstack := openstackservice.New(s.cred)54 s.openstack = openstackservice.New(s.cred)
49 openstack.SetupHTTP(s.Mux)55 s.openstack.SetupHTTP(s.Mux)
5056
51 s.LiveTests.SetUpSuite(c)57 s.LiveTests.SetUpSuite(c)
52}58}
@@ -59,6 +65,7 @@
59}65}
6066
61func (s *localLiveSuite) SetUpTest(c *C) {67func (s *localLiveSuite) SetUpTest(c *C) {
68 s.retryErrorCount = 0
62 s.LiveTests.SetUpTest(c)69 s.LiveTests.SetUpTest(c)
63}70}
6471
@@ -68,12 +75,18 @@
6875
69// Additional tests to be run against the service double only go here.76// Additional tests to be run against the service double only go here.
7077
71// TestRateLimitRetry checks that when we make too many requests and receive a Retry-After response, the retry78func (s *localLiveSuite) retryLimitHook(sc testservices.ServiceControl) testservices.ControlProcessor {
72// occurs and the request ultimately succeeds.79 return func(sc testservices.ServiceControl, args ...interface{}) error {
73func (s *localLiveSuite) TestRateLimitRetry(c *C) {80 sendError := s.retryErrorCount < s.retryErrorCountToSend
74 // Capture the logged output so we can check for retry messages.81 if sendError {
75 var logout bytes.Buffer82 s.retryErrorCount++
76 logger := log.New(&logout, "", log.LstdFlags)83 return &testservices.RateLimitExceededError{fmt.Errorf("retry limit exceeded")}
84 }
85 return nil
86 }
87}
88
89func (s *localLiveSuite) setupRetryErrorTest(c *C, logger *log.Logger) (*nova.Client, *nova.SecurityGroup) {
77 client := client.NewClient(s.cred, identity.AuthUserPass, logger)90 client := client.NewClient(s.cred, identity.AuthUserPass, logger)
78 nova := nova.New(client)91 nova := nova.New(client)
79 // Delete the artifact if it already exists.92 // Delete the artifact if it already exists.
@@ -84,11 +97,35 @@
84 nova.DeleteSecurityGroup(testGroup.Id)97 nova.DeleteSecurityGroup(testGroup.Id)
85 c.Assert(err, IsNil)98 c.Assert(err, IsNil)
86 }99 }
87 testGroup, err = nova.CreateSecurityGroup("test_group", "test rate limit")100 testGroup, err = nova.CreateSecurityGroup("test_group", "test")
88 c.Assert(err, IsNil)101 c.Assert(err, IsNil)
89 nova.DeleteSecurityGroup(testGroup.Id)102 return nova, testGroup
103}
104
105// TestRateLimitRetry checks that when we make too many requests and receive a Retry-After response, the retry
106// occurs and the request ultimately succeeds.
107func (s *localLiveSuite) TestRateLimitRetry(c *C) {
108 // Capture the logged output so we can check for retry messages.
109 var logout bytes.Buffer
110 logger := log.New(&logout, "", log.LstdFlags)
111 nova, testGroup := s.setupRetryErrorTest(c, logger)
112 s.retryErrorCountToSend = goosehttp.MaxSendAttempts - 1
113 s.openstack.Nova.RegisterControlPoint("removeSecurityGroup", s.retryLimitHook(s.openstack.Nova))
114 defer s.openstack.Nova.RegisterControlPoint("removeSecurityGroup", nil)
115 err := nova.DeleteSecurityGroup(testGroup.Id)
90 c.Assert(err, IsNil)116 c.Assert(err, IsNil)
91 // Ensure we got at least one retry message.117 // Ensure we got at least one retry message.
92 output := logout.String()118 output := logout.String()
93 c.Assert(strings.Contains(output, "Too many requests, retrying in"), Equals, true)119 c.Assert(strings.Contains(output, "Too many requests, retrying in"), Equals, true)
94}120}
121
122// TestRateLimitRetryExceeded checks that an error is raised if too many retry responses are received from the server.
123func (s *localLiveSuite) TestRateLimitRetryExceeded(c *C) {
124 nova, testGroup := s.setupRetryErrorTest(c, nil)
125 s.retryErrorCountToSend = goosehttp.MaxSendAttempts
126 s.openstack.Nova.RegisterControlPoint("removeSecurityGroup", s.retryLimitHook(s.openstack.Nova))
127 defer s.openstack.Nova.RegisterControlPoint("removeSecurityGroup", nil)
128 err := nova.DeleteSecurityGroup(testGroup.Id)
129 c.Assert(err, Not(IsNil))
130 c.Assert(err.Error(), Matches, ".*Maximum number of attempts.*")
131}
95132
=== modified file 'testservices/novaservice/service.go'
--- testservices/novaservice/service.go 2013-01-29 01:46:22 +0000
+++ testservices/novaservice/service.go 2013-01-29 01:46:23 +0000
@@ -18,17 +18,16 @@
18// contains the service double's internal state.18// contains the service double's internal state.
19type Nova struct {19type Nova struct {
20 testservices.ServiceInstance20 testservices.ServiceInstance
21 flavors map[string]nova.FlavorDetail21 flavors map[string]nova.FlavorDetail
22 servers map[string]nova.ServerDetail22 servers map[string]nova.ServerDetail
23 groups map[int]nova.SecurityGroup23 groups map[int]nova.SecurityGroup
24 rules map[int]nova.SecurityGroupRule24 rules map[int]nova.SecurityGroupRule
25 floatingIPs map[int]nova.FloatingIP25 floatingIPs map[int]nova.FloatingIP
26 serverGroups map[string][]int26 serverGroups map[string][]int
27 serverIPs map[string][]int27 serverIPs map[string][]int
28 nextGroupId int28 nextGroupId int
29 nextRuleId int29 nextRuleId int
30 nextIPId int30 nextIPId int
31 sendFakeRateLimitResponse bool
32}31}
3332
34// endpoint returns either a versioned or non-versioned service33// endpoint returns either a versioned or non-versioned service
@@ -82,9 +81,6 @@
82 floatingIPs: make(map[int]nova.FloatingIP),81 floatingIPs: make(map[int]nova.FloatingIP),
83 serverGroups: make(map[string][]int),82 serverGroups: make(map[string][]int),
84 serverIPs: make(map[string][]int),83 serverIPs: make(map[string][]int),
85 // The following attribute controls whether rate limit responses are sent back to the caller.
86 // This is switched off when we want to ensure the client eventually gets a proper response.
87 sendFakeRateLimitResponse: true,
88 ServiceInstance: testservices.ServiceInstance{84 ServiceInstance: testservices.ServiceInstance{
89 IdentityService: identityService,85 IdentityService: identityService,
90 Hostname: hostname,86 Hostname: hostname,
9187
=== modified file 'testservices/novaservice/service_http.go'
--- testservices/novaservice/service_http.go 2013-01-23 23:37:38 +0000
+++ testservices/novaservice/service_http.go 2013-01-29 01:46:23 +0000
@@ -9,6 +9,7 @@
9 "io"9 "io"
10 "io/ioutil"10 "io/ioutil"
11 "launchpad.net/goose/nova"11 "launchpad.net/goose/nova"
12 "launchpad.net/goose/testservices"
12 "launchpad.net/goose/testservices/identityservice"13 "launchpad.net/goose/testservices/identityservice"
13 "net/http"14 "net/http"
14 "path"15 "path"
@@ -212,7 +213,8 @@
212 "",213 "",
213 "text/plain; charset=UTF-8",214 "text/plain; charset=UTF-8",
214 "too many requests",215 "too many requests",
215 map[string]string{"Retry-After": "1"},216 // RFC says that Retry-After should be an int, but we don't want to wait an entire second during the test suite.
217 map[string]string{"Retry-After": "0.001"},
216 nil,218 nil,
217 }219 }
218)220)
@@ -286,15 +288,20 @@
286 if err == nil {288 if err == nil {
287 return289 return
288 }290 }
289 resp, _ := err.(http.Handler)291 var resp http.Handler
290 if resp == nil {292 if _, ok := err.(*testservices.RateLimitExceededError); ok {
291 resp = &errorResponse{293 resp = errRateLimitExceeded
292 http.StatusInternalServerError,294 } else {
293 `{"internalServerError":{"message":"$ERROR$",code:500}}`,295 resp, _ = err.(http.Handler)
294 "application/json",296 if resp == nil {
295 err.Error(),297 resp = &errorResponse{
296 nil,298 http.StatusInternalServerError,
297 h.n,299 `{"internalServerError":{"message":"$ERROR$",code:500}}`,
300 "application/json",
301 err.Error(),
302 nil,
303 h.n,
304 }
298 }305 }
299 }306 }
300 resp.ServeHTTP(w, r)307 resp.ServeHTTP(w, r)
@@ -782,12 +789,6 @@
782 if err == nil {789 if err == nil {
783 return errBadRequestDuplicateValue790 return errBadRequestDuplicateValue
784 }791 }
785 if req.Group.Description == "test rate limit" && n.sendFakeRateLimitResponse {
786 n.sendFakeRateLimitResponse = false
787 return errRateLimitExceeded
788 } else {
789 n.sendFakeRateLimitResponse = true
790 }
791 n.nextGroupId++792 n.nextGroupId++
792 nextId := n.nextGroupId793 nextId := n.nextGroupId
793 err = n.addSecurityGroup(nova.SecurityGroup{794 err = n.addSecurityGroup(nova.SecurityGroup{
794795
=== modified file 'testservices/service.go'
--- testservices/service.go 2013-01-29 01:46:22 +0000
+++ testservices/service.go 2013-01-29 01:46:23 +0000
@@ -25,6 +25,11 @@
25 ControlHooks map[string]ControlProcessor25 ControlHooks map[string]ControlProcessor
26}26}
2727
28// Internal Openstack errors.
29type RateLimitExceededError struct {
30 error
31}
32
28// ControlProcessor defines a function that is run when a specified control point is reached in the service33// ControlProcessor defines a function that is run when a specified control point is reached in the service
29// business logic. The function receives the service instance so internal state can be inspected, plus for any34// business logic. The function receives the service instance so internal state can be inspected, plus for any
30// arguments passed to the currently executing service function.35// arguments passed to the currently executing service function.

Subscribers

People subscribed via source and target branches