Merge lp:~dimitern/goose/swift-testing-service into lp:~gophers/goose/trunk

Proposed by Dimiter Naydenov on 2012-11-27
Status: Merged
Merged at revision: 25
Proposed branch: lp:~dimitern/goose/swift-testing-service
Merge into: lp:~gophers/goose/trunk
Diff against target: 596 lines (+566/-0)
6 files modified
testservices/swiftservice/service.go (+98/-0)
testservices/swiftservice/service_http.go (+151/-0)
testservices/swiftservice/service_http_test.go (+205/-0)
testservices/swiftservice/service_test.go (+86/-0)
testservices/swiftservice/setup_test.go (+10/-0)
testservices/swiftservice/swiftservice.go (+16/-0)
To merge this branch: bzr merge lp:~dimitern/goose/swift-testing-service
Reviewer Review Type Date Requested Status
The Go Language Gophers 2012-11-27 Pending
Review via email: mp+136362@code.launchpad.net

Description of the Change

Swift test service double implemented.

Implements a minimal number of OS Swift API, needed for juju testing.

https://codereview.appspot.com/6851112/

To post a comment you must log in.
Dimiter Naydenov (dimitern) wrote :

Reviewers: mp+136362_code.launchpad.net,

Message:
Please take a look.

Description:
Swift test service double implemented.

Implements a minimal number of OS Swift API, needed for juju testing.

https://code.launchpad.net/~dimitern/goose/swift-testing-service/+merge/136362

(do not edit description out of merge proposal)

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

Affected files:
   A [revision details]
   A testservices/swiftservice/service.go
   A testservices/swiftservice/service_test.go
   A testservices/swiftservice/setup_test.go
   A testservices/swiftservice/swiftservice.go

John A Meinel (jameinel) wrote :

I think this is reasonable to land as a first draft of the SWIFT
services, and we can just iterate from here.

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go
File testservices/swiftservice/service.go (right):

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go#newcode44
testservices/swiftservice/service.go:44: return fmt.Errorf("container
already exists %q", name)
If at the HTTP request level it is not an error to AddContainer
something that already exists, I probably wouldn't make it an error at
this level.
Instead, we could just return "(created bool)".

Note that we'll want doc comments for all of these APIs eventually.

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go#newcode96
testservices/swiftservice/service.go:96: path :=
strings.Replace(r.URL.Path, s.baseURL, "", -1)
I don't think we want Replace(-1) here. As if the base URL was something
like "foo/" that would replace all occurances. So "foo/foo/foo/" would
end up as just "".
Probably what we really want is just to look if the prefix matches and
then remove it. More like:
if r.URL.Path[:len(s.baseURL)] == s.baseURL { path =
r.URL.Path[len(s.baseURL):]}
And probably an error if it doesn't match?

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go#newcode98
testservices/swiftservice/service.go:98: path = strings.TrimRight(path,
"/")
We can probably just unconditionally TrimRight here.

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go#newcode106
testservices/swiftservice/service.go:106: }
I think that eventually we'll want to have knowledge of multiple users
and tokens, etc. But this is reasonable as a first step.
We'll probably want some generic code for checking Auth-Tokens (residing
in identityservice?)

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go#newcode111
testservices/swiftservice/service.go:111: return
If err != nil then we say Accepted? Shouldn't that be "if err == nil" ?

Maybe I'm missing the api of AddContainer (it is acceptable to try to
PUT a container that already exists.)
We'll want to make sure to get test coverage of that.

This is the sort of thing where you sort of want TDD, because it is easy
to forget that you handled this case, and then the test case doesn't get
written.

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go#newcode141
testservices/swiftservice/service.go:141: }
Ultimately, we probably want something like 'gorest' to do the work of
finding what methods need to be connected. But honestly, this doesn't
look too bad.
I would probably split it up more as:
if len(parts) == 1 { DoContainerAPI(params) }
elif len(parts) == 2 {DoObjectAPI(params) }
else { w.Write(ERROR) }
And then the container stuff of GET/DELETE/PUT are all nicely clustered,
and the Object stuff is also nicely clustered, and you don't repeat the
'len(parts)' checks everywhere.

https://codereview.appspot.com/6851112/

Martin Packman (gz) wrote :

Seems like a good start, a few cleanup suggestions for the service.

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go
File testservices/swiftservice/service.go (right):

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go#newcode100
testservices/swiftservice/service.go:100: parts := strings.Split(path,
"/")
I would have container and object name explicitly here and check which
is nil, rather than leaving as parts.

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go#newcode142
testservices/swiftservice/service.go:142:
w.WriteHeader(http.StatusNotFound)
This throws away the err string and returns no body?

https://codereview.appspot.com/6851112/

24. By Dimiter Naydenov on 2012-11-27

Changes after review + some reorganizing

Dimiter Naydenov (dimitern) wrote :
Download full text (7.4 KiB)

Please take a look.

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go
File testservices/swiftservice/service.go (right):

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go#newcode44
testservices/swiftservice/service.go:44: return fmt.Errorf("container
already exists %q", name)
On 2012/11/27 11:03:07, john.meinel wrote:
> If at the HTTP request level it is not an error to AddContainer
something that
> already exists, I probably wouldn't make it an error at this level.
> Instead, we could just return "(created bool)".

> Note that we'll want doc comments for all of these APIs eventually.

Sure I'll add doc comments. Not sure about the error though - it seems
to me nice and consistent this way. Care to elaborate why not?

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go#newcode51
testservices/swiftservice/service.go:51: _, err :=
s.GetObject(container, name)
On 2012/11/27 11:13:33, dfc wrote:
> This can be expressed more compactly

> if _, err := ... ; err != nil {

> }
Sure, I was not sure, but I'll do it, 10x

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go#newcode58
testservices/swiftservice/service.go:58: if ok :=
s.HasContainer(container); !ok {
On 2012/11/27 11:13:33, dfc wrote:
> same

> if err := s.AddContainer... ; err != nil {
> return err
> }

Done.

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go#newcode78
testservices/swiftservice/service.go:78: _, err :=
s.GetObject(container, name)
On 2012/11/27 11:13:33, dfc wrote:
> same as above

Done.

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go#newcode82
testservices/swiftservice/service.go:82: s.containers[container][name] =
nil
On 2012/11/27 11:13:33, dfc wrote:
> not sure if you need to nil out the reference here, the delete below
will orphan
> it.
Again, was not sure delete is enough, I'll change it, 10x.

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go#newcode88
testservices/swiftservice/service.go:88: _, err :=
s.GetObject(container, object)
On 2012/11/27 11:13:33, dfc wrote:
> same as above

Done.

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go#newcode92
testservices/swiftservice/service.go:92: return fmt.Sprintf("%s%s%s/%s",
s.hostname, s.baseURL, container, object), nil
On 2012/11/27 11:13:33, dfc wrote:
> Pedantic people would suggest using path.Join() here, but I think this
is
> reasonable.
Some strings have slashes, some don't so that's why I concatenate them
like this.

https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go#newcode96
testservices/swiftservice/service.go:96: path :=
strings.Replace(r.URL.Path, s.baseURL, "", -1)
On 2012/11/27 11:03:07, john.meinel wrote:
> I don't think we want Replace(-1) here. As if the base URL was
something like
> "foo/" that would replace all occurances. So "foo/foo/foo/" would end
up as just
> "".
> Probably what we really want is just to look if the prefix matches and
then
> remove it. More like:
> if r.URL.Path[:len(s.baseURL)] == ...

Read more...

25. By Dimiter Naydenov on 2012-11-27

Omission from previous commit

Dimiter Naydenov (dimitern) wrote :
John A Meinel (jameinel) wrote :

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

On 2012-11-27 23:01, Dimiter Naydenov wrote:
> Please take a look.
>
>
> https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go
>
>
File testservices/swiftservice/service.go (right):
>
> https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go#newcode44
>
>
testservices/swiftservice/service.go:44: return fmt.Errorf("container
> already exists %q", name) On 2012/11/27 11:03:07, john.meinel
> wrote:
>> If at the HTTP request level it is not an error to AddContainer
> something that
>> already exists, I probably wouldn't make it an error at this
>> level. Instead, we could just return "(created bool)".
>
>> Note that we'll want doc comments for all of these APIs
>> eventually.
>
> Sure I'll add doc comments. Not sure about the error though - it
> seems to me nice and consistent this way. Care to elaborate why
> not?
>

Because at the Openstack HTTP level, it is considered to not be an
error, but a no-op (accepted) case. It seems strange to have no-op be
an error, that we then ignore.
...

> https://codereview.appspot.com/6851112/diff/1/testservices/swiftservice/service.go#newcode92
>
>
testservices/swiftservice/service.go:92: return fmt.Sprintf("%s%s%s/%s",
> s.hostname, s.baseURL, container, object), nil On 2012/11/27
> 11:13:33, dfc wrote:
>> Pedantic people would suggest using path.Join() here, but I think
>> this
> is
>> reasonable.
> Some strings have slashes, some don't so that's why I concatenate
> them like this.

path.Join() would handle if a section has slashes or not (I think). At
least, my guess is that Dave was suggesting using Join() to normalize
the slashes.

John
=:->

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

iEYEARECAAYFAlC1z8wACgkQJdeBCYSNAANzrgCfZyzxsTC5Cw4SNehVMzLOReKy
TCQAnj46rVXl6SXpZNSjqCb0kTngaIBv
=T6j+
-----END PGP SIGNATURE-----

John A Meinel (jameinel) wrote :

Overall LGTM.
I'd like to see the tests split up a little bit more, but that's
probably the only thing.

https://codereview.appspot.com/6851112/diff/8002/testservices/swiftservice/service_http.go
File testservices/swiftservice/service_http.go (right):

https://codereview.appspot.com/6851112/diff/8002/testservices/swiftservice/service_http.go#newcode10
testservices/swiftservice/service_http.go:10:
Did the response really have this crazy spacing? That is surprising.

https://codereview.appspot.com/6851112/diff/8002/testservices/swiftservice/service_http.go#newcode131
testservices/swiftservice/service_http.go:131: if token != s.token {
Did we test that if you pass an invalid token you get the same response
as if you don't pass any token at all? We might, but something we'll
want to test.

https://codereview.appspot.com/6851112/diff/8002/testservices/swiftservice/service_http_test.go
File testservices/swiftservice/service_http_test.go (right):

https://codereview.appspot.com/6851112/diff/8002/testservices/swiftservice/service_http_test.go#newcode54
testservices/swiftservice/service_http_test.go:54:
Ultimately, I'd like to see this split out into stuff like:
TestPUTExistsAccepted
TestPUTMissingCreated
TestRemoveExistsOK
TestRemoveMissingError
etc.

It is ok to have it this way for now.

I realize there will be a small amount of extra setup and teardown in
the tests. However, changing one test will not affect the others, which
is a huge win.

https://codereview.appspot.com/6851112/diff/8002/testservices/swiftservice/service_http_test.go#newcode101
testservices/swiftservice/service_http_test.go:101:
Similarly here.

https://codereview.appspot.com/6851112/diff/8002/testservices/swiftservice/swiftservice.go
File testservices/swiftservice/swiftservice.go (right):

https://codereview.appspot.com/6851112/diff/8002/testservices/swiftservice/swiftservice.go#newcode6
testservices/swiftservice/swiftservice.go:6:
As mentioned by others, we'll want doc comments on the type and on the
individual functions.

https://codereview.appspot.com/6851112/

Dimiter Naydenov (dimitern) wrote :

Submitting, with the changes suggested.

https://codereview.appspot.com/6851112/diff/8002/testservices/swiftservice/service_http.go
File testservices/swiftservice/service_http.go (right):

https://codereview.appspot.com/6851112/diff/8002/testservices/swiftservice/service_http.go#newcode10
testservices/swiftservice/service_http.go:10:
On 2012/11/28 10:19:33, jameinel wrote:
> Did the response really have this crazy spacing? That is surprising.

Yes, it's exactly how I sniffed it from the real canonistack session.

https://codereview.appspot.com/6851112/diff/8002/testservices/swiftservice/service_http.go#newcode131
testservices/swiftservice/service_http.go:131: if token != s.token {
On 2012/11/28 10:19:33, jameinel wrote:
> Did we test that if you pass an invalid token you get the same
response as if
> you don't pass any token at all? We might, but something we'll want to
test.

Sure, I'm adding that test.

https://codereview.appspot.com/6851112/diff/8002/testservices/swiftservice/service_http_test.go
File testservices/swiftservice/service_http_test.go (right):

https://codereview.appspot.com/6851112/diff/8002/testservices/swiftservice/service_http_test.go#newcode54
testservices/swiftservice/service_http_test.go:54:
On 2012/11/28 10:19:33, jameinel wrote:
> Ultimately, I'd like to see this split out into stuff like:
> TestPUTExistsAccepted
> TestPUTMissingCreated
> TestRemoveExistsOK
> TestRemoveMissingError
> etc.

> It is ok to have it this way for now.

> I realize there will be a small amount of extra setup and teardown in
the tests.
> However, changing one test will not affect the others, which is a huge
win.

I was thinking of the same thing - it got kinda too much for just 2 test
cases.

https://codereview.appspot.com/6851112/diff/8002/testservices/swiftservice/service_http_test.go#newcode101
testservices/swiftservice/service_http_test.go:101:
On 2012/11/28 10:19:33, jameinel wrote:
> Similarly here.

Done.

https://codereview.appspot.com/6851112/diff/8002/testservices/swiftservice/swiftservice.go
File testservices/swiftservice/swiftservice.go (right):

https://codereview.appspot.com/6851112/diff/8002/testservices/swiftservice/swiftservice.go#newcode6
testservices/swiftservice/swiftservice.go:6:
On 2012/11/28 10:19:33, jameinel wrote:
> As mentioned by others, we'll want doc comments on the type and on the
> individual functions.

Done.

https://codereview.appspot.com/6851112/

Dimiter Naydenov (dimitern) wrote :

*** Submitted:

Swift test service double implemented.

Implements a minimal number of OS Swift API, needed for juju testing.

R=
CC=
https://codereview.appspot.com/6851112

https://codereview.appspot.com/6851112/

John A Meinel (jameinel) wrote :

LGTM

I have some comments, but don't think it needs another review before
landing.

https://codereview.appspot.com/6851112/diff/11001/testservices/swiftservice/service_http_test.go
File testservices/swiftservice/service_http_test.go (right):

https://codereview.appspot.com/6851112/diff/11001/testservices/swiftservice/service_http_test.go#newcode38
testservices/swiftservice/service_http_test.go:38: func (s
*SwiftHTTPSuite) sendRequest(method, path string, body []byte)
(*http.Response, error) {
All callers of this follow up with:
c.Assert(err, IsNil)
c.Assert(resp.ResponseCode, Equals, X)

So I would propose to move that code into this function, and simplify
all of the callers.

https://codereview.appspot.com/6851112/diff/11001/testservices/swiftservice/service_http_test.go#newcode50
testservices/swiftservice/service_http_test.go:50: if token != "" {
Where is 'token' being defined? I just realized I don't see its
definition, and it is both here and in the SetUpSuite.
If it is a global variable (which I'm not super excited about, but might
be necessary), then it *definitely* should not just be called "token".
The fact that we sometimes compare it to "" means that not only is it
global state, it is *mutable* global state, which is almost always
incorrect.

https://codereview.appspot.com/6851112/diff/11001/testservices/swiftservice/service_http_test.go#newcode106
testservices/swiftservice/service_http_test.go:106: }
The 'removeContainer' here looks like cleanup. So if have a
'ensureNoContainer' that removes it if it exists, but doesn't fail
(removeContainer might already be that), then we can do:

s.ensureNotContainer("test", c)
defer s.removeContainer("test", c)
s.sendRequest(c, "PUT", "test", nil, http.StatusCreated)

And that is the complete test. Though we might take it a step further,
and TearDownTest should be the thing that makes sure you don't leave any
state in the service.

Overall splitting these up looks good to me.

https://codereview.appspot.com/6851112/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'testservices/swiftservice'
2=== added file 'testservices/swiftservice/service.go'
3--- testservices/swiftservice/service.go 1970-01-01 00:00:00 +0000
4+++ testservices/swiftservice/service.go 2012-11-27 19:03:21 +0000
5@@ -0,0 +1,98 @@
6+// Swift double testing service - internal direct API implementation
7+
8+package swiftservice
9+
10+import "fmt"
11+
12+type object map[string][]byte
13+
14+type Swift struct {
15+ containers map[string]object
16+ hostname string
17+ baseURL string
18+ token string
19+}
20+
21+// New creates an instance of the Swift object, given the parameters.
22+func New(hostname, baseURL, token string) *Swift {
23+ swift := &Swift{
24+ containers: make(map[string]object),
25+ hostname: hostname,
26+ baseURL: baseURL,
27+ token: token,
28+ }
29+ return swift
30+}
31+
32+// HasContainer verifies the given container exists or not.
33+func (s *Swift) HasContainer(name string) bool {
34+ _, ok := s.containers[name]
35+ return ok
36+}
37+
38+// GetObject retrieves a given object from its container, returning
39+// the object data or an error.
40+func (s *Swift) GetObject(container, name string) ([]byte, error) {
41+ data, ok := s.containers[container][name]
42+ if !ok {
43+ return nil, fmt.Errorf("no such object %q in container %q", name, container)
44+ }
45+ return data, nil
46+}
47+
48+// AddContainer creates a new container with the given name, if it
49+// does not exist. Otherwise an error is returned.
50+func (s *Swift) AddContainer(name string) error {
51+ if s.HasContainer(name) {
52+ return fmt.Errorf("container already exists %q", name)
53+ }
54+ s.containers[name] = make(object)
55+ return nil
56+}
57+
58+// AddObject creates a new object with the given name in the specified
59+// container, setting the object's data. It's an error if the object
60+// already exists. If the container does not exist, it will be
61+// created.
62+func (s *Swift) AddObject(container, name string, data []byte) error {
63+ if _, err := s.GetObject(container, name); err == nil {
64+ return fmt.Errorf(
65+ "object %q in container %q already exists",
66+ name,
67+ container)
68+ }
69+ if ok := s.HasContainer(container); !ok {
70+ if err := s.AddContainer(container); err != nil {
71+ return err
72+ }
73+ }
74+ s.containers[container][name] = data
75+ return nil
76+}
77+
78+// RemoveContainer deletes an existing container with the given name.
79+func (s *Swift) RemoveContainer(name string) error {
80+ if ok := s.HasContainer(name); !ok {
81+ return fmt.Errorf("no such container %q", name)
82+ }
83+ delete(s.containers, name)
84+ return nil
85+}
86+
87+// RemoveObject deletes an existing object in a given container.
88+func (s *Swift) RemoveObject(container, name string) error {
89+ if _, err := s.GetObject(container, name); err != nil {
90+ return err
91+ }
92+ delete(s.containers[container], name)
93+ return nil
94+}
95+
96+// GetURL returns the full URL, which can be used to GET the
97+// object. An error occurs if the object does not exist.
98+func (s *Swift) GetURL(container, object string) (string, error) {
99+ if _, err := s.GetObject(container, object); err != nil {
100+ return "", err
101+ }
102+ return fmt.Sprintf("%s%s%s/%s", s.hostname, s.baseURL, container, object), nil
103+}
104
105=== added file 'testservices/swiftservice/service_http.go'
106--- testservices/swiftservice/service_http.go 1970-01-01 00:00:00 +0000
107+++ testservices/swiftservice/service_http.go 2012-11-27 19:03:21 +0000
108@@ -0,0 +1,151 @@
109+// Swift double testing service - HTTP API implementation
110+
111+package swiftservice
112+
113+import (
114+ "io/ioutil"
115+ "net/http"
116+ "strings"
117+)
118+
119+// verbatim real Swift responses
120+const (
121+ notFoundResponse = `404 Not Found
122+
123+The resource could not be found.
124+
125+
126+`
127+ createdResponse = `201 Created
128+
129+
130+
131+
132+`
133+ acceptedResponse = `202 Accepted
134+
135+The request is accepted for processing.
136+
137+
138+`
139+)
140+
141+// handleContainers processes HTTP requests for container management.
142+func (s *Swift) handleContainers(container string, w http.ResponseWriter, r *http.Request) {
143+ var err error
144+ w.Header().Set("Content-Type", "text/html; charset=UTF-8")
145+ exists := s.HasContainer(container)
146+ if !exists && r.Method != "PUT" {
147+ w.WriteHeader(http.StatusNotFound)
148+ w.Write([]byte(notFoundResponse))
149+ return
150+ }
151+ switch r.Method {
152+ case "GET":
153+ w.WriteHeader(http.StatusOK)
154+ w.Write([]byte("[]"))
155+ case "DELETE":
156+ if err = s.RemoveContainer(container); err != nil {
157+ w.WriteHeader(http.StatusInternalServerError)
158+ w.Write([]byte(err.Error()))
159+ } else {
160+ w.Header().Set("Content-Length", "0")
161+ w.WriteHeader(http.StatusNoContent)
162+ }
163+ case "PUT":
164+ if exists {
165+ w.WriteHeader(http.StatusAccepted)
166+ w.Write([]byte(acceptedResponse))
167+ } else {
168+ if err = s.AddContainer(container); err != nil {
169+ w.WriteHeader(http.StatusInternalServerError)
170+ w.Write([]byte(err.Error()))
171+ } else {
172+ w.WriteHeader(http.StatusCreated)
173+ w.Write([]byte(createdResponse))
174+ }
175+ }
176+ default:
177+ panic("not implemented request type: " + r.Method)
178+ }
179+}
180+
181+// handleObjects processes HTTP requests for object management.
182+func (s *Swift) handleObjects(container, object string, w http.ResponseWriter, r *http.Request) {
183+ var err error
184+ w.Header().Set("Content-Type", "text/html; charset=UTF-8")
185+ if exists := s.HasContainer(container); !exists {
186+ w.WriteHeader(http.StatusNotFound)
187+ w.Write([]byte(notFoundResponse))
188+ return
189+ }
190+ objdata, err := s.GetObject(container, object)
191+ if err != nil && r.Method != "PUT" {
192+ w.WriteHeader(http.StatusNotFound)
193+ w.Write([]byte(notFoundResponse))
194+ return
195+ }
196+ exists := err == nil
197+ switch r.Method {
198+ case "GET":
199+ w.WriteHeader(http.StatusOK)
200+ w.Header().Set("Content-Type", "application/json; charset=UF-8")
201+ w.Write([]byte(objdata))
202+ case "DELETE":
203+ if err = s.RemoveObject(container, object); err != nil {
204+ w.WriteHeader(http.StatusInternalServerError)
205+ w.Write([]byte(err.Error()))
206+ } else {
207+ w.Header().Set("Content-Length", "0")
208+ w.WriteHeader(http.StatusNoContent)
209+ }
210+ case "PUT":
211+ bodydata, err := ioutil.ReadAll(r.Body)
212+ if err != nil {
213+ w.WriteHeader(http.StatusInternalServerError)
214+ w.Write([]byte(err.Error()))
215+ return
216+ }
217+ if exists {
218+ err = s.RemoveObject(container, object)
219+ if err != nil {
220+ w.WriteHeader(http.StatusInternalServerError)
221+ w.Write([]byte(err.Error()))
222+ }
223+ }
224+ if err = s.AddObject(container, object, bodydata); err != nil {
225+ w.WriteHeader(http.StatusInternalServerError)
226+ w.Write([]byte(err.Error()))
227+ } else {
228+ w.WriteHeader(http.StatusCreated)
229+ w.Write([]byte(createdResponse))
230+ }
231+ default:
232+ panic("not implemented request type: " + r.Method)
233+ }
234+}
235+
236+// ServeHTTP is the main entry point in the HTTP request processing.
237+func (s *Swift) ServeHTTP(w http.ResponseWriter, r *http.Request) {
238+ token := r.Header.Get("X-Auth-Token")
239+ if token != s.token {
240+ w.WriteHeader(http.StatusUnauthorized)
241+ return
242+ }
243+ path := r.URL.Path
244+ if path[:len(s.baseURL)] == s.baseURL {
245+ path = path[len(s.baseURL):]
246+ }
247+ path = strings.TrimRight(path, "/")
248+ parts := strings.Split(path, "/")
249+ if len(parts) == 1 {
250+ container := parts[0]
251+ s.handleContainers(container, w, r)
252+ } else if len(parts) == 2 {
253+ container := parts[0]
254+ object := parts[1]
255+ s.handleObjects(container, object, w, r)
256+ } else {
257+ panic("not implemented request: " + r.URL.Path)
258+ }
259+}
260
261=== added file 'testservices/swiftservice/service_http_test.go'
262--- testservices/swiftservice/service_http_test.go 1970-01-01 00:00:00 +0000
263+++ testservices/swiftservice/service_http_test.go 2012-11-27 19:03:21 +0000
264@@ -0,0 +1,205 @@
265+// Swift double testing service - HTTP API tests
266+
267+package swiftservice
268+
269+import (
270+ "bytes"
271+ "io/ioutil"
272+ . "launchpad.net/gocheck"
273+ "launchpad.net/goose/testing/httpsuite"
274+ "net/http"
275+)
276+
277+type SwiftHTTPSuite struct {
278+ httpsuite.HTTPSuite
279+ service SwiftService
280+}
281+
282+var _ = Suite(&SwiftHTTPSuite{})
283+
284+func (s *SwiftHTTPSuite) SetUpSuite(c *C) {
285+ s.HTTPSuite.SetUpSuite(c)
286+ s.service = New(s.Server.URL, baseURL, token)
287+}
288+
289+func (s *SwiftHTTPSuite) SetUpTest(c *C) {
290+ s.HTTPSuite.SetUpTest(c)
291+ s.Mux.Handle(baseURL, s.service)
292+}
293+
294+func (s *SwiftHTTPSuite) TearDownTest(c *C) {
295+ s.HTTPSuite.TearDownTest(c)
296+}
297+
298+func (s *SwiftHTTPSuite) TearDownSuite(c *C) {
299+ s.HTTPSuite.TearDownSuite(c)
300+}
301+
302+func (s *SwiftHTTPSuite) sendRequest(method, path string, body []byte) (*http.Response, error) {
303+ var req *http.Request
304+ var err error
305+ url := s.Server.URL + baseURL + path
306+ if body != nil {
307+ req, err = http.NewRequest(method, url, bytes.NewBuffer(body))
308+ } else {
309+ req, err = http.NewRequest(method, url, nil)
310+ }
311+ if err != nil {
312+ return nil, err
313+ }
314+ req.Header.Add("X-Auth-Token", token)
315+ client := &http.Client{}
316+ return client.Do(req)
317+}
318+
319+func (s *SwiftHTTPSuite) TestContainerHandling(c *C) {
320+ ok := s.service.HasContainer("test")
321+ c.Assert(ok, Equals, false)
322+
323+ resp, err := s.sendRequest("PUT", "test", nil)
324+ c.Assert(err, IsNil)
325+ c.Assert(resp.StatusCode, Equals, http.StatusCreated)
326+
327+ ok = s.service.HasContainer("test")
328+ c.Assert(ok, Equals, true)
329+
330+ resp, err = s.sendRequest("PUT", "test", nil)
331+ c.Assert(err, IsNil)
332+ c.Assert(resp.StatusCode, Equals, http.StatusAccepted)
333+
334+ ok = s.service.HasContainer("test")
335+ c.Assert(ok, Equals, true)
336+
337+ resp, err = s.sendRequest("GET", "test", nil)
338+ c.Assert(err, IsNil)
339+ c.Assert(resp.StatusCode, Equals, http.StatusOK)
340+
341+ ok = s.service.HasContainer("test")
342+ c.Assert(ok, Equals, true)
343+
344+ resp, err = s.sendRequest("DELETE", "test", nil)
345+ c.Assert(err, IsNil)
346+ c.Assert(resp.StatusCode, Equals, http.StatusNoContent)
347+
348+ ok = s.service.HasContainer("test")
349+ c.Assert(ok, Equals, false)
350+
351+ resp, err = s.sendRequest("DELETE", "test", nil)
352+ c.Assert(err, IsNil)
353+ c.Assert(resp.StatusCode, Equals, http.StatusNotFound)
354+
355+ ok = s.service.HasContainer("test")
356+ c.Assert(ok, Equals, false)
357+
358+ resp, err = s.sendRequest("GET", "test", nil)
359+ c.Assert(err, IsNil)
360+ c.Assert(resp.StatusCode, Equals, http.StatusNotFound)
361+
362+ ok = s.service.HasContainer("test")
363+ c.Assert(ok, Equals, false)
364+}
365+
366+func (s *SwiftHTTPSuite) TestObjectsHandling(c *C) {
367+ _, err := s.service.GetObject("test", "obj")
368+ c.Assert(err, Not(IsNil))
369+
370+ resp, err := s.sendRequest("GET", "test/obj", nil)
371+ c.Assert(err, IsNil)
372+ c.Assert(resp.StatusCode, Equals, http.StatusNotFound)
373+
374+ data := []byte("test data")
375+ err = s.service.AddObject("test", "obj", data)
376+ c.Assert(err, IsNil)
377+
378+ resp, err = s.sendRequest("GET", "test/obj", nil)
379+ c.Assert(err, IsNil)
380+ c.Assert(resp.StatusCode, Equals, http.StatusOK)
381+ defer resp.Body.Close()
382+ body, err := ioutil.ReadAll(resp.Body)
383+ c.Assert(err, IsNil)
384+ c.Assert(body, DeepEquals, data)
385+
386+ resp, err = s.sendRequest("DELETE", "test/obj", nil)
387+ c.Assert(err, IsNil)
388+ c.Assert(resp.StatusCode, Equals, http.StatusNoContent)
389+
390+ _, err = s.service.GetObject("test", "obj")
391+ c.Assert(err, Not(IsNil))
392+
393+ ok := s.service.HasContainer("test")
394+ c.Assert(ok, Equals, true)
395+
396+ resp, err = s.sendRequest("PUT", "test/obj", data)
397+ c.Assert(err, IsNil)
398+ c.Assert(resp.StatusCode, Equals, http.StatusCreated)
399+
400+ err = s.service.RemoveContainer("test")
401+ c.Assert(err, IsNil)
402+
403+ ok = s.service.HasContainer("test")
404+ c.Assert(ok, Equals, false)
405+
406+ resp, err = s.sendRequest("PUT", "test/obj", data)
407+ c.Assert(err, IsNil)
408+ c.Assert(resp.StatusCode, Equals, http.StatusNotFound)
409+
410+ resp, err = s.sendRequest("GET", "test/obj", data)
411+ c.Assert(err, IsNil)
412+ c.Assert(resp.StatusCode, Equals, http.StatusNotFound)
413+
414+ resp, err = s.sendRequest("DELETE", "test/obj", data)
415+ c.Assert(err, IsNil)
416+ c.Assert(resp.StatusCode, Equals, http.StatusNotFound)
417+
418+ err = s.service.AddContainer("test")
419+ c.Assert(err, IsNil)
420+
421+ ok = s.service.HasContainer("test")
422+ c.Assert(ok, Equals, true)
423+
424+ _, err = s.service.GetObject("test", "obj")
425+ c.Assert(err, Not(IsNil))
426+
427+ resp, err = s.sendRequest("GET", "test/obj", data)
428+ c.Assert(err, IsNil)
429+ c.Assert(resp.StatusCode, Equals, http.StatusNotFound)
430+
431+ resp, err = s.sendRequest("DELETE", "test/obj", data)
432+ c.Assert(err, IsNil)
433+ c.Assert(resp.StatusCode, Equals, http.StatusNotFound)
434+
435+ resp, err = s.sendRequest("PUT", "test/obj", data)
436+ c.Assert(err, IsNil)
437+ c.Assert(resp.StatusCode, Equals, http.StatusCreated)
438+
439+ objdata, err := s.service.GetObject("test", "obj")
440+ c.Assert(err, IsNil)
441+ c.Assert(objdata, DeepEquals, data)
442+
443+ resp, err = s.sendRequest("GET", "test/obj", nil)
444+ c.Assert(err, IsNil)
445+ c.Assert(resp.StatusCode, Equals, http.StatusOK)
446+ defer resp.Body.Close()
447+ body, err = ioutil.ReadAll(resp.Body)
448+ c.Assert(body, DeepEquals, data)
449+
450+ newdata := []byte("new test data")
451+ resp, err = s.sendRequest("PUT", "test/obj", newdata)
452+ c.Assert(err, IsNil)
453+ c.Assert(resp.StatusCode, Equals, http.StatusCreated)
454+
455+ objdata, err = s.service.GetObject("test", "obj")
456+ c.Assert(err, IsNil)
457+ c.Assert(objdata, DeepEquals, newdata)
458+
459+ resp, err = s.sendRequest("GET", "test/obj", nil)
460+ c.Assert(err, IsNil)
461+ c.Assert(resp.StatusCode, Equals, http.StatusOK)
462+ defer resp.Body.Close()
463+ body, err = ioutil.ReadAll(resp.Body)
464+ c.Assert(err, IsNil)
465+ c.Assert(body, DeepEquals, newdata)
466+
467+ err = s.service.RemoveContainer("test")
468+ c.Assert(err, IsNil)
469+}
470
471=== added file 'testservices/swiftservice/service_test.go'
472--- testservices/swiftservice/service_test.go 1970-01-01 00:00:00 +0000
473+++ testservices/swiftservice/service_test.go 2012-11-27 19:03:21 +0000
474@@ -0,0 +1,86 @@
475+// Swift double testing service - internal direct API tests
476+
477+package swiftservice
478+
479+import (
480+ . "launchpad.net/gocheck"
481+)
482+
483+type SwiftServiceSuite struct {
484+ service SwiftService
485+}
486+
487+var baseURL = "/v1/AUTH_tenant/"
488+var token = "token"
489+var hostname = "localhost" // not really used here
490+
491+var _ = Suite(&SwiftServiceSuite{})
492+
493+func (s *SwiftServiceSuite) SetUpSuite(c *C) {
494+ s.service = New(hostname, baseURL, token)
495+}
496+
497+func (s *SwiftServiceSuite) TestAddHasRemoveContainer(c *C) {
498+ ok := s.service.HasContainer("test")
499+ c.Assert(ok, Equals, false)
500+ err := s.service.AddContainer("test")
501+ c.Assert(err, IsNil)
502+ ok = s.service.HasContainer("test")
503+ c.Assert(ok, Equals, true)
504+ err = s.service.RemoveContainer("test")
505+ c.Assert(err, IsNil)
506+ ok = s.service.HasContainer("test")
507+ c.Assert(ok, Equals, false)
508+}
509+
510+func (s *SwiftServiceSuite) TestAddGetRemoveObject(c *C) {
511+ _, err := s.service.GetObject("test", "obj")
512+ c.Assert(err, Not(IsNil))
513+ err = s.service.AddContainer("test")
514+ c.Assert(err, IsNil)
515+ ok := s.service.HasContainer("test")
516+ c.Assert(ok, Equals, true)
517+ data := []byte("test data")
518+ err = s.service.AddObject("test", "obj", data)
519+ c.Assert(err, IsNil)
520+ objdata, err := s.service.GetObject("test", "obj")
521+ c.Assert(err, IsNil)
522+ c.Assert(objdata, DeepEquals, data)
523+ err = s.service.RemoveObject("test", "obj")
524+ c.Assert(err, IsNil)
525+ _, err = s.service.GetObject("test", "obj")
526+ c.Assert(err, Not(IsNil))
527+ err = s.service.RemoveContainer("test")
528+ c.Assert(err, IsNil)
529+ ok = s.service.HasContainer("test")
530+ c.Assert(ok, Equals, false)
531+}
532+
533+func (s *SwiftServiceSuite) TestRemoveContainerWithObjects(c *C) {
534+ ok := s.service.HasContainer("test")
535+ c.Assert(ok, Equals, false)
536+ err := s.service.AddObject("test", "obj", []byte("test data"))
537+ c.Assert(err, IsNil)
538+ err = s.service.RemoveContainer("test")
539+ c.Assert(err, IsNil)
540+ _, err = s.service.GetObject("test", "obj")
541+ c.Assert(err, Not(IsNil))
542+}
543+
544+func (s *SwiftServiceSuite) TestGetURL(c *C) {
545+ ok := s.service.HasContainer("test")
546+ c.Assert(ok, Equals, false)
547+ err := s.service.AddContainer("test")
548+ c.Assert(err, IsNil)
549+ data := []byte("test data")
550+ err = s.service.AddObject("test", "obj", data)
551+ c.Assert(err, IsNil)
552+ url, err := s.service.GetURL("test", "obj")
553+ path := baseURL + "test/obj"
554+ c.Assert(err, IsNil)
555+ c.Assert(url, Equals, hostname+path)
556+ err = s.service.RemoveContainer("test")
557+ c.Assert(err, IsNil)
558+ ok = s.service.HasContainer("test")
559+ c.Assert(ok, Equals, false)
560+}
561
562=== added file 'testservices/swiftservice/setup_test.go'
563--- testservices/swiftservice/setup_test.go 1970-01-01 00:00:00 +0000
564+++ testservices/swiftservice/setup_test.go 2012-11-27 19:03:21 +0000
565@@ -0,0 +1,10 @@
566+package swiftservice
567+
568+import (
569+ . "launchpad.net/gocheck"
570+ "testing"
571+)
572+
573+func Test(t *testing.T) {
574+ TestingT(t)
575+}
576
577=== added file 'testservices/swiftservice/swiftservice.go'
578--- testservices/swiftservice/swiftservice.go 1970-01-01 00:00:00 +0000
579+++ testservices/swiftservice/swiftservice.go 2012-11-27 19:03:21 +0000
580@@ -0,0 +1,16 @@
581+package swiftservice
582+
583+import (
584+ "net/http"
585+)
586+
587+type SwiftService interface {
588+ AddContainer(name string) error
589+ AddObject(container, name string, data []byte) error
590+ HasContainer(name string) bool
591+ GetObject(container, name string) ([]byte, error)
592+ RemoveContainer(name string) error
593+ RemoveObject(container, name string) error
594+ GetURL(container, object string) (string, error)
595+ ServeHTTP(w http.ResponseWriter, r *http.Request)
596+}

Subscribers

People subscribed via source and target branches