Merge lp:~dstroppa/juju-core/joyent-provider-storage into lp:~go-bot/juju-core/trunk
- joyent-provider-storage
- Merge into trunk
Status: | Work in progress |
---|---|
Proposed branch: | lp:~dstroppa/juju-core/joyent-provider-storage |
Merge into: | lp:~go-bot/juju-core/trunk |
Diff against target: |
1198 lines (+685/-134) 13 files modified
.bzrignore (+2/-0) dependencies.tsv (+1/-0) provider/joyent/config.go (+41/-15) provider/joyent/config_test.go (+63/-47) provider/joyent/environ.go (+28/-13) provider/joyent/environ_firewall.go (+3/-3) provider/joyent/environ_instance.go (+4/-4) provider/joyent/export_test.go (+3/-2) provider/joyent/instance.go (+1/-1) provider/joyent/joyent_test.go (+167/-14) provider/joyent/provider.go (+5/-1) provider/joyent/storage.go (+178/-34) provider/joyent/storage_test.go (+189/-0) |
To merge this branch: | bzr merge lp:~dstroppa/juju-core/joyent-provider-storage |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email: mp+200851@code.launchpad.net |
Commit message
provider/joyent: Implementation of storage
Add storage implementation to joyent provider. Also
pulls in new gojoyent library dependency.
R=gz, jameinel
Description of the change
Joyent Provider - Storage impl
Daniele Stroppa (dstroppa) wrote : | # |
Please take a look.
Martin Packman (gz) wrote : | # |
Thanks for getting this next step up for review! Several things in need
of fixing:
https:/
File dependencies.tsv (right):
https:/
dependencies.tsv:7:
launchpad.
<email address hidden> 47
It seems gojoyent in turn depends on github.
tests in localservices/
That would also need adding here - do we want it as a dep or should the
tests just generate sample uuids through some other method?
https:/
File provider/
https:/
provider/
What's the reason to change this from external to internal testing?
https:/
provider/
"12:c3:
Not changed here, but duplicated later, so mentioning. These fake values
should be faker, rather than being for name and a real-looking key id.
Better to spot in test assertions and such like if it's testuser and
00:11:22:... or similar obvious made up values.
https:/
File provider/
https:/
provider/
"12:c3:
These values look very similar to the earlier ones, perhaps they need to
be in a shared location and accessed from there. Otherwise, same comment
applies, user and key-id should be more obviously fake.
https:/
File provider/
https:/
provider/
comtainer from the storage account.
Typo, 'coNtainer'.
https:/
provider/
can retrieve the object at, without any credentials
This may as well be an actual function doc comment.
https:/
provider/
time.Now(
I think the EC2 provider uses a mcuh longer duration, what a sensible
value is I'm less sure.
https:/
provider/
Remove.
https:/
provider/
error {
This is logic I'm not wild about having done (and slightly differently)
in all provide...
Daniele Stroppa (dstroppa) wrote : | # |
https:/
File provider/
https:/
provider/
comtainer from the storage account.
On 2014/01/09 17:50:34, gz wrote:
> Typo, 'coNtainer'.
Done.
https:/
provider/
can retrieve the object at, without any credentials
On 2014/01/09 17:50:34, gz wrote:
> This may as well be an actual function doc comment.
Done.
https:/
provider/
time.Now(
On 2014/01/09 17:50:34, gz wrote:
> I think the EC2 provider uses a mcuh longer duration, what a sensible
value is
> I'm less sure.
5 minutes is the value used in the Node.js SDK (used as reference). EC2,
Azure and OpenStack providers use 10 years. Changed to 10 years to be
consistent.
https:/
provider/
On 2014/01/09 17:50:34, gz wrote:
> Remove.
Done.
Daniele Stroppa (dstroppa) wrote : | # |
https:/
File dependencies.tsv (right):
https:/
dependencies.tsv:7:
launchpad.
<email address hidden> 47
On 2014/01/09 17:50:34, gz wrote:
> It seems gojoyent in turn depends on github.
tests in
> localservices/
> That would also need adding here - do we want it as a dep or should
the tests
> just generate sample uuids through some other method?
Replaced the gouuid dep with a custom method implemented with std libs.
Daniele Stroppa (dstroppa) wrote : | # |
https:/
File provider/
https:/
provider/
On 2014/01/09 17:50:34, gz wrote:
> What's the reason to change this from external to internal testing?
No reason actually, it was just a mistake. Changed it to external
testing.
Daniele Stroppa (dstroppa) wrote : | # |
https:/
File provider/
https:/
provider/
"12:c3:
On 2014/01/09 17:50:34, gz wrote:
> Not changed here, but duplicated later, so mentioning. These fake
values should
> be faker, rather than being for name and a real-looking key id. Better
to spot
> in test assertions and such like if it's testuser and 00:11:22:... or
similar
> obvious made up values.
Changed to obvious made up values.
Daniele Stroppa (dstroppa) wrote : | # |
https:/
File provider/
https:/
provider/
"12:c3:
On 2014/01/09 17:50:34, gz wrote:
> These values look very similar to the earlier ones, perhaps they need
to be in a
> shared location and accessed from there. Otherwise, same comment
applies, user
> and key-id should be more obviously fake.
Changed to valid fake values.
Daniele Stroppa (dstroppa) wrote : | # |
https:/
File provider/
https:/
provider/
On 2014/01/09 17:50:34, gz wrote:
> All the tests in this file bar TestURL fail when I run them here, with
> variations on:
> FAIL: storage_
> storage_
> s.assertContain
> storage_test.go:43:
> c.Assert(err, gc.IsNil)
> ... value *errors.
> &errors.
> errcode:
("failed to
> create directory: juju-test\ncaused by: Bad request
> https:/
request
> (https:/
unexpected
> status: 400; error info:
{\"code\
> header invalid: signature was not specified\"}")
> It seems they're escaping isolation, rather than using the test
services?
Changed the storage test to run against the local services instead of
the Joyent Live environment
Daniele Stroppa (dstroppa) wrote : | # |
Please take a look.
Martin Packman (gz) wrote : | # |
LGTM, should be ready to go with a settled gojoyent version.
William Reade (fwereade) wrote : | # |
Martin, is this ready to land now?
John A Meinel (jameinel) wrote : | # |
On 2014/01/23 16:23:42, gz wrote:
> LGTM, should be ready to go with a settled gojoyent version.
We're still hanging on this 10 days later. It does seem like we should
just land it so that we can start ratcheting forward rather than
delaying it.
Daniele Stroppa (dstroppa) wrote : | # |
Please take a look.
Go Bot (go-bot) wrote : | # |
The attempt to merge lp:~dstroppa/juju-core/joyent-provider-storage into lp:juju-core failed. Below is the output from the failed tests.
provider/
/usr/lib/
/home/
provider/
/usr/lib/
/home/
provider/
/usr/lib/
/home/
Go Bot (go-bot) wrote : | # |
The attempt to merge lp:~dstroppa/juju-core/joyent-provider-storage into lp:juju-core failed. Below is the output from the failed tests.
-------
FAIL: dependencies_
dependencies_
c.Assert(
... obtained []string = []string{
... n int = 4
OOPS: 0 passed, 1 FAILED
--- FAIL: Test (0.00 seconds)
FAIL
FAIL launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
? launchpad.
? launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
Go Bot (go-bot) wrote : | # |
There are additional revisions which have not been approved in review. Please seek review and approval of these new revisions.
Go Bot (go-bot) wrote : | # |
The attempt to merge lp:~dstroppa/juju-core/joyent-provider-storage into lp:juju-core failed. Below is the output from the failed tests.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
? launchpad.
? launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
? launchpad.
Go Bot (go-bot) wrote : | # |
The attempt to merge lp:~dstroppa/juju-core/joyent-provider-storage into lp:juju-core failed. Below is the output from the failed tests.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
? launchpad.
*** Test killed: ran too long (10m0s).
FAIL launchpad.
-------
FAIL: machine_
[LOG] 39.51530 DEBUG juju.environs.
[LOG] 44.18314 DEBUG juju.environs.tools reading v1.* tools
[LOG] 44.18320 INFO juju environs/testing: uploading FAKE tools 1.17.4-
[LOG] 44.18412 DEBUG juju.environs.tools no architecture specified when finding tools, looking for any
[LOG] 44.18413 DEBUG juju.environs.tools no series specified when finding tools, looking for any
[LOG] 44.18430 DEBUG juju.environs.
[LOG] 44.18432 DEBUG juju.environs.
[LOG] 44.18436 DEBUG juju.environs.
[LOG] 44.18437 DEBUG juju.environs.
[LOG] 44.18482 INFO juju.environs.tools Writing tools/streams/
[LOG] 44.18504 INFO juju.environs.tools Writing tools/streams/
[LOG] 44.18508 INFO juju.environs.
[LOG] 44.18512 DEBUG juju.environs.
[LOG] 44.18514 INFO juju.environs.tools reading tools with major.minor version 1.17
[LOG] 44.18515 INFO juju.environs.tools filtering tools by version: 1.17.4
[LOG] 44.18517 INFO juju.environs.tools filtering tools by series: precise
[LOG] 44.18519 DEBUG juju.environs.tools no architecture specified when finding tools, looking for any
[LOG] 44.18522 DEBUG juju.environs.
[LOG] 44.18524 DEBUG juju.environs.
[LOG] 44.18534 DEBUG juju.environs.
Go Bot (go-bot) wrote : | # |
The attempt to merge lp:~dstroppa/juju-core/joyent-provider-storage into lp:juju-core failed. Below is the output from the failed tests.
provider/
/usr/lib/
/home/
provider/
/usr/lib/
/home/
provider/
/usr/lib/
/home/
Unmerged revisions
- 1983. By Daniele Stroppa
-
Updated dependencies.tsv: tabs instead of spaces
- 1982. By Daniele Stroppa
-
Merged trunk
- 1981. By Daniele Stroppa
-
Updated GoJoyent dependency
Preview Diff
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2013-08-27 15:27:47 +0000 |
3 | +++ .bzrignore 2014-02-27 18:08:22 +0000 |
4 | @@ -8,3 +8,5 @@ |
5 | .emacs.desktop |
6 | .emacs.desktop.lock |
7 | *.test |
8 | +.idea |
9 | +add-joyent-provider.iml |
10 | |
11 | === modified file 'dependencies.tsv' |
12 | --- dependencies.tsv 2014-02-25 22:19:30 +0000 |
13 | +++ dependencies.tsv 2014-02-27 18:08:22 +0000 |
14 | @@ -6,6 +6,7 @@ |
15 | launchpad.net/gnuflag bzr roger.peppe@canonical.com-20121003093437-zcyyw0lpvj2nifpk 12 |
16 | launchpad.net/goamz bzr roger.peppe@canonical.com-20131218155244-hbnkvlkkzy3vmlh9 44 |
17 | launchpad.net/gocheck bzr gustavo@niemeyer.net-20130302024745-6ikofwq2c03h7giu 85 |
18 | +launchpad.net/gojoyent bzr daniele.stroppa@joyent.com-20140225172734-ltce6n3vrhj3cngg 61 |
19 | launchpad.net/golxc bzr ian.booth@canonical.com-20140116225718-lvoikrb0me6zugin 6 |
20 | launchpad.net/gomaasapi bzr ian.booth@canonical.com-20131017011445-m1hmr0ap14osd7li 47 |
21 | launchpad.net/goose bzr tarmac-20140124165235-h9rloooc531udms5 116 |
22 | |
23 | === modified file 'provider/joyent/config.go' |
24 | --- provider/joyent/config.go 2013-11-21 17:53:05 +0000 |
25 | +++ provider/joyent/config.go 2014-02-27 18:08:22 +0000 |
26 | @@ -22,16 +22,21 @@ |
27 | # sdc-user: <secret> |
28 | # Can be set via env variables, or specified here |
29 | # sdc-key-id: <secret> |
30 | - # region defaults to us-west-1, override if required |
31 | - # sdc-region: us-west-1 |
32 | + # url defaults to us-west-1 DC, override if required |
33 | + # sdc-url: https://us-west-1.api.joyentcloud.com |
34 | |
35 | # Manta config |
36 | # Can be set via env variables, or specified here |
37 | # manta-user: <secret> |
38 | # Can be set via env variables, or specified here |
39 | # manta-key-id: <secret> |
40 | - # region defaults to us-east, override if required |
41 | - # manta-region: us-east |
42 | + # url defaults to us-east DC, override if required |
43 | + # manta-url: https://us-east.manta.joyent.com |
44 | + |
45 | + # Auth config |
46 | + # key-file: <secret> |
47 | + # algorithm defaults to rsa-sha256, override if required |
48 | + # algorithm: rsa-sha256 |
49 | ` |
50 | |
51 | const ( |
52 | @@ -41,6 +46,7 @@ |
53 | MantaUser = "MANTA_USER" |
54 | MantaKeyId = "MANTA_KEY_ID" |
55 | MantaUrl = "MANTA_URL" |
56 | + Home = "HOME" |
57 | ) |
58 | |
59 | var environmentVariables = map[string]string{ |
60 | @@ -48,21 +54,25 @@ |
61 | "sdc-key-id": SdcKeyId, |
62 | "manta-user": MantaUser, |
63 | "manta-key-id": MantaKeyId, |
64 | + "key-file": Home, |
65 | } |
66 | |
67 | var configFields = schema.Fields{ |
68 | "sdc-user": schema.String(), |
69 | "sdc-key-id": schema.String(), |
70 | - "sdc-region": schema.String(), |
71 | + "sdc-url": schema.String(), |
72 | "manta-user": schema.String(), |
73 | "manta-key-id": schema.String(), |
74 | - "manta-region": schema.String(), |
75 | + "manta-url": schema.String(), |
76 | + "key-file": schema.String(), |
77 | + "algorithm": schema.String(), |
78 | "control-dir": schema.String(), |
79 | } |
80 | |
81 | var configDefaultFields = schema.Defaults{ |
82 | - "sdc-region": "us-west-1", |
83 | - "manta-region": "us-east", |
84 | + "sdc-url": "https://us-west-1.api.joyentcloud.com", |
85 | + "manta-url": "https://us-east.manta.joyent.com", |
86 | + "algorithm": "rsa-sha256", |
87 | } |
88 | |
89 | var configSecretFields = []string{ |
90 | @@ -70,11 +80,14 @@ |
91 | "sdc-key-id", |
92 | "manta-user", |
93 | "manta-key-id", |
94 | + "key-file", |
95 | } |
96 | |
97 | var configImmutableFields = []string{ |
98 | - "sdc-region", |
99 | - "manta-region", |
100 | + "sdc-url", |
101 | + "manta-url", |
102 | + "key-file", |
103 | + "algorithm", |
104 | } |
105 | |
106 | func prepareConfig(cfg *config.Config) (*config.Config, error) { |
107 | @@ -86,7 +99,12 @@ |
108 | if err != nil { |
109 | return nil, err |
110 | } |
111 | - attrs["control-bucket"] = fmt.Sprintf("%x", uuid.Raw()) |
112 | + attrs["control-dir"] = fmt.Sprintf("%x", uuid.Raw()) |
113 | + } |
114 | + |
115 | + if _, ok := attrs["key-file"]; !ok { |
116 | + localEnvVariableHome := os.Getenv(environmentVariables["key-file"]) |
117 | + attrs["key-file"] = fmt.Sprintf("%s/.ssh/id_rsa", localEnvVariableHome) |
118 | } |
119 | |
120 | // Read env variables |
121 | @@ -165,8 +183,8 @@ |
122 | attrs map[string]interface{} |
123 | } |
124 | |
125 | -func (ecfg *environConfig) sdcRegion() string { |
126 | - return ecfg.attrs["sdc-region"].(string) |
127 | +func (ecfg *environConfig) sdcUrl() string { |
128 | + return ecfg.attrs["sdc-url"].(string) |
129 | } |
130 | |
131 | func (ecfg *environConfig) sdcUser() string { |
132 | @@ -177,8 +195,8 @@ |
133 | return ecfg.attrs["sdc-key-id"].(string) |
134 | } |
135 | |
136 | -func (ecfg *environConfig) mantaRegion() string { |
137 | - return ecfg.attrs["manta-region"].(string) |
138 | +func (ecfg *environConfig) mantaUrl() string { |
139 | + return ecfg.attrs["manta-url"].(string) |
140 | } |
141 | |
142 | func (ecfg *environConfig) mantaUser() string { |
143 | @@ -189,6 +207,14 @@ |
144 | return ecfg.attrs["manta-key-id"].(string) |
145 | } |
146 | |
147 | +func (ecfg *environConfig) keyFile() string { |
148 | + return ecfg.attrs["key-file"].(string) |
149 | +} |
150 | + |
151 | +func (ecfg *environConfig) algorithm() string { |
152 | + return ecfg.attrs["algorithm"].(string) |
153 | +} |
154 | + |
155 | func (c *environConfig) controlDir() string { |
156 | return c.attrs["control-dir"].(string) |
157 | } |
158 | |
159 | === modified file 'provider/joyent/config_test.go' |
160 | --- provider/joyent/config_test.go 2014-02-13 02:46:58 +0000 |
161 | +++ provider/joyent/config_test.go 2014-02-27 18:08:22 +0000 |
162 | @@ -8,7 +8,7 @@ |
163 | |
164 | "launchpad.net/juju-core/environs" |
165 | "launchpad.net/juju-core/environs/config" |
166 | - "launchpad.net/juju-core/provider/joyent" |
167 | + jp "launchpad.net/juju-core/provider/joyent" |
168 | "launchpad.net/juju-core/testing" |
169 | "launchpad.net/juju-core/testing/testbase" |
170 | ) |
171 | @@ -23,12 +23,14 @@ |
172 | func validAttrs() testing.Attrs { |
173 | return testing.FakeConfig().Merge(testing.Attrs{ |
174 | "type": "joyent", |
175 | - "sdc-user": "dstroppa", |
176 | - "sdc-key-id": "12:c3:a7:cb:a2:29:e2:90:88:3f:04:53:3b:4e:75:40", |
177 | - "sdc-region": "us-west-1", |
178 | - "manta-user": "dstroppa", |
179 | - "manta-key-id": "12:c3:a7:cb:a2:29:e2:90:88:3f:04:53:3b:4e:75:40", |
180 | - "manta-region": "us-east", |
181 | + "sdc-user": "juju-test", |
182 | + "sdc-key-id": "00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff", |
183 | + "sdc-url": "https://test.api.joyentcloud.com", |
184 | + "manta-user": "juju-test", |
185 | + "manta-key-id": "00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff", |
186 | + "manta-url": "https://test.manta.joyent.com", |
187 | + "key-file": "~/.ssh/id_rsa", |
188 | + "algorithm": "rsa-sha256", |
189 | "control-dir": "juju-test", |
190 | }) |
191 | } |
192 | @@ -41,10 +43,10 @@ |
193 | var _ = gc.Suite(&ConfigSuite{}) |
194 | |
195 | func (s *ConfigSuite) SetUpSuite(c *gc.C) { |
196 | - s.PatchEnvironment(joyent.SdcAccount, "tester") |
197 | - s.PatchEnvironment(joyent.SdcKeyId, "11:c4:b6:c0:a3:24:22:96:a8:1f:07:53:3f:8e:14:7a") |
198 | - s.PatchEnvironment(joyent.MantaUser, "tester") |
199 | - s.PatchEnvironment(joyent.MantaKeyId, "11:c4:b6:c0:a3:24:22:96:a8:1f:07:53:3f:8e:14:7a") |
200 | + s.PatchEnvironment(jp.SdcAccount, "tester") |
201 | + s.PatchEnvironment(jp.SdcKeyId, "ff:ee:dd:cc:bb:aa:99:88:77:66:55:44:33:22:11:00") |
202 | + s.PatchEnvironment(jp.MantaUser, "tester") |
203 | + s.PatchEnvironment(jp.MantaKeyId, "ff:ee:dd:cc:bb:aa:99:88:77:66:55:44:33:22:11:00") |
204 | } |
205 | |
206 | var newConfigTests = []struct { |
207 | @@ -70,16 +72,16 @@ |
208 | insert: testing.Attrs{"sdc-key-id": ""}, |
209 | err: "sdc-key-id: must not be empty", |
210 | }, { |
211 | - info: "sdc-region is inserted if missing", |
212 | - expect: testing.Attrs{"sdc-region": "us-west-1"}, |
213 | -}, { |
214 | - info: "sdc-region cannot be empty", |
215 | - insert: testing.Attrs{"sdc-region": ""}, |
216 | - err: "sdc-region: must not be empty", |
217 | -}, { |
218 | - info: "sdc-region is untouched if present", |
219 | - insert: testing.Attrs{"sdc-region": "us-west-1"}, |
220 | - expect: testing.Attrs{"sdc-region": "us-west-1"}, |
221 | + info: "sdc-url is inserted if missing", |
222 | + expect: testing.Attrs{"sdc-url": "https://test.api.joyentcloud.com"}, |
223 | +}, { |
224 | + info: "sdc-url cannot be empty", |
225 | + insert: testing.Attrs{"sdc-url": ""}, |
226 | + err: "sdc-url: must not be empty", |
227 | +}, { |
228 | + info: "sdc-url is untouched if present", |
229 | + insert: testing.Attrs{"sdc-url": "https://test.api.joyentcloud.com"}, |
230 | + expect: testing.Attrs{"sdc-url": "https://test.api.joyentcloud.com"}, |
231 | }, { |
232 | info: "manta-user is required", |
233 | remove: []string{"manta-user"}, |
234 | @@ -97,16 +99,30 @@ |
235 | insert: testing.Attrs{"manta-key-id": ""}, |
236 | err: "manta-key-id: must not be empty", |
237 | }, { |
238 | - info: "manta-region is inserted if missing", |
239 | - expect: testing.Attrs{"manta-region": "us-east"}, |
240 | -}, { |
241 | - info: "manta-region cannot be empty", |
242 | - insert: testing.Attrs{"manta-region": ""}, |
243 | - err: "manta-region: must not be empty", |
244 | -}, { |
245 | - info: "manta-region is untouched if present", |
246 | - insert: testing.Attrs{"manta-region": "us-east"}, |
247 | - expect: testing.Attrs{"manta-region": "us-east"}, |
248 | + info: "manta-url is inserted if missing", |
249 | + expect: testing.Attrs{"manta-url": "https://test.manta.joyent.com"}, |
250 | +}, { |
251 | + info: "manta-url cannot be empty", |
252 | + insert: testing.Attrs{"manta-url": ""}, |
253 | + err: "manta-url: must not be empty", |
254 | +}, { |
255 | + info: "manta-url is untouched if present", |
256 | + insert: testing.Attrs{"manta-url": "https://test.manta.joyent.com"}, |
257 | + expect: testing.Attrs{"manta-url": "https://test.manta.joyent.com"}, |
258 | +}, { |
259 | + info: "key-file is inserted if missing", |
260 | + expect: testing.Attrs{"key-file": "~/.ssh/id_rsa"}, |
261 | +}, { |
262 | + info: "key-file cannot be empty", |
263 | + insert: testing.Attrs{"key-file": ""}, |
264 | + err: "key-file: must not be empty", |
265 | +}, { |
266 | + info: "algorithm is inserted if missing", |
267 | + expect: testing.Attrs{"algorithm": "rsa-sha256"}, |
268 | +}, { |
269 | + info: "algorithm cannot be empty", |
270 | + insert: testing.Attrs{"algorithm": ""}, |
271 | + err: "algorithm: must not be empty", |
272 | }, { |
273 | info: "unknown field is not touched", |
274 | insert: testing.Attrs{"unknown-field": 12345}, |
275 | @@ -137,7 +153,7 @@ |
276 | c.Logf("test %d: %s", i, test.info) |
277 | attrs := validAttrs().Merge(test.insert).Delete(test.remove...) |
278 | testConfig := newConfig(c, attrs) |
279 | - validatedConfig, err := joyent.Provider.Validate(testConfig, nil) |
280 | + validatedConfig, err := Provider.Validate(testConfig, nil) |
281 | if test.err == "" { |
282 | c.Assert(err, gc.IsNil) |
283 | attrs := validatedConfig.AllAttrs() |
284 | @@ -157,7 +173,7 @@ |
285 | c.Logf("test %d: %s", i, test.info) |
286 | attrs := validAttrs().Merge(test.insert).Delete(test.remove...) |
287 | testConfig := newConfig(c, attrs) |
288 | - validatedConfig, err := joyent.Provider.Validate(knownGoodConfig, testConfig) |
289 | + validatedConfig, err := Provider.Validate(knownGoodConfig, testConfig) |
290 | if test.err == "" { |
291 | c.Assert(err, gc.IsNil) |
292 | attrs := validatedConfig.AllAttrs() |
293 | @@ -186,24 +202,24 @@ |
294 | expect: testing.Attrs{"sdc-user": "joyent_user"}, |
295 | }, { |
296 | info: "can change sdc-key-id", |
297 | - insert: testing.Attrs{"sdc-key-id": "11:c4:b6:c0:a3:24:22:96:a8:1f:07:53:3f:8e:14:7a"}, |
298 | - expect: testing.Attrs{"sdc-key-id": "11:c4:b6:c0:a3:24:22:96:a8:1f:07:53:3f:8e:14:7a"}, |
299 | + insert: testing.Attrs{"sdc-key-id": "ff:ee:dd:cc:bb:aa:99:88:77:66:55:44:33:22:11:00"}, |
300 | + expect: testing.Attrs{"sdc-key-id": "ff:ee:dd:cc:bb:aa:99:88:77:66:55:44:33:22:11:00"}, |
301 | }, { |
302 | - info: "can change sdc-region", |
303 | - insert: testing.Attrs{"sdc-region": "us-west-1"}, |
304 | - expect: testing.Attrs{"sdc-region": "us-west-1"}, |
305 | + info: "can change sdc-url", |
306 | + insert: testing.Attrs{"sdc-url": "https://test.api.joyentcloud.com"}, |
307 | + expect: testing.Attrs{"sdc-url": "https://test.api.joyentcloud.com"}, |
308 | }, { |
309 | info: "can change manta-user", |
310 | insert: testing.Attrs{"manta-user": "manta_user"}, |
311 | expect: testing.Attrs{"manta-user": "manta_user"}, |
312 | }, { |
313 | info: "can change manta-key-id", |
314 | - insert: testing.Attrs{"manta-key-id": "11:c4:b6:c0:a3:24:22:96:a8:1f:07:53:3f:8e:14:7a"}, |
315 | - expect: testing.Attrs{"manta-key-id": "11:c4:b6:c0:a3:24:22:96:a8:1f:07:53:3f:8e:14:7a"}, |
316 | + insert: testing.Attrs{"manta-key-id": "ff:ee:dd:cc:bb:aa:99:88:77:66:55:44:33:22:11:00"}, |
317 | + expect: testing.Attrs{"manta-key-id": "ff:ee:dd:cc:bb:aa:99:88:77:66:55:44:33:22:11:00"}, |
318 | }, { |
319 | - info: "can change manta-region", |
320 | - insert: testing.Attrs{"manta-region": "us-east"}, |
321 | - expect: testing.Attrs{"manta-region": "us-east"}, |
322 | + info: "can change manta-url", |
323 | + insert: testing.Attrs{"manta-url": "https://test.manta.joyent.com"}, |
324 | + expect: testing.Attrs{"manta-url": "https://test.manta.joyent.com"}, |
325 | }, { |
326 | info: "can insert unknown field", |
327 | insert: testing.Attrs{"unknown": "ignoti"}, |
328 | @@ -216,7 +232,7 @@ |
329 | c.Logf("test %d: %s", i, test.info) |
330 | attrs := validAttrs().Merge(test.insert).Delete(test.remove...) |
331 | testConfig := newConfig(c, attrs) |
332 | - validatedConfig, err := joyent.Provider.Validate(testConfig, baseConfig) |
333 | + validatedConfig, err := Provider.Validate(testConfig, baseConfig) |
334 | if test.err == "" { |
335 | c.Assert(err, gc.IsNil) |
336 | attrs := validatedConfig.AllAttrs() |
337 | @@ -270,7 +286,7 @@ |
338 | }, { |
339 | info: "can get sdc-key-id from env variable", |
340 | insert: testing.Attrs{"sdc-key-id": ""}, |
341 | - expect: testing.Attrs{"sdc-key-id": "11:c4:b6:c0:a3:24:22:96:a8:1f:07:53:3f:8e:14:7a"}, |
342 | + expect: testing.Attrs{"sdc-key-id": "ff:ee:dd:cc:bb:aa:99:88:77:66:55:44:33:22:11:00"}, |
343 | }, { |
344 | info: "can get manta-user from env variable", |
345 | insert: testing.Attrs{"manta-user": ""}, |
346 | @@ -278,7 +294,7 @@ |
347 | }, { |
348 | info: "can get manta-key-id from env variable", |
349 | insert: testing.Attrs{"manta-key-id": ""}, |
350 | - expect: testing.Attrs{"manta-key-id": "11:c4:b6:c0:a3:24:22:96:a8:1f:07:53:3f:8e:14:7a"}, |
351 | + expect: testing.Attrs{"manta-key-id": "ff:ee:dd:cc:bb:aa:99:88:77:66:55:44:33:22:11:00"}, |
352 | }} |
353 | |
354 | func (s *ConfigSuite) TestPrepare(c *gc.C) { |
355 | @@ -286,7 +302,7 @@ |
356 | c.Logf("test %d: %s", i, test.info) |
357 | attrs := validAttrs().Merge(test.insert).Delete(test.remove...) |
358 | testConfig := newConfig(c, attrs) |
359 | - preparedConfig, err := joyent.Provider.Prepare(testing.Context(c), testConfig) |
360 | + preparedConfig, err := Provider.Prepare(testing.Context(c), testConfig) |
361 | if test.err == "" { |
362 | c.Assert(err, gc.IsNil) |
363 | attrs := preparedConfig.Config().AllAttrs() |
364 | |
365 | === modified file 'provider/joyent/environ.go' |
366 | --- provider/joyent/environ.go 2013-12-19 08:40:11 +0000 |
367 | +++ provider/joyent/environ.go 2014-02-27 18:08:22 +0000 |
368 | @@ -23,7 +23,7 @@ |
369 | // must be implemented ) and environ_firewall.go (which can be safely |
370 | // ignored until you've got an environment bootstrapping successfully). |
371 | |
372 | -type environ struct { |
373 | +type JoyentEnviron struct { |
374 | name string |
375 | // All mutating operations should lock the mutex. Non-mutating operations |
376 | // should read all fields (other than name, which is immutable) from a |
377 | @@ -35,17 +35,32 @@ |
378 | storage storage.Storage |
379 | } |
380 | |
381 | -var _ environs.Environ = (*environ)(nil) |
382 | - |
383 | -func (env *environ) Name() string { |
384 | +var _ environs.Environ = (*JoyentEnviron)(nil) |
385 | + |
386 | +func NewEnviron(cfg *config.Config) (*JoyentEnviron, error) { |
387 | + env := new(JoyentEnviron) |
388 | + err := env.SetConfig(cfg) |
389 | + if err != nil { |
390 | + return nil, err |
391 | + } |
392 | + env.name = cfg.Name() |
393 | + env.storage = NewStorage(env) |
394 | + return env, nil |
395 | +} |
396 | + |
397 | +func (env *JoyentEnviron) SetName(envName string) { |
398 | + env.name = envName |
399 | +} |
400 | + |
401 | +func (env *JoyentEnviron) Name() string { |
402 | return env.name |
403 | } |
404 | |
405 | -func (*environ) Provider() environs.EnvironProvider { |
406 | +func (*JoyentEnviron) Provider() environs.EnvironProvider { |
407 | return providerInstance |
408 | } |
409 | |
410 | -func (env *environ) SetConfig(cfg *config.Config) error { |
411 | +func (env *JoyentEnviron) SetConfig(cfg *config.Config) error { |
412 | env.lock.Lock() |
413 | defer env.lock.Unlock() |
414 | ecfg, err := validateConfig(cfg, env.ecfg) |
415 | @@ -61,7 +76,7 @@ |
416 | return nil |
417 | } |
418 | |
419 | -func (env *environ) getSnapshot() *environ { |
420 | +func (env *JoyentEnviron) getSnapshot() *JoyentEnviron { |
421 | env.lock.Lock() |
422 | clone := *env |
423 | env.lock.Unlock() |
424 | @@ -69,26 +84,26 @@ |
425 | return &clone |
426 | } |
427 | |
428 | -func (env *environ) Config() *config.Config { |
429 | +func (env *JoyentEnviron) Config() *config.Config { |
430 | return env.getSnapshot().ecfg.Config |
431 | } |
432 | |
433 | -func (env *environ) Storage() storage.Storage { |
434 | +func (env *JoyentEnviron) Storage() storage.Storage { |
435 | return env.getSnapshot().storage |
436 | } |
437 | |
438 | -func (env *environ) PublicStorage() storage.StorageReader { |
439 | +func (env *JoyentEnviron) PublicStorage() storage.StorageReader { |
440 | return environs.EmptyStorage |
441 | } |
442 | |
443 | -func (env *environ) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) error { |
444 | +func (env *JoyentEnviron) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) error { |
445 | return common.Bootstrap(ctx, env, cons) |
446 | } |
447 | |
448 | -func (env *environ) StateInfo() (*state.Info, *api.Info, error) { |
449 | +func (env *JoyentEnviron) StateInfo() (*state.Info, *api.Info, error) { |
450 | return common.StateInfo(env) |
451 | } |
452 | |
453 | -func (env *environ) Destroy() error { |
454 | +func (env *JoyentEnviron) Destroy() error { |
455 | return common.Destroy(env) |
456 | } |
457 | |
458 | === modified file 'provider/joyent/environ_firewall.go' |
459 | --- provider/joyent/environ_firewall.go 2013-10-07 10:57:04 +0000 |
460 | +++ provider/joyent/environ_firewall.go 2014-02-27 18:08:22 +0000 |
461 | @@ -11,19 +11,19 @@ |
462 | // cause `juju expose` to work when the firewall-mode is "global". If you |
463 | // implement one of them, you should implement them all. |
464 | |
465 | -func (env *environ) OpenPorts(ports []instance.Port) error { |
466 | +func (env *JoyentEnviron) OpenPorts(ports []instance.Port) error { |
467 | logger.Warningf("pretending to open ports %v for all instances", ports) |
468 | _ = env.getSnapshot() |
469 | return nil |
470 | } |
471 | |
472 | -func (env *environ) ClosePorts(ports []instance.Port) error { |
473 | +func (env *JoyentEnviron) ClosePorts(ports []instance.Port) error { |
474 | logger.Warningf("pretending to close ports %v for all instances", ports) |
475 | _ = env.getSnapshot() |
476 | return nil |
477 | } |
478 | |
479 | -func (env *environ) Ports() ([]instance.Port, error) { |
480 | +func (env *JoyentEnviron) Ports() ([]instance.Port, error) { |
481 | _ = env.getSnapshot() |
482 | return nil, nil |
483 | } |
484 | |
485 | === modified file 'provider/joyent/environ_instance.go' |
486 | --- provider/joyent/environ_instance.go 2013-10-07 10:57:04 +0000 |
487 | +++ provider/joyent/environ_instance.go 2014-02-27 18:08:22 +0000 |
488 | @@ -10,7 +10,7 @@ |
489 | "launchpad.net/juju-core/tools" |
490 | ) |
491 | |
492 | -func (env *environ) StartInstance( |
493 | +func (env *JoyentEnviron) StartInstance( |
494 | cons constraints.Value, possibleTools tools.List, machineConf *cloudinit.MachineConfig, |
495 | ) ( |
496 | instance.Instance, *instance.HardwareCharacteristics, error, |
497 | @@ -22,7 +22,7 @@ |
498 | return nil, nil, errNotImplemented |
499 | } |
500 | |
501 | -func (env *environ) AllInstances() ([]instance.Instance, error) { |
502 | +func (env *JoyentEnviron) AllInstances() ([]instance.Instance, error) { |
503 | // Please note that this must *not* return instances that have not been |
504 | // allocated as part of this environment -- if it does, juju will see they |
505 | // are not tracked in state, assume they're stale/rogue, and shut them down. |
506 | @@ -30,7 +30,7 @@ |
507 | return nil, errNotImplemented |
508 | } |
509 | |
510 | -func (env *environ) Instances(ids []instance.Id) ([]instance.Instance, error) { |
511 | +func (env *JoyentEnviron) Instances(ids []instance.Id) ([]instance.Instance, error) { |
512 | // Please note that this must *not* return instances that have not been |
513 | // allocated as part of this environment -- if it does, juju will see they |
514 | // are not tracked in state, assume they're stale/rogue, and shut them down. |
515 | @@ -41,7 +41,7 @@ |
516 | return nil, errNotImplemented |
517 | } |
518 | |
519 | -func (env *environ) StopInstances(instances []instance.Instance) error { |
520 | +func (env *JoyentEnviron) StopInstances(instances []instance.Instance) error { |
521 | _ = env.getSnapshot() |
522 | return errNotImplemented |
523 | } |
524 | |
525 | === modified file 'provider/joyent/export_test.go' |
526 | --- provider/joyent/export_test.go 2013-10-07 10:57:04 +0000 |
527 | +++ provider/joyent/export_test.go 2014-02-27 18:08:22 +0000 |
528 | @@ -1,10 +1,11 @@ |
529 | // Copyright 2013 Joyent Inc. |
530 | // Licensed under the AGPLv3, see LICENCE file for details. |
531 | |
532 | -package joyent |
533 | +package joyent_test |
534 | |
535 | import ( |
536 | "launchpad.net/juju-core/environs" |
537 | + jp "launchpad.net/juju-core/provider/joyent" |
538 | ) |
539 | |
540 | -var Provider environs.EnvironProvider = providerInstance |
541 | +var Provider environs.EnvironProvider = jp.GetProviderInstance() |
542 | |
543 | === modified file 'provider/joyent/instance.go' |
544 | --- provider/joyent/instance.go 2013-12-16 09:53:26 +0000 |
545 | +++ provider/joyent/instance.go 2014-02-27 18:08:22 +0000 |
546 | @@ -10,7 +10,7 @@ |
547 | |
548 | type environInstance struct { |
549 | id instance.Id |
550 | - env *environ |
551 | + env *JoyentEnviron |
552 | } |
553 | |
554 | var _ instance.Instance = (*environInstance)(nil) |
555 | |
556 | === modified file 'provider/joyent/joyent_test.go' |
557 | --- provider/joyent/joyent_test.go 2013-10-10 09:24:18 +0000 |
558 | +++ provider/joyent/joyent_test.go 2014-02-27 18:08:22 +0000 |
559 | @@ -4,24 +4,177 @@ |
560 | package joyent_test |
561 | |
562 | import ( |
563 | - "testing" |
564 | + "fmt" |
565 | + "io/ioutil" |
566 | + "net/http" |
567 | + "net/http/httptest" |
568 | + "os" |
569 | + stdtesting "testing" |
570 | |
571 | gc "launchpad.net/gocheck" |
572 | |
573 | - "launchpad.net/juju-core/environs" |
574 | - "launchpad.net/juju-core/provider/joyent" |
575 | -) |
576 | - |
577 | -func TestPackage(t *testing.T) { |
578 | + "launchpad.net/juju-core/environs/config" |
579 | + envtesting "launchpad.net/juju-core/environs/testing" |
580 | + jp "launchpad.net/juju-core/provider/joyent" |
581 | + coretesting "launchpad.net/juju-core/testing" |
582 | + "launchpad.net/juju-core/testing/testbase" |
583 | + |
584 | + "launchpad.net/gojoyent/jpc" |
585 | + localmanta "launchpad.net/gojoyent/localservices/manta" |
586 | +) |
587 | + |
588 | +const ( |
589 | + testUser = "test" |
590 | + testKeyFileName = "provider_id_rsa" |
591 | + testPrivateKey = `-----BEGIN RSA PRIVATE KEY----- |
592 | +MIIEpAIBAAKCAQEAza+KvczCrcpQGRq9e347VHx9oEvuhseJt0ydR+UMAveyQprU |
593 | +4JHvzwUUhGnG147GJQYyfQ4nzaSG62az/YThoZJzw8gtxGkVHv0wlAlRkYhxbKbq |
594 | +8WQIh73xDQkHLw2lXLvf7Tt0Mhow0qGEmkOjTb5fPsj2evphrV3jJ15QlhL4cv33 |
595 | +t8jVadIrL0iIpwdqWiPqUKpsrSfKJghkoXS6quPy78P820TnuoBG+/Ppr8Kkvn6m |
596 | +A7j4xnOQ12QE6jPK4zkikj5ZczSC4fTG0d3BwwX4VYu+4y/T/BX0L9VNUmQU22Y+ |
597 | +/MRXAUZxsa8VhNB+xXF5XSubyb2n6loMWWaYGwIDAQABAoIBAQDCJt9JxYxGS+BL |
598 | +sigF9+O9Hj3fH42p/5QJR/J2uMgbzP+hS1GCIX9B5MO3MbmWI5j5vd3OmZwMyy7n |
599 | +6Wwg9FufDgTkW4KIEcD0HX7LXfh27VpTe0PuU8SRjUOKUGlNiw36eQUog6Rs3rgT |
600 | +Oo9Wpl3xtq9lLoErGEk3QpZ2xNpArTfsN9N3pdmD4sv7wmJq0PZQyej482g9R0g/ |
601 | +5k2ni6JpcEifzBQ6Bzx3EV2l9UipEIqbqDpMOtYFCpnLQhEaDfUribqXINGIsjiq |
602 | +VyFa3Mbg/eayqG3UX3rVTCif2NnW2ojl4mMgWCyxgWfb4Jg1trc3v7X4SXfbgPWD |
603 | +WcfrOhOhAoGBAP7ZC8KHAnjujwvXf3PxVNm6CTs5evbByQVyxNciGxRuOomJIR4D |
604 | +euqepQ4PuNAabnrbMyQWXpibIKpmLnBVoj1q0IMXYvi2MZF5e2tH/Gx01UvxamHh |
605 | +bKhHmp9ImHhVl6kObXOdNvLVTt/BI5FZBblvm7qOoiVwImPbqqVHP7Q5AoGBAM6d |
606 | +mNsrW0iV/nP1m7d8mcFw74PI0FNlNdfUoePUgokO0t5OU0Ri/lPBDCRGlvVF3pj1 |
607 | +HnmwroNtdWr9oPVB6km8193fb2zIWe53tj+6yRFQpz5elrSPfeZaZXlJZAGCCCdN |
608 | +gBggWQFPeQiT54aPywPpcTZHIs72XBqQ6QsIPrbzAoGAdW2hg5MeSobyFuzHZ69N |
609 | +/70/P7DuvgDxFbeah97JR5K7GmC7h87mtnE/cMlByXJEcgvK9tfv4rWoSZwnzc9H |
610 | +oLE1PxJpolyhXnzxp69V2svC9OlasZtjq+7Cip6y0s/twBJL0Lgid6ZeX6/pKbIx |
611 | +dw68XSwX/tQ6pHS1ns7DxdECgYBJbBWapNCefbbbjEcWsC+PX0uuABmP2SKGHSie |
612 | +ZrEwdVUX7KuIXMlWB/8BkRgp9vdAUbLPuap6R9Z2+8RMA213YKUxUiotdREIPgBE |
613 | +q2KyRX/5GPHjHi62Qh9XN25TXtr45ICFklEutwgitTSMS+Lv8+/oQuUquL9ILYCz |
614 | +C+4FYwKBgQDE9yZTUpJjG2424z6bl/MHzwl5RB4pMronp0BbeVqPwhCBfj0W5I42 |
615 | +1ZL4+8eniHfUs4GXzf5tb9YwVt3EltIF2JybaBvFsv2o356yJUQmqQ+jyYRoEpT5 |
616 | +2SwilFo/XCotCXxi5n8sm9V94a0oix4ehZrohTA/FZLsggwFCPmXfw== |
617 | +-----END RSA PRIVATE KEY-----` |
618 | + testKeyFingerprint = "66:ca:1c:09:75:99:35:69:be:91:08:25:03:c0:17:c0" |
619 | +) |
620 | + |
621 | +func TestJoyentProvider(t *stdtesting.T) { |
622 | gc.TestingT(t) |
623 | } |
624 | |
625 | -type JoyentSuite struct{} |
626 | - |
627 | -var _ = gc.Suite(&JoyentSuite{}) |
628 | - |
629 | -func (*JoyentSuite) TestRegistered(c *gc.C) { |
630 | - provider, err := environs.Provider("joyent") |
631 | - c.Assert(err, gc.IsNil) |
632 | - c.Assert(provider, gc.Equals, joyent.Provider) |
633 | +type localMantaService struct { |
634 | + creds *jpc.Credentials |
635 | + Server *httptest.Server |
636 | + Mux *http.ServeMux |
637 | + oldHandler http.Handler |
638 | + manta *localmanta.Manta |
639 | +} |
640 | + |
641 | +func (s *localMantaService) Start(c *gc.C) { |
642 | + // Set up the HTTP server. |
643 | + s.Server = httptest.NewServer(nil) |
644 | + c.Assert(s.Server, gc.NotNil) |
645 | + s.oldHandler = s.Server.Config.Handler |
646 | + s.Mux = http.NewServeMux() |
647 | + s.Server.Config.Handler = s.Mux |
648 | + |
649 | + // Set up a Joyent Manta service. |
650 | + auth := jpc.Auth{User: testUser, KeyFile: testKeyFileName, Algorithm: "rsa-sha256"} |
651 | + |
652 | + s.creds = &jpc.Credentials{ |
653 | + UserAuthentication: auth, |
654 | + MantaKeyId: testKeyFingerprint, |
655 | + MantaEndpoint: jpc.Endpoint{URL: s.Server.URL}, |
656 | + } |
657 | + s.manta = localmanta.New(s.creds.MantaEndpoint.URL, s.creds.UserAuthentication.User) |
658 | + s.manta.SetupHTTP(s.Mux) |
659 | + c.Logf("Started local Manta service at: %v", s.Server.URL) |
660 | +} |
661 | + |
662 | +func (s *localMantaService) Stop() { |
663 | + s.Mux = nil |
664 | + s.Server.Config.Handler = s.oldHandler |
665 | + s.Server.Close() |
666 | +} |
667 | + |
668 | +type providerSuite struct { |
669 | + testbase.LoggingSuite |
670 | + envtesting.ToolsFixture |
671 | + restoreTimeouts func() |
672 | +} |
673 | + |
674 | +var _ = gc.Suite(&providerSuite{}) |
675 | + |
676 | +func (s *providerSuite) SetUpSuite(c *gc.C) { |
677 | + s.restoreTimeouts = envtesting.PatchAttemptStrategies() |
678 | + s.LoggingSuite.SetUpSuite(c) |
679 | + createTestKey() |
680 | +} |
681 | + |
682 | +func (s *providerSuite) TearDownSuite(c *gc.C) { |
683 | + removeTestKey() |
684 | + s.restoreTimeouts() |
685 | + s.LoggingSuite.TearDownSuite(c) |
686 | +} |
687 | + |
688 | +func (s *providerSuite) SetUpTest(c *gc.C) { |
689 | + s.LoggingSuite.SetUpTest(c) |
690 | + s.ToolsFixture.SetUpTest(c) |
691 | +} |
692 | + |
693 | +func (s *providerSuite) TearDownTest(c *gc.C) { |
694 | + s.ToolsFixture.TearDownTest(c) |
695 | + s.LoggingSuite.TearDownTest(c) |
696 | +} |
697 | + |
698 | +func GetFakeConfig(sdcUrl, mantaUrl string) coretesting.Attrs { |
699 | + return coretesting.FakeConfig().Merge(coretesting.Attrs{ |
700 | + "name": "joyent test environment", |
701 | + "type": "joyent", |
702 | + "sdc-user": testUser, |
703 | + "sdc-key-id": testKeyFingerprint, |
704 | + "sdc-url": sdcUrl, |
705 | + "manta-user": testUser, |
706 | + "manta-key-id": testKeyFingerprint, |
707 | + "manta-url": mantaUrl, |
708 | + "key-file": fmt.Sprintf("%s/.ssh/%s", os.Getenv("HOME"), testKeyFileName), |
709 | + "algorithm": "rsa-sha256", |
710 | + "control-dir": "juju-test", |
711 | + }) |
712 | +} |
713 | + |
714 | +// makeEnviron creates a functional Joyent environ for a test. |
715 | +func (suite *providerSuite) makeEnviron(sdcUrl, mantaUrl string) *jp.JoyentEnviron { |
716 | + /*attrs := coretesting.FakeConfig().Merge(coretesting.Attrs{ |
717 | + "name": "joyent test environment", |
718 | + "type": "joyent", |
719 | + "sdc-user": "dstroppa", |
720 | + "sdc-key-id": "12:c3:a7:cb:a2:29:e2:90:88:3f:04:53:3b:4e:75:40", |
721 | + "sdc-url": "https://us-west-1.api.joyentcloud.com", |
722 | + "manta-user": "dstroppa", |
723 | + "manta-key-id": "12:c3:a7:cb:a2:29:e2:90:88:3f:04:53:3b:4e:75:40", |
724 | + "manta-url": "https://us-east.manta.joyent.com", |
725 | + "key-file": fmt.Sprintf("%s/.ssh/id_rsa", os.Getenv("HOME")), |
726 | + "algorithm": "rsa-sha256", |
727 | + "control-dir": "juju-test", |
728 | + })*/ |
729 | + |
730 | + attrs := GetFakeConfig(sdcUrl, mantaUrl) |
731 | + cfg, err := config.New(config.NoDefaults, attrs) |
732 | + if err != nil { |
733 | + panic(err) |
734 | + } |
735 | + env, err := jp.NewEnviron(cfg) |
736 | + if err != nil { |
737 | + panic(err) |
738 | + } |
739 | + return env |
740 | +} |
741 | + |
742 | +func createTestKey() error { |
743 | + keyFile := fmt.Sprintf("%s/.ssh/%s", os.Getenv("HOME"), testKeyFileName) |
744 | + return ioutil.WriteFile(keyFile, []byte(testPrivateKey), 400) |
745 | +} |
746 | + |
747 | +func removeTestKey() error { |
748 | + keyFile := fmt.Sprintf("%s/.ssh/%s", os.Getenv("HOME"), testKeyFileName) |
749 | + return os.Remove(keyFile) |
750 | } |
751 | |
752 | === modified file 'provider/joyent/provider.go' |
753 | --- provider/joyent/provider.go 2014-02-13 02:46:58 +0000 |
754 | +++ provider/joyent/provider.go 2014-02-27 18:08:22 +0000 |
755 | @@ -45,7 +45,7 @@ |
756 | } |
757 | |
758 | func (environProvider) Open(cfg *config.Config) (environs.Environ, error) { |
759 | - env := &environ{name: cfg.Name()} |
760 | + env := &JoyentEnviron{name: cfg.Name()} |
761 | if err := env.SetConfig(cfg); err != nil { |
762 | return nil, err |
763 | } |
764 | @@ -118,3 +118,7 @@ |
765 | // provider; and it needs to return the address of *that* instance. |
766 | return "", errNotImplemented |
767 | } |
768 | + |
769 | +func GetProviderInstance() environs.EnvironProvider { |
770 | + return providerInstance |
771 | +} |
772 | |
773 | === modified file 'provider/joyent/storage.go' |
774 | --- provider/joyent/storage.go 2013-11-21 17:37:18 +0000 |
775 | +++ provider/joyent/storage.go 2014-02-27 18:08:22 +0000 |
776 | @@ -4,50 +4,194 @@ |
777 | package joyent |
778 | |
779 | import ( |
780 | + "bytes" |
781 | + "fmt" |
782 | "io" |
783 | + "sync" |
784 | + "time" |
785 | |
786 | "launchpad.net/juju-core/environs/storage" |
787 | "launchpad.net/juju-core/utils" |
788 | + |
789 | + "launchpad.net/gojoyent/client" |
790 | + "launchpad.net/gojoyent/jpc" |
791 | + "launchpad.net/gojoyent/manta" |
792 | ) |
793 | |
794 | -type environStorage struct { |
795 | - ecfg *environConfig |
796 | -} |
797 | - |
798 | -var _ storage.Storage = (*environStorage)(nil) |
799 | +type JoyentStorage struct { |
800 | + sync.Mutex |
801 | + ecfg *environConfig |
802 | + madeContainer bool |
803 | + containerName string |
804 | + manta *manta.Client |
805 | +} |
806 | + |
807 | +type byteCloser struct { |
808 | + io.Reader |
809 | +} |
810 | + |
811 | +func (byteCloser) Close() error { |
812 | + return nil |
813 | +} |
814 | + |
815 | +var _ storage.Storage = (*JoyentStorage)(nil) |
816 | + |
817 | +func NewStorage(env *JoyentEnviron) storage.Storage { |
818 | + if stor, err := newStorage(env.ecfg); err == nil { |
819 | + return stor |
820 | + } |
821 | + return nil |
822 | +} |
823 | + |
824 | +func getCredentials(ecfg *environConfig) *jpc.Credentials { |
825 | + auth := jpc.Auth{User: ecfg.mantaUser(), KeyFile: ecfg.keyFile(), Algorithm: ecfg.algorithm()} |
826 | + |
827 | + return &jpc.Credentials{ |
828 | + UserAuthentication: auth, |
829 | + MantaKeyId: ecfg.mantaKeyId(), |
830 | + MantaEndpoint: jpc.Endpoint{URL: ecfg.mantaUrl()}, |
831 | + } |
832 | +} |
833 | |
834 | func newStorage(ecfg *environConfig) (storage.Storage, error) { |
835 | - return &environStorage{ecfg}, nil |
836 | -} |
837 | - |
838 | -func (s *environStorage) List(prefix string) ([]string, error) { |
839 | - return nil, errNotImplemented |
840 | -} |
841 | - |
842 | -func (s *environStorage) URL(name string) (string, error) { |
843 | - return "", errNotImplemented |
844 | -} |
845 | - |
846 | -func (s *environStorage) Get(name string) (io.ReadCloser, error) { |
847 | - return nil, errNotImplemented |
848 | -} |
849 | - |
850 | -func (s *environStorage) Put(name string, r io.Reader, length int64) error { |
851 | - return errNotImplemented |
852 | -} |
853 | - |
854 | -func (s *environStorage) Remove(name string) error { |
855 | - return errNotImplemented |
856 | -} |
857 | - |
858 | -func (s *environStorage) RemoveAll() error { |
859 | - return errNotImplemented |
860 | -} |
861 | - |
862 | -func (s *environStorage) DefaultConsistencyStrategy() utils.AttemptStrategy { |
863 | + client := client.NewClient(ecfg.mantaUrl(), "", getCredentials(ecfg), &logger) |
864 | + |
865 | + return &JoyentStorage{ |
866 | + ecfg: ecfg, |
867 | + containerName: ecfg.controlDir(), |
868 | + manta: manta.New(client)}, nil |
869 | +} |
870 | + |
871 | +func (s *JoyentStorage) GetContainerName() string { |
872 | + return s.containerName |
873 | +} |
874 | + |
875 | +func (s *JoyentStorage) GetMantaUrl() string { |
876 | + return s.ecfg.mantaUrl() |
877 | +} |
878 | + |
879 | +func (s *JoyentStorage) GetMantaUser() string { |
880 | + return s.ecfg.mantaUser() |
881 | +} |
882 | + |
883 | +// CreateContainer makes the environment's control container, the |
884 | +// place where bootstrap information and deployed charms |
885 | +// are stored. To avoid two round trips on every PUT operation, |
886 | +// we do this only once for each environ. |
887 | +func (s *JoyentStorage) CreateContainer() error { |
888 | + s.Lock() |
889 | + defer s.Unlock() |
890 | + if s.madeContainer { |
891 | + return nil |
892 | + } |
893 | + // try to make the container |
894 | + err := s.manta.PutDirectory(s.containerName) |
895 | + if err == nil { |
896 | + s.madeContainer = true |
897 | + } |
898 | + return err |
899 | +} |
900 | + |
901 | +// deleteContainer deletes the named container from the storage account. |
902 | +func (s *JoyentStorage) DeleteContainer(containerName string) error { |
903 | + err := s.manta.DeleteDirectory(containerName) |
904 | + if err == nil { |
905 | + s.madeContainer = false |
906 | + } |
907 | + return err |
908 | +} |
909 | + |
910 | +func (s *JoyentStorage) List(prefix string) ([]string, error) { |
911 | + // use empty opts, i.e. default values |
912 | + // -- might be added in the provider config? |
913 | + contents, err := s.manta.ListDirectory(s.containerName, manta.ListDirectoryOpts{}) |
914 | + if err != nil { |
915 | + return nil, err |
916 | + } |
917 | + var names []string |
918 | + for _, item := range contents { |
919 | + names = append(names, item.Name) |
920 | + } |
921 | + return names, nil |
922 | +} |
923 | + |
924 | +//return something that a random wget can retrieve the object at, without any credentials |
925 | +func (s *JoyentStorage) URL(name string) (string, error) { |
926 | + path := fmt.Sprintf("/%s/stor/%s/%s", s.ecfg.mantaUser(), s.containerName, name) |
927 | + return s.manta.SignURL(path, time.Now().AddDate(10, 0, 0)) |
928 | +} |
929 | + |
930 | +func (s *JoyentStorage) Get(name string) (io.ReadCloser, error) { |
931 | + b, err := s.manta.GetObject(s.containerName, name) |
932 | + if err != nil { |
933 | + return nil, err |
934 | + } |
935 | + r := byteCloser{bytes.NewReader(b)} |
936 | + return r, nil |
937 | +} |
938 | + |
939 | +func (s *JoyentStorage) Put(name string, r io.Reader, length int64) error { |
940 | + if err := s.CreateContainer(); err != nil { |
941 | + return fmt.Errorf("cannot make Manta control container: %v", err) |
942 | + } |
943 | + err := s.manta.PutObject(s.containerName, name, r) |
944 | + if err != nil { |
945 | + return fmt.Errorf("cannot write file %q to control container %q: %v", name, s.containerName, err) |
946 | + } |
947 | + return nil |
948 | +} |
949 | + |
950 | +func (s *JoyentStorage) Remove(name string) error { |
951 | + err := s.manta.DeleteObject(s.containerName, name) |
952 | + if err != nil { |
953 | + return err |
954 | + } |
955 | + return nil |
956 | +} |
957 | + |
958 | +func (s *JoyentStorage) RemoveAll() error { |
959 | + names, err := storage.List(s, "") |
960 | + if err != nil { |
961 | + return err |
962 | + } |
963 | + // Remove all the objects in parallel so that we incur less round-trips. |
964 | + // If we're in danger of having hundreds of objects, |
965 | + // we'll want to change this to limit the number |
966 | + // of concurrent operations. |
967 | + var wg sync.WaitGroup |
968 | + wg.Add(len(names)) |
969 | + errc := make(chan error, len(names)) |
970 | + for _, name := range names { |
971 | + name := name |
972 | + go func() { |
973 | + defer wg.Done() |
974 | + if err := s.Remove(name); err != nil { |
975 | + errc <- err |
976 | + } |
977 | + }() |
978 | + } |
979 | + wg.Wait() |
980 | + select { |
981 | + case err := <-errc: |
982 | + return fmt.Errorf("cannot delete all provider state: %v", err) |
983 | + default: |
984 | + } |
985 | + |
986 | + s.Lock() |
987 | + defer s.Unlock() |
988 | + // Even DeleteContainer fails, it won't harm if we try again - the |
989 | + // operation might have succeeded even if we get an error. |
990 | + s.madeContainer = false |
991 | + if err = s.manta.DeleteDirectory(s.containerName); err != nil { |
992 | + return err |
993 | + } |
994 | + return nil |
995 | +} |
996 | + |
997 | +func (s *JoyentStorage) DefaultConsistencyStrategy() utils.AttemptStrategy { |
998 | return utils.AttemptStrategy{} |
999 | } |
1000 | |
1001 | -func (s *environStorage) ShouldRetry(err error) bool { |
1002 | +func (s *JoyentStorage) ShouldRetry(err error) bool { |
1003 | return false |
1004 | } |
1005 | |
1006 | === added file 'provider/joyent/storage_test.go' |
1007 | --- provider/joyent/storage_test.go 1970-01-01 00:00:00 +0000 |
1008 | +++ provider/joyent/storage_test.go 2014-02-27 18:08:22 +0000 |
1009 | @@ -0,0 +1,189 @@ |
1010 | +// Copyright 2013 Joyent Inc. |
1011 | +// Licensed under the AGPLv3, see LICENCE file for details. |
1012 | + |
1013 | +package joyent_test |
1014 | + |
1015 | +import ( |
1016 | + "fmt" |
1017 | + "io/ioutil" |
1018 | + "math/rand" |
1019 | + "net/http" |
1020 | + "net/url" |
1021 | + "strings" |
1022 | + |
1023 | + gc "launchpad.net/gocheck" |
1024 | + jp "launchpad.net/juju-core/provider/joyent" |
1025 | + jc "launchpad.net/juju-core/testing/checkers" |
1026 | + |
1027 | + "launchpad.net/gojoyent/errors" |
1028 | +) |
1029 | + |
1030 | +type storageSuite struct { |
1031 | + providerSuite |
1032 | + localMantaService |
1033 | +} |
1034 | + |
1035 | +const ( |
1036 | + storageName = "testStorage" |
1037 | + fileName = "testFile" |
1038 | + fileBlobContent = "Juju Joyent Provider Storage - Test" |
1039 | +) |
1040 | + |
1041 | +var _ = gc.Suite(&storageSuite{}) |
1042 | + |
1043 | +func (s *storageSuite) SetUpSuite(c *gc.C) { |
1044 | + s.providerSuite.SetUpSuite(c) |
1045 | + s.localMantaService.Start(c) |
1046 | +} |
1047 | + |
1048 | +func (s *storageSuite) TearDownSuite(c *gc.C) { |
1049 | + s.localMantaService.Stop() |
1050 | + s.providerSuite.TearDownSuite(c) |
1051 | +} |
1052 | + |
1053 | +// s.makeStorage creates a Manta storage object for the running test. |
1054 | +func (s *storageSuite) assertStorage(name string, c *gc.C) *jp.JoyentStorage { |
1055 | + env := s.makeEnviron("localhost", s.localMantaService.Server.URL) |
1056 | + env.SetName(name) |
1057 | + storage := jp.NewStorage(env).(*jp.JoyentStorage) |
1058 | + c.Assert(storage, gc.NotNil) |
1059 | + return storage |
1060 | +} |
1061 | + |
1062 | +func (s *storageSuite) assertContainer(storage *jp.JoyentStorage, c *gc.C) { |
1063 | + err := storage.CreateContainer() |
1064 | + c.Assert(err, gc.IsNil) |
1065 | +} |
1066 | + |
1067 | +func (s *storageSuite) assertFile(storage *jp.JoyentStorage, c *gc.C) { |
1068 | + err := storage.Put(fileName, strings.NewReader(fileBlobContent), int64(len(fileBlobContent))) |
1069 | + c.Assert(err, gc.IsNil) |
1070 | +} |
1071 | + |
1072 | +// makeRandomBytes returns an array of arbitrary byte values. |
1073 | +func makeRandomBytes(length int) []byte { |
1074 | + data := make([]byte, length) |
1075 | + for index := range data { |
1076 | + data[index] = byte(rand.Intn(256)) |
1077 | + } |
1078 | + return data |
1079 | +} |
1080 | + |
1081 | +func makeResponse(content string, status int) *http.Response { |
1082 | + return &http.Response{ |
1083 | + Status: fmt.Sprintf("%d", status), |
1084 | + StatusCode: status, |
1085 | + Body: ioutil.NopCloser(strings.NewReader(content)), |
1086 | + } |
1087 | +} |
1088 | + |
1089 | +func (s *storageSuite) TestList(c *gc.C) { |
1090 | + mantaStorage := s.assertStorage(storageName, c) |
1091 | + s.assertContainer(mantaStorage, c) |
1092 | + s.assertFile(mantaStorage, c) |
1093 | + |
1094 | + names, err := mantaStorage.List("prefix") |
1095 | + c.Assert(err, gc.IsNil) |
1096 | + c.Check(names, gc.DeepEquals, []string{fileName}) |
1097 | +} |
1098 | + |
1099 | +func (s *storageSuite) TestGet(c *gc.C) { |
1100 | + mantaStorage := s.assertStorage(storageName, c) |
1101 | + s.assertFile(mantaStorage, c) |
1102 | + |
1103 | + reader, err := mantaStorage.Get(fileName) |
1104 | + c.Assert(err, gc.IsNil) |
1105 | + c.Assert(reader, gc.NotNil) |
1106 | + defer reader.Close() |
1107 | + |
1108 | + data, err := ioutil.ReadAll(reader) |
1109 | + c.Assert(err, gc.IsNil) |
1110 | + c.Check(string(data), gc.Equals, fileBlobContent) |
1111 | +} |
1112 | + |
1113 | +func (s *storageSuite) TestGetFileNotExists(c *gc.C) { |
1114 | + mantaStorage := s.assertStorage(storageName, c) |
1115 | + |
1116 | + _, err := mantaStorage.Get("noFile") |
1117 | + c.Assert(err, gc.NotNil) |
1118 | + c.Assert(err, jc.Satisfies, errors.IsResourceNotFound) |
1119 | +} |
1120 | + |
1121 | +func (s *storageSuite) TestPut(c *gc.C) { |
1122 | + mantaStorage := s.assertStorage(storageName, c) |
1123 | + |
1124 | + s.assertFile(mantaStorage, c) |
1125 | +} |
1126 | + |
1127 | +func (s *storageSuite) TestRemove(c *gc.C) { |
1128 | + mantaStorage := s.assertStorage(storageName, c) |
1129 | + s.assertFile(mantaStorage, c) |
1130 | + |
1131 | + err := mantaStorage.Remove(fileName) |
1132 | + c.Assert(err, gc.IsNil) |
1133 | +} |
1134 | + |
1135 | +func (s *storageSuite) TestRemoveFileNotExists(c *gc.C) { |
1136 | + mantaStorage := s.assertStorage(storageName, c) |
1137 | + |
1138 | + err := mantaStorage.Remove("nofile") |
1139 | + c.Assert(err, gc.NotNil) |
1140 | + c.Assert(err, jc.Satisfies, errors.IsResourceNotFound) |
1141 | +} |
1142 | + |
1143 | +func (s *storageSuite) TestRemoveAll(c *gc.C) { |
1144 | + mantaStorage := s.assertStorage(storageName, c) |
1145 | + |
1146 | + err := mantaStorage.RemoveAll() |
1147 | + c.Assert(err, gc.IsNil) |
1148 | +} |
1149 | + |
1150 | +func (s *storageSuite) TestURL(c *gc.C) { |
1151 | + mantaStorage := s.assertStorage(storageName, c) |
1152 | + |
1153 | + URL, err := mantaStorage.URL(fileName) |
1154 | + c.Assert(err, gc.IsNil) |
1155 | + parsedURL, err := url.Parse(URL) |
1156 | + c.Assert(err, gc.IsNil) |
1157 | + c.Check(parsedURL.Host, gc.Matches, mantaStorage.GetMantaUrl()[strings.LastIndex(mantaStorage.GetMantaUrl(), "/")+1:]) |
1158 | + c.Check(parsedURL.Path, gc.Matches, fmt.Sprintf("/%s/stor/%s/%s", mantaStorage.GetMantaUser(), mantaStorage.GetContainerName(), fileName)) |
1159 | +} |
1160 | + |
1161 | +func (s *storageSuite) TestCreateContainer(c *gc.C) { |
1162 | + mantaStorage := s.assertStorage(storageName, c) |
1163 | + |
1164 | + s.assertContainer(mantaStorage, c) |
1165 | +} |
1166 | + |
1167 | +func (s *storageSuite) TestCreateContainerAlreadyExists(c *gc.C) { |
1168 | + mantaStorage := s.assertStorage(storageName, c) |
1169 | + |
1170 | + s.assertContainer(mantaStorage, c) |
1171 | + s.assertContainer(mantaStorage, c) |
1172 | +} |
1173 | + |
1174 | +func (s *storageSuite) TestDeleteContainer(c *gc.C) { |
1175 | + mantaStorage := s.assertStorage(storageName, c) |
1176 | + s.assertContainer(mantaStorage, c) |
1177 | + |
1178 | + err := mantaStorage.DeleteContainer(mantaStorage.GetContainerName()) |
1179 | + c.Assert(err, gc.IsNil) |
1180 | +} |
1181 | + |
1182 | +func (s *storageSuite) TestDeleteContainerNotEmpty(c *gc.C) { |
1183 | + mantaStorage := s.assertStorage(storageName, c) |
1184 | + s.assertContainer(mantaStorage, c) |
1185 | + s.assertFile(mantaStorage, c) |
1186 | + |
1187 | + err := mantaStorage.DeleteContainer(mantaStorage.GetContainerName()) |
1188 | + c.Assert(err, gc.NotNil) |
1189 | + c.Assert(err, jc.Satisfies, errors.IsBadRequest) |
1190 | +} |
1191 | + |
1192 | +func (s *storageSuite) TestDeleteContainerNotExists(c *gc.C) { |
1193 | + mantaStorage := s.assertStorage(storageName, c) |
1194 | + |
1195 | + err := mantaStorage.DeleteContainer("noContainer") |
1196 | + c.Assert(err, gc.NotNil) |
1197 | + c.Assert(err, jc.Satisfies, errors.IsResourceNotFound) |
1198 | +} |
Reviewers: mp+200851_ code.launchpad. net,
Message:
Please take a look.
Description:
Joyent Provider - Storage impl
https:/ /code.launchpad .net/~dstroppa/ juju-core/ joyent- provider- storage/ +merge/ 200851
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/49050044/
Affected files (+566, -151 lines): joyent/ config. go joyent/ config_ test.go joyent/ environ. go joyent/ environ_ firewall. go joyent/ environ_ instance. go joyent/ instance. go joyent/ joyent_ test.go joyent/ provider. go joyent/ storage. go joyent/ storage_ test.go
M .bzrignore
A [revision details]
M dependencies.tsv
M provider/
M provider/
M provider/
M provider/
M provider/
M provider/
M provider/
M provider/
M provider/
A provider/