Merge lp:~wallyworld/juju-core/improve-image-metadata-command into lp:~go-bot/juju-core/trunk

Proposed by Ian Booth
Status: Merged
Approved by: Ian Booth
Approved revision: no longer in the source branch.
Merged at revision: 1987
Proposed branch: lp:~wallyworld/juju-core/improve-image-metadata-command
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 750 lines (+262/-148)
5 files modified
cmd/plugins/juju-metadata/imagemetadata.go (+99/-25)
cmd/plugins/juju-metadata/imagemetadata_test.go (+127/-66)
cmd/plugins/juju-metadata/validateimagemetadata_test.go (+22/-18)
environs/imagemetadata/generate.go (+9/-25)
environs/imagemetadata/validation_test.go (+5/-14)
To merge this branch: bzr merge lp:~wallyworld/juju-core/improve-image-metadata-command
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+190517@code.launchpad.net

Commit message

Improve the image metadata generation command

juju metadata generate-image was originally written
as a developer tool to aid simplestreams development.
It now is useful for setting up private clouds so has
been given a facelift to make it more user friendly.
The key feature is it will now use the current environment
to pick up most of what it needs, and the user just has to
supply the image id and architecture (if something different
to amd64 is required).

https://codereview.appspot.com/14502059/

Description of the change

Improve the image metadata generation command

juju metadata generate-image was originally written
as a developer tool to aid simplestreams development.
It now is useful for setting up private clouds so has
been given a facelift to make it more user friendly.
The key feature is it will now use the current environment
to pick up most of what it needs, and the user just has to
supply the image id and architecture (if something different
to amd64 is required).

https://codereview.appspot.com/14502059/

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

Reviewers: mp+190517_code.launchpad.net,

Message:
Please take a look.

Description:
Improve the image metadata generation command

juju metadata generate-image was originally written
as a developer tool to aid simplestreams development.
It now is useful for setting up private clouds so has
been given a facelift to make it more user friendly.
The key feature is it will now use the current environment
to pick up most of what it needs, and the user just has to
supply the image id and architecture (if something different
to amd64 is required).

https://code.launchpad.net/~wallyworld/juju-core/improve-image-metadata-command/+merge/190517

(do not edit description out of merge proposal)

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

Affected files (+202, -133 lines):
   A [revision details]
   M cmd/plugins/juju-metadata/imagemetadata.go
   M cmd/plugins/juju-metadata/imagemetadata_test.go
   M cmd/plugins/juju-metadata/validateimagemetadata_test.go
   M environs/imagemetadata/generate.go

Revision history for this message
Andrew Wilkins (axwalk) wrote :

https://codereview.appspot.com/14502059/diff/1/cmd/plugins/juju-metadata/imagemetadata.go
File cmd/plugins/juju-metadata/imagemetadata.go (right):

https://codereview.appspot.com/14502059/diff/1/cmd/plugins/juju-metadata/imagemetadata.go#newcode72
cmd/plugins/juju-metadata/imagemetadata.go:72: return
fmt.Errorf("environment %q cannot provide region and endpoint",
environ.Name())
What if the user has specified -s, -r, and -e on the command line? Is
this
really an error then?

Just a thought: perhaps ImageMetadataCommand could implement HasRegion.
If environ doesn't implement HasRegion, fall back to
ImageMetadataCommand.

https://codereview.appspot.com/14502059/diff/1/cmd/plugins/juju-metadata/imagemetadata_test.go
File cmd/plugins/juju-metadata/imagemetadata_test.go (right):

https://codereview.appspot.com/14502059/diff/1/cmd/plugins/juju-metadata/imagemetadata_test.go#newcode20
cmd/plugins/juju-metadata/imagemetadata_test.go:20: "path/filepath"
Move me please

https://codereview.appspot.com/14502059/diff/1/cmd/plugins/juju-metadata/imagemetadata_test.go#newcode42
cmd/plugins/juju-metadata/imagemetadata_test.go:42: restore :=
testbase.PatchEnvironment("AWS_ACCESS_KEY_ID", "access")
You can just use s.PatchEnvironment here, which wraps
testbase.PatchEnvironment & AddCleanup.

https://codereview.appspot.com/14502059/

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

Please take a look.

https://codereview.appspot.com/14502059/diff/1/cmd/plugins/juju-metadata/imagemetadata.go
File cmd/plugins/juju-metadata/imagemetadata.go (right):

https://codereview.appspot.com/14502059/diff/1/cmd/plugins/juju-metadata/imagemetadata.go#newcode72
cmd/plugins/juju-metadata/imagemetadata.go:72: return
fmt.Errorf("environment %q cannot provide region and endpoint",
environ.Name())
On 2013/10/15 07:08:34, axw wrote:
> What if the user has specified -s, -r, and -e on the command line? Is
this
> really an error then?

Hmmm. I thought yes when I wrote this. But I changed it so that it works
more as expected.

> Just a thought: perhaps ImageMetadataCommand could implement
HasRegion. If
> environ doesn't implement HasRegion, fall back to
ImageMetadataCommand.

See my changes - I think it's ok as is.

https://codereview.appspot.com/14502059/diff/1/cmd/plugins/juju-metadata/imagemetadata_test.go
File cmd/plugins/juju-metadata/imagemetadata_test.go (right):

https://codereview.appspot.com/14502059/diff/1/cmd/plugins/juju-metadata/imagemetadata_test.go#newcode20
cmd/plugins/juju-metadata/imagemetadata_test.go:20: "path/filepath"
On 2013/10/15 07:08:34, axw wrote:
> Move me please

Done.

https://codereview.appspot.com/14502059/diff/1/cmd/plugins/juju-metadata/imagemetadata_test.go#newcode42
cmd/plugins/juju-metadata/imagemetadata_test.go:42: restore :=
testbase.PatchEnvironment("AWS_ACCESS_KEY_ID", "access")
On 2013/10/15 07:08:34, axw wrote:
> You can just use s.PatchEnvironment here, which wraps
testbase.PatchEnvironment
> & AddCleanup.

Done.

https://codereview.appspot.com/14502059/

Revision history for this message
Andrew Wilkins (axwalk) wrote :

On 2013/10/16 02:57:49, wallyworld wrote:
> Please take a look.

https://codereview.appspot.com/14502059/diff/1/cmd/plugins/juju-metadata/imagemetadata.go
> File cmd/plugins/juju-metadata/imagemetadata.go (right):

https://codereview.appspot.com/14502059/diff/1/cmd/plugins/juju-metadata/imagemetadata.go#newcode72
> cmd/plugins/juju-metadata/imagemetadata.go:72: return
fmt.Errorf("environment %q
> cannot provide region and endpoint", environ.Name())
> On 2013/10/15 07:08:34, axw wrote:
> > What if the user has specified -s, -r, and -e on the command line?
Is this
> > really an error then?
> >

> Hmmm. I thought yes when I wrote this. But I changed it so that it
works more as
> expected.

> > Just a thought: perhaps ImageMetadataCommand could implement
HasRegion. If
> > environ doesn't implement HasRegion, fall back to
ImageMetadataCommand.

> See my changes - I think it's ok as is.

https://codereview.appspot.com/14502059/diff/1/cmd/plugins/juju-metadata/imagemetadata_test.go
> File cmd/plugins/juju-metadata/imagemetadata_test.go (right):

https://codereview.appspot.com/14502059/diff/1/cmd/plugins/juju-metadata/imagemetadata_test.go#newcode20
> cmd/plugins/juju-metadata/imagemetadata_test.go:20: "path/filepath"
> On 2013/10/15 07:08:34, axw wrote:
> > Move me please

> Done.

https://codereview.appspot.com/14502059/diff/1/cmd/plugins/juju-metadata/imagemetadata_test.go#newcode42
> cmd/plugins/juju-metadata/imagemetadata_test.go:42: restore :=
> testbase.PatchEnvironment("AWS_ACCESS_KEY_ID", "access")
> On 2013/10/15 07:08:34, axw wrote:
> > You can just use s.PatchEnvironment here, which wraps
> testbase.PatchEnvironment
> > & AddCleanup.

> Done.

Thanks, LGTM

https://codereview.appspot.com/14502059/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cmd/plugins/juju-metadata/imagemetadata.go'
2--- cmd/plugins/juju-metadata/imagemetadata.go 2013-08-07 04:37:43 +0000
3+++ cmd/plugins/juju-metadata/imagemetadata.go 2013-10-16 02:57:31 +0000
4@@ -5,51 +5,102 @@
5
6 import (
7 "fmt"
8- "strings"
9+ "os"
10+ "path/filepath"
11
12 "launchpad.net/gnuflag"
13- "launchpad.net/goose/identity"
14
15 "launchpad.net/juju-core/cmd"
16+ "launchpad.net/juju-core/environs"
17 "launchpad.net/juju-core/environs/config"
18+ "launchpad.net/juju-core/environs/configstore"
19 "launchpad.net/juju-core/environs/imagemetadata"
20 "launchpad.net/juju-core/environs/simplestreams"
21 )
22
23-// ImageMetadataCommand is used to write out a boilerplate environments.yaml file.
24+// ImageMetadataCommand is used to write out simplestreams image metadata information.
25 type ImageMetadataCommand struct {
26- cmd.CommandBase
27- Name string
28- Series string
29- Arch string
30- ImageId string
31- Region string
32- Endpoint string
33+ cmd.EnvCommandBase
34+ Dir string
35+ Series string
36+ Arch string
37+ ImageId string
38+ Region string
39+ Endpoint string
40+ privateStorage string
41 }
42
43+var imageMetadataDoc = `
44+generate-image creates simplestreams image metadata for the specified cloud.
45+
46+The cloud specification comes from the current Juju environment, as specified in
47+the usual way from either ~/.juju/environments.yaml, the -e option, or JUJU_ENV.
48+
49+Using command arguments, it is possible to override cloud attributes region, endpoint, and series.
50+By default, "amd64" is used for the architecture but this may also be changed.
51+`
52+
53 func (c *ImageMetadataCommand) Info() *cmd.Info {
54 return &cmd.Info{
55 Name: "generate-image",
56 Purpose: "generate simplestreams image metadata",
57+ Doc: imageMetadataDoc,
58 }
59 }
60
61 func (c *ImageMetadataCommand) SetFlags(f *gnuflag.FlagSet) {
62- f.StringVar(&c.Series, "s", "precise", "the charm series")
63+ c.EnvCommandBase.SetFlags(f)
64+ f.StringVar(&c.Series, "s", "", "the charm series")
65 f.StringVar(&c.Arch, "a", "amd64", "the image achitecture")
66- f.StringVar(&c.Name, "n", "", "the cloud name, as a prefix for the generated file names")
67+ f.StringVar(&c.Dir, "d", "", "the destination directory in which to place the metadata files")
68 f.StringVar(&c.ImageId, "i", "", "the image id")
69 f.StringVar(&c.Region, "r", "", "the region")
70 f.StringVar(&c.Endpoint, "u", "", "the cloud endpoint (for Openstack, this is the Identity Service endpoint)")
71 }
72
73 func (c *ImageMetadataCommand) Init(args []string) error {
74- cred := identity.CredentialsFromEnv()
75- if c.Region == "" {
76- c.Region = cred.Region
77- }
78- if c.Endpoint == "" {
79- c.Endpoint = cred.URL
80+ c.privateStorage = "<private storage name>"
81+ var environ environs.Environ
82+ if store, err := configstore.Default(); err == nil {
83+ if environ, err = environs.PrepareFromName(c.EnvName, store); err == nil {
84+ logger.Infof("creating image metadata for environment %q", environ.Name())
85+ // If the user has not specified region and endpoint, try and get it from the environment.
86+ if c.Region == "" || c.Endpoint == "" {
87+ var cloudSpec simplestreams.CloudSpec
88+ if inst, ok := environ.(simplestreams.HasRegion); ok {
89+ if cloudSpec, err = inst.Region(); err != nil {
90+ return err
91+ }
92+ } else {
93+ return fmt.Errorf("environment %q cannot provide region and endpoint", environ.Name())
94+ }
95+ // If only one of region or endpoint is provided, that is a problem.
96+ if cloudSpec.Region != cloudSpec.Endpoint && (cloudSpec.Region == "" || cloudSpec.Endpoint == "") {
97+ return fmt.Errorf("cannot generate metadata without a complete cloud configuration")
98+ }
99+ if c.Region == "" {
100+ c.Region = cloudSpec.Region
101+ }
102+ if c.Endpoint == "" {
103+ c.Endpoint = cloudSpec.Endpoint
104+ }
105+ }
106+ cfg := environ.Config()
107+ if c.Series == "" {
108+ c.Series = cfg.DefaultSeries()
109+ }
110+ if v, ok := cfg.AllAttrs()["control-bucket"]; ok {
111+ c.privateStorage = v.(string)
112+ }
113+ } else {
114+ logger.Warningf("environment %q could not be opened: %v", c.EnvName, err)
115+ }
116+ }
117+ if environ == nil {
118+ logger.Infof("no environment found, creating image metadata using user supplied data")
119+ }
120+ if c.Series == "" {
121+ c.Series = config.DefaultSeries
122 }
123 if c.ImageId == "" {
124 return fmt.Errorf("image id must be specified")
125@@ -60,10 +111,36 @@
126 if c.Endpoint == "" {
127 return fmt.Errorf("cloud endpoint URL must be specified")
128 }
129+ if c.Dir == "" {
130+ logger.Infof("no destination directory specified, using current directory")
131+ var err error
132+ if c.Dir, err = os.Getwd(); err != nil {
133+ return err
134+ }
135+ }
136
137 return cmd.CheckEmpty(args)
138 }
139
140+var helpDoc = `
141+image metadata files have been written to:
142+%q.
143+For Juju to use this metadata, the files need to be put into the
144+image metadata search path. There are 2 options:
145+
146+1. Use tools-url in $JUJU_HOME/environments.yaml
147+Configure a http server to serve the contents of
148+%q
149+and set the value of tools-url accordingly.
150+
151+2. Upload the contents of
152+%q
153+to your cloud's private storage (for ec2 and openstack).
154+eg for openstack
155+"cd %s; swift upload %s streams/v1/*"
156+
157+`
158+
159 func (c *ImageMetadataCommand) Run(context *cmd.Context) error {
160 out := context.Stdout
161
162@@ -75,14 +152,11 @@
163 Region: c.Region,
164 Endpoint: c.Endpoint,
165 }
166- files, err := imagemetadata.Boilerplate(c.Name, c.Series, &im, &cloudSpec)
167+ _, err := imagemetadata.GenerateMetadata(c.Series, &im, &cloudSpec, c.Dir)
168 if err != nil {
169- return fmt.Errorf("boilerplate image metadata files could not be created: %v", err)
170+ return fmt.Errorf("image metadata files could not be created: %v", err)
171 }
172- fmt.Fprintf(
173- out,
174- "Boilerplate image metadata files %q have been written to %s.\n", strings.Join(files, ", "), config.JujuHome())
175- fmt.Fprintf(out, `Copy the files to the path "streams/v1" in your cloud's public bucket.`)
176- fmt.Fprintln(out, "")
177+ dest := filepath.Join(c.Dir, "streams", "v1")
178+ fmt.Fprintf(out, fmt.Sprintf(helpDoc, dest, c.Dir, c.Dir, c.Dir, c.privateStorage))
179 return nil
180 }
181
182=== modified file 'cmd/plugins/juju-metadata/imagemetadata_test.go'
183--- cmd/plugins/juju-metadata/imagemetadata_test.go 2013-07-30 23:18:16 +0000
184+++ cmd/plugins/juju-metadata/imagemetadata_test.go 2013-10-16 02:57:31 +0000
185@@ -9,26 +9,45 @@
186 "fmt"
187 "io/ioutil"
188 "os"
189+ "path/filepath"
190 "strings"
191
192 gc "launchpad.net/gocheck"
193
194 "launchpad.net/juju-core/cmd"
195 "launchpad.net/juju-core/testing"
196+ jc "launchpad.net/juju-core/testing/checkers"
197+ "launchpad.net/juju-core/testing/testbase"
198 )
199
200 type ImageMetadataSuite struct {
201+ testbase.LoggingSuite
202 environ []string
203+ home *testing.FakeHome
204+ dir string
205 }
206
207 var _ = gc.Suite(&ImageMetadataSuite{})
208
209 func (s *ImageMetadataSuite) SetUpSuite(c *gc.C) {
210+ s.LoggingSuite.SetUpSuite(c)
211 s.environ = os.Environ()
212 }
213
214 func (s *ImageMetadataSuite) SetUpTest(c *gc.C) {
215+ s.LoggingSuite.SetUpTest(c)
216 os.Clearenv()
217+ s.dir = c.MkDir()
218+ // Create a fake certificate so azure test environment can be opened.
219+ certfile, err := ioutil.TempFile(s.dir, "")
220+ c.Assert(err, gc.IsNil)
221+ filename := certfile.Name()
222+ err = ioutil.WriteFile(filename, []byte("test certificate"), 0644)
223+ c.Assert(err, gc.IsNil)
224+ envConfig := strings.Replace(metadataTestEnvConfig, "/home/me/azure.pem", filename, -1)
225+ s.home = testing.MakeFakeHome(c, envConfig)
226+ s.PatchEnvironment("AWS_ACCESS_KEY_ID", "access")
227+ s.PatchEnvironment("AWS_SECRET_ACCESS_KEY", "secret")
228 }
229
230 func (s *ImageMetadataSuite) TearDownTest(c *gc.C) {
231@@ -36,6 +55,8 @@
232 kv := strings.SplitN(envstring, "=", 2)
233 os.Setenv(kv[0], kv[1])
234 }
235+ s.home.Restore()
236+ s.LoggingSuite.TearDownTest(c)
237 }
238
239 var seriesVersions map[string]string = map[string]string{
240@@ -43,31 +64,46 @@
241 "raring": "13.04",
242 }
243
244-func (*ImageMetadataSuite) assertCommandOutput(c *gc.C, errOut, series, arch, indexFileName, imageFileName string) {
245+type expectedMetadata struct {
246+ series string
247+ arch string
248+ region string
249+ endpoint string
250+}
251+
252+func (s *ImageMetadataSuite) assertCommandOutput(c *gc.C, expected expectedMetadata, errOut, indexFileName, imageFileName string) {
253+ if expected.region == "" {
254+ expected.region = "region"
255+ }
256+ if expected.endpoint == "" {
257+ expected.endpoint = "endpoint"
258+ }
259 strippedOut := strings.Replace(errOut, "\n", "", -1)
260- c.Check(strippedOut, gc.Matches, `Boilerplate image metadata files.*have been written.*Copy the files.*`)
261- indexpath := testing.HomePath(".juju", indexFileName)
262+ c.Check(strippedOut, gc.Matches, `image metadata files have been written to.*`)
263+ indexpath := filepath.Join(s.dir, "streams", "v1", indexFileName)
264 data, err := ioutil.ReadFile(indexpath)
265 c.Assert(err, gc.IsNil)
266+ content := string(data)
267 var indices interface{}
268 err = json.Unmarshal(data, &indices)
269 c.Assert(err, gc.IsNil)
270 c.Assert(indices.(map[string]interface{})["format"], gc.Equals, "index:1.0")
271- prodId := fmt.Sprintf("com.ubuntu.cloud:server:%s:%s", seriesVersions[series], arch)
272- c.Assert(strings.Contains(string(data), prodId), gc.Equals, true)
273- c.Assert(strings.Contains(string(data), `"region": "region"`), gc.Equals, true)
274- c.Assert(strings.Contains(string(data), `"endpoint": "endpoint"`), gc.Equals, true)
275- c.Assert(strings.Contains(string(data), fmt.Sprintf(`"path": "streams/v1/%s"`, imageFileName)), gc.Equals, true)
276+ prodId := fmt.Sprintf("com.ubuntu.cloud:server:%s:%s", seriesVersions[expected.series], expected.arch)
277+ c.Assert(strings.Contains(content, prodId), jc.IsTrue)
278+ c.Assert(strings.Contains(content, fmt.Sprintf(`"region": %q`, expected.region)), jc.IsTrue)
279+ c.Assert(strings.Contains(content, fmt.Sprintf(`"endpoint": %q`, expected.endpoint)), jc.IsTrue)
280+ c.Assert(strings.Contains(content, fmt.Sprintf(`"path": "streams/v1/%s"`, imageFileName)), jc.IsTrue)
281
282- imagepath := testing.HomePath(".juju", imageFileName)
283+ imagepath := filepath.Join(s.dir, "streams", "v1", imageFileName)
284 data, err = ioutil.ReadFile(imagepath)
285 c.Assert(err, gc.IsNil)
286+ content = string(data)
287 var images interface{}
288 err = json.Unmarshal(data, &images)
289 c.Assert(err, gc.IsNil)
290 c.Assert(images.(map[string]interface{})["format"], gc.Equals, "products:1.0")
291- c.Assert(strings.Contains(string(data), prodId), gc.Equals, true)
292- c.Assert(strings.Contains(string(data), `"id": "1234"`), gc.Equals, true)
293+ c.Assert(strings.Contains(content, prodId), gc.Equals, true)
294+ c.Assert(strings.Contains(content, `"id": "1234"`), gc.Equals, true)
295 }
296
297 const (
298@@ -76,71 +112,92 @@
299 )
300
301 func (s *ImageMetadataSuite) TestImageMetadataFilesNoEnv(c *gc.C) {
302- defer testing.MakeEmptyFakeHome(c).Restore()
303-
304- ctx := testing.Context(c)
305- code := cmd.Main(
306- &ImageMetadataCommand{}, ctx, []string{"-i", "1234", "-r", "region", "-a", "arch", "-u", "endpoint", "-s", "raring"})
307- c.Assert(code, gc.Equals, 0)
308- errOut := ctx.Stdout.(*bytes.Buffer).String()
309- s.assertCommandOutput(c, errOut, "raring", "arch", defaultIndexFileName, defaultImageFileName)
310-}
311-
312-func (s *ImageMetadataSuite) TestImageMetadataFilesWithName(c *gc.C) {
313- defer testing.MakeEmptyFakeHome(c).Restore()
314-
315- ctx := testing.Context(c)
316- code := cmd.Main(
317- &ImageMetadataCommand{}, ctx, []string{"-n", "foo", "-i", "1234", "-r", "region", "-a", "arch", "-u", "endpoint", "-s", "raring"})
318- c.Assert(code, gc.Equals, 0)
319- errOut := ctx.Stdout.(*bytes.Buffer).String()
320- s.assertCommandOutput(c, errOut, "raring", "arch", "foo-"+defaultIndexFileName, "foo-"+defaultImageFileName)
321+ ctx := testing.Context(c)
322+ code := cmd.Main(
323+ &ImageMetadataCommand{}, ctx, []string{
324+ "-d", s.dir, "-i", "1234", "-r", "region", "-a", "arch", "-u", "endpoint", "-s", "raring"})
325+ c.Assert(code, gc.Equals, 0)
326+ errOut := ctx.Stdout.(*bytes.Buffer).String()
327+ expected := expectedMetadata{
328+ series: "raring",
329+ arch: "arch",
330+ }
331+ s.assertCommandOutput(c, expected, errOut, defaultIndexFileName, defaultImageFileName)
332 }
333
334 func (s *ImageMetadataSuite) TestImageMetadataFilesDefaultArch(c *gc.C) {
335- defer testing.MakeEmptyFakeHome(c).Restore()
336-
337 ctx := testing.Context(c)
338 code := cmd.Main(
339- &ImageMetadataCommand{}, ctx, []string{"-i", "1234", "-r", "region", "-u", "endpoint", "-s", "raring"})
340+ &ImageMetadataCommand{}, ctx, []string{
341+ "-d", s.dir, "-i", "1234", "-r", "region", "-u", "endpoint", "-s", "raring"})
342 c.Assert(code, gc.Equals, 0)
343 errOut := ctx.Stdout.(*bytes.Buffer).String()
344- s.assertCommandOutput(c, errOut, "raring", "amd64", defaultIndexFileName, defaultImageFileName)
345+ expected := expectedMetadata{
346+ series: "raring",
347+ arch: "amd64",
348+ }
349+ s.assertCommandOutput(c, expected, errOut, defaultIndexFileName, defaultImageFileName)
350 }
351
352 func (s *ImageMetadataSuite) TestImageMetadataFilesDefaultSeries(c *gc.C) {
353- defer testing.MakeEmptyFakeHome(c).Restore()
354-
355- ctx := testing.Context(c)
356- code := cmd.Main(
357- &ImageMetadataCommand{}, ctx, []string{"-i", "1234", "-r", "region", "-a", "arch", "-u", "endpoint"})
358- c.Assert(code, gc.Equals, 0)
359- errOut := ctx.Stdout.(*bytes.Buffer).String()
360- s.assertCommandOutput(c, errOut, "precise", "arch", defaultIndexFileName, defaultImageFileName)
361-}
362-
363-func (s *ImageMetadataSuite) TestImageMetadataFilesUsingEnvRegion(c *gc.C) {
364- defer testing.MakeEmptyFakeHome(c).Restore()
365-
366- os.Setenv("OS_REGION_NAME", "region")
367- ctx := testing.Context(c)
368- code := cmd.Main(
369- &ImageMetadataCommand{}, ctx, []string{"-i", "1234", "-u", "endpoint"})
370- c.Assert(code, gc.Equals, 0)
371- errOut := ctx.Stdout.(*bytes.Buffer).String()
372- s.assertCommandOutput(c, errOut, "precise", "amd64", defaultIndexFileName, defaultImageFileName)
373-}
374-
375-func (s *ImageMetadataSuite) TestImageMetadataFilesUsingEnvEndpoint(c *gc.C) {
376- defer testing.MakeEmptyFakeHome(c).Restore()
377-
378- os.Setenv("OS_AUTH_URL", "endpoint")
379- ctx := testing.Context(c)
380- code := cmd.Main(
381- &ImageMetadataCommand{}, ctx, []string{"-i", "1234", "-r", "region"})
382- c.Assert(code, gc.Equals, 0)
383- errOut := ctx.Stdout.(*bytes.Buffer).String()
384- s.assertCommandOutput(c, errOut, "precise", "amd64", defaultIndexFileName, defaultImageFileName)
385+ ctx := testing.Context(c)
386+ code := cmd.Main(
387+ &ImageMetadataCommand{}, ctx, []string{
388+ "-d", s.dir, "-i", "1234", "-r", "region", "-a", "arch", "-u", "endpoint"})
389+ c.Assert(code, gc.Equals, 0)
390+ errOut := ctx.Stdout.(*bytes.Buffer).String()
391+ expected := expectedMetadata{
392+ series: "precise",
393+ arch: "arch",
394+ }
395+ s.assertCommandOutput(c, expected, errOut, defaultIndexFileName, defaultImageFileName)
396+}
397+
398+func (s *ImageMetadataSuite) TestImageMetadataFilesUsingEnv(c *gc.C) {
399+ ctx := testing.Context(c)
400+ code := cmd.Main(
401+ &ImageMetadataCommand{}, ctx, []string{"-d", s.dir, "-e", "ec2", "-i", "1234"})
402+ c.Assert(code, gc.Equals, 0)
403+ errOut := ctx.Stdout.(*bytes.Buffer).String()
404+ expected := expectedMetadata{
405+ series: "precise",
406+ arch: "amd64",
407+ region: "us-east-1",
408+ endpoint: "https://ec2.us-east-1.amazonaws.com",
409+ }
410+ s.assertCommandOutput(c, expected, errOut, defaultIndexFileName, defaultImageFileName)
411+}
412+
413+func (s *ImageMetadataSuite) TestImageMetadataFilesUsingEnvWithRegionOverride(c *gc.C) {
414+ ctx := testing.Context(c)
415+ code := cmd.Main(
416+ &ImageMetadataCommand{}, ctx, []string{
417+ "-d", s.dir, "-e", "ec2", "-r", "us-west-1", "-u", "https://ec2.us-west-1.amazonaws.com", "-i", "1234"})
418+ c.Assert(code, gc.Equals, 0)
419+ errOut := ctx.Stdout.(*bytes.Buffer).String()
420+ expected := expectedMetadata{
421+ series: "precise",
422+ arch: "amd64",
423+ region: "us-west-1",
424+ endpoint: "https://ec2.us-west-1.amazonaws.com",
425+ }
426+ s.assertCommandOutput(c, expected, errOut, defaultIndexFileName, defaultImageFileName)
427+}
428+
429+func (s *ImageMetadataSuite) TestImageMetadataFilesUsingEnvWithNoHasRegion(c *gc.C) {
430+ ctx := testing.Context(c)
431+ code := cmd.Main(
432+ &ImageMetadataCommand{}, ctx, []string{
433+ "-d", s.dir, "-e", "azure", "-r", "region", "-u", "endpoint", "-i", "1234"})
434+ c.Assert(code, gc.Equals, 0)
435+ errOut := ctx.Stdout.(*bytes.Buffer).String()
436+ expected := expectedMetadata{
437+ series: "raring",
438+ arch: "amd64",
439+ region: "region",
440+ endpoint: "endpoint",
441+ }
442+ s.assertCommandOutput(c, expected, errOut, defaultIndexFileName, defaultImageFileName)
443 }
444
445 type errTestParams struct {
446@@ -160,6 +217,10 @@
447 // Missing endpoint
448 args: []string{"-i", "1234", "-u", "endpoint", "-a", "arch", "-s", "precise"},
449 },
450+ {
451+ // Missing endpoint/region for environment with no HasRegion interface
452+ args: []string{"-i", "1234", "-e", "azure"},
453+ },
454 }
455
456 func (s *ImageMetadataSuite) TestImageMetadataBadArgs(c *gc.C) {
457
458=== modified file 'cmd/plugins/juju-metadata/validateimagemetadata_test.go'
459--- cmd/plugins/juju-metadata/validateimagemetadata_test.go 2013-10-10 11:40:54 +0000
460+++ cmd/plugins/juju-metadata/validateimagemetadata_test.go 2013-10-16 02:57:31 +0000
461@@ -11,7 +11,6 @@
462 gc "launchpad.net/gocheck"
463
464 "launchpad.net/juju-core/cmd"
465- "launchpad.net/juju-core/environs/config"
466 "launchpad.net/juju-core/environs/imagemetadata"
467 "launchpad.net/juju-core/environs/simplestreams"
468 coretesting "launchpad.net/juju-core/testing"
469@@ -20,7 +19,8 @@
470
471 type ValidateImageMetadataSuite struct {
472 testbase.LoggingSuite
473- home *coretesting.FakeHome
474+ home *coretesting.FakeHome
475+ metadataDir string
476 }
477
478 var _ = gc.Suite(&ValidateImageMetadataSuite{})
479@@ -73,7 +73,7 @@
480 Region: region,
481 Endpoint: endpoint,
482 }
483- _, err := imagemetadata.MakeBoilerplate("", series, &im, &cloudSpec, false)
484+ _, err := imagemetadata.GenerateMetadata(series, &im, &cloudSpec, s.metadataDir)
485 if err != nil {
486 return err
487 }
488@@ -84,13 +84,21 @@
489 environments:
490 ec2:
491 type: ec2
492- control-bucket: foo
493 default-series: precise
494 region: us-east-1
495+
496+ azure:
497+ type: azure
498+ default-series: raring
499+ location: US West
500+ management-subscription-id: foo
501+ storage-account-name: bar
502+ management-certificate-path: /home/me/azure.pem
503 `
504
505 func (s *ValidateImageMetadataSuite) SetUpTest(c *gc.C) {
506 s.LoggingSuite.SetUpTest(c)
507+ s.metadataDir = c.MkDir()
508 s.home = coretesting.MakeFakeHome(c, metadataTestEnvConfig)
509 restore := testbase.PatchEnvironment("AWS_ACCESS_KEY_ID", "access")
510 s.AddCleanup(func(*gc.C) { restore() })
511@@ -115,9 +123,8 @@
512 func (s *ValidateImageMetadataSuite) TestEc2LocalMetadataUsingEnvironment(c *gc.C) {
513 s.setupEc2LocalMetadata(c, "us-east-1")
514 ctx := coretesting.Context(c)
515- metadataDir := config.JujuHomePath("")
516 code := cmd.Main(
517- &ValidateImageMetadataCommand{}, ctx, []string{"-e", "ec2", "-d", metadataDir},
518+ &ValidateImageMetadataCommand{}, ctx, []string{"-e", "ec2", "-d", s.metadataDir},
519 )
520 c.Assert(code, gc.Equals, 0)
521 errOut := ctx.Stdout.(*bytes.Buffer).String()
522@@ -128,11 +135,12 @@
523 func (s *ValidateImageMetadataSuite) TestEc2LocalMetadataUsingIncompleteEnvironment(c *gc.C) {
524 testbase.PatchEnvironment("AWS_ACCESS_KEY_ID", "")
525 testbase.PatchEnvironment("AWS_SECRET_ACCESS_KEY", "")
526+ testbase.PatchEnvironment("EC2_ACCESS_KEY", "")
527+ testbase.PatchEnvironment("EC2_SECRET_KEY", "")
528 s.setupEc2LocalMetadata(c, "us-east-1")
529 ctx := coretesting.Context(c)
530- metadataDir := config.JujuHomePath("")
531 code := cmd.Main(
532- &ValidateImageMetadataCommand{}, ctx, []string{"-e", "ec2", "-d", metadataDir},
533+ &ValidateImageMetadataCommand{}, ctx, []string{"-e", "ec2", "-d", s.metadataDir},
534 )
535 c.Assert(code, gc.Equals, 1)
536 errOut := ctx.Stderr.(*bytes.Buffer).String()
537@@ -143,11 +151,10 @@
538 func (s *ValidateImageMetadataSuite) TestEc2LocalMetadataWithManualParams(c *gc.C) {
539 s.setupEc2LocalMetadata(c, "us-west-1")
540 ctx := coretesting.Context(c)
541- metadataDir := config.JujuHomePath("")
542 code := cmd.Main(
543 &ValidateImageMetadataCommand{}, ctx, []string{
544 "-p", "ec2", "-s", "precise", "-r", "us-west-1",
545- "-u", "https://ec2.us-west-1.amazonaws.com", "-d", metadataDir},
546+ "-u", "https://ec2.us-west-1.amazonaws.com", "-d", s.metadataDir},
547 )
548 c.Assert(code, gc.Equals, 0)
549 errOut := ctx.Stdout.(*bytes.Buffer).String()
550@@ -158,17 +165,16 @@
551 func (s *ValidateImageMetadataSuite) TestEc2LocalMetadataNoMatch(c *gc.C) {
552 s.setupEc2LocalMetadata(c, "us-east-1")
553 ctx := coretesting.Context(c)
554- metadataDir := config.JujuHomePath("")
555 code := cmd.Main(
556 &ValidateImageMetadataCommand{}, ctx, []string{
557 "-p", "ec2", "-s", "raring", "-r", "us-west-1",
558- "-u", "https://ec2.us-west-1.amazonaws.com", "-d", metadataDir},
559+ "-u", "https://ec2.us-west-1.amazonaws.com", "-d", s.metadataDir},
560 )
561 c.Assert(code, gc.Equals, 1)
562 code = cmd.Main(
563 &ValidateImageMetadataCommand{}, ctx, []string{
564 "-p", "ec2", "-s", "precise", "-r", "region",
565- "-u", "https://ec2.region.amazonaws.com", "-d", metadataDir},
566+ "-u", "https://ec2.region.amazonaws.com", "-d", s.metadataDir},
567 )
568 c.Assert(code, gc.Equals, 1)
569 }
570@@ -176,11 +182,10 @@
571 func (s *ValidateImageMetadataSuite) TestOpenstackLocalMetadataWithManualParams(c *gc.C) {
572 s.makeLocalMetadata(c, "1234", "region-2", "raring", "some-auth-url")
573 ctx := coretesting.Context(c)
574- metadataDir := config.JujuHomePath("")
575 code := cmd.Main(
576 &ValidateImageMetadataCommand{}, ctx, []string{
577 "-p", "openstack", "-s", "raring", "-r", "region-2",
578- "-u", "some-auth-url", "-d", metadataDir},
579+ "-u", "some-auth-url", "-d", s.metadataDir},
580 )
581 c.Assert(code, gc.Equals, 0)
582 errOut := ctx.Stdout.(*bytes.Buffer).String()
583@@ -191,17 +196,16 @@
584 func (s *ValidateImageMetadataSuite) TestOpenstackLocalMetadataNoMatch(c *gc.C) {
585 s.makeLocalMetadata(c, "1234", "region-2", "raring", "some-auth-url")
586 ctx := coretesting.Context(c)
587- metadataDir := config.JujuHomePath("")
588 code := cmd.Main(
589 &ValidateImageMetadataCommand{}, ctx, []string{
590 "-p", "openstack", "-s", "precise", "-r", "region-2",
591- "-u", "some-auth-url", "-d", metadataDir},
592+ "-u", "some-auth-url", "-d", s.metadataDir},
593 )
594 c.Assert(code, gc.Equals, 1)
595 code = cmd.Main(
596 &ValidateImageMetadataCommand{}, ctx, []string{
597 "-p", "openstack", "-s", "raring", "-r", "region-3",
598- "-u", "some-auth-url", "-d", metadataDir},
599+ "-u", "some-auth-url", "-d", s.metadataDir},
600 )
601 c.Assert(code, gc.Equals, 1)
602 }
603
604=== renamed file 'environs/imagemetadata/boilerplate.go' => 'environs/imagemetadata/generate.go'
605--- environs/imagemetadata/boilerplate.go 2013-08-07 04:34:02 +0000
606+++ environs/imagemetadata/generate.go 2013-10-16 02:57:31 +0000
607@@ -12,38 +12,25 @@
608 "text/template"
609 "time"
610
611- "launchpad.net/juju-core/environs/config"
612 "launchpad.net/juju-core/environs/simplestreams"
613 )
614
615 const (
616 defaultIndexFileName = "index.json"
617 defaultImageFileName = "imagemetadata.json"
618- streamsDir = "streams/v1"
619 )
620
621-// Boilerplate generates some basic simplestreams metadata using the specified cloud and image details.
622-// If name is non-empty, it will be used as a prefix for the names of the generated index and image files.
623-func Boilerplate(name, series string, im *ImageMetadata, cloudSpec *simplestreams.CloudSpec) ([]string, error) {
624- return MakeBoilerplate(name, series, im, cloudSpec, true)
625-}
626-
627-// MakeBoilerplate exists so it can be called by tests. See Boilerplate above. It provides an option to retain
628-// the streams directories when writing the generated metadata files.
629-func MakeBoilerplate(name, series string, im *ImageMetadata, cloudSpec *simplestreams.CloudSpec, flattenPath bool) ([]string, error) {
630+// GenerateMetadata generates some basic simplestreams metadata using the specified cloud and image details.
631+func GenerateMetadata(series string, im *ImageMetadata, cloudSpec *simplestreams.CloudSpec, dest string) ([]string, error) {
632 indexFileName := defaultIndexFileName
633 imageFileName := defaultImageFileName
634- if name != "" {
635- indexFileName = fmt.Sprintf("%s-%s", name, indexFileName)
636- imageFileName = fmt.Sprintf("%s-%s", name, imageFileName)
637- }
638 now := time.Now()
639 imparams := imageMetadataParams{
640 Id: im.Id,
641 Arch: im.Arch,
642 Region: cloudSpec.Region,
643 URL: cloudSpec.Endpoint,
644- Path: streamsDir,
645+ Path: "streams/v1",
646 ImageFileName: imageFileName,
647 Updated: now.Format(time.RFC1123Z),
648 VersionKey: now.Format("20060102"),
649@@ -55,14 +42,12 @@
650 return nil, fmt.Errorf("invalid series %q", series)
651 }
652
653- if !flattenPath {
654- streamsPath := config.JujuHomePath(streamsDir)
655- if err = os.MkdirAll(streamsPath, 0755); err != nil {
656- return nil, err
657- }
658- indexFileName = filepath.Join(streamsDir, indexFileName)
659- imageFileName = filepath.Join(streamsDir, imageFileName)
660+ streamsPath := filepath.Join(dest, "streams", "v1")
661+ if err = os.MkdirAll(streamsPath, 0755); err != nil {
662+ return nil, err
663 }
664+ indexFileName = filepath.Join(streamsPath, indexFileName)
665+ imageFileName = filepath.Join(streamsPath, imageFileName)
666 err = writeJsonFile(imparams, indexFileName, indexBoilerplate)
667 if err != nil {
668 return nil, err
669@@ -94,8 +79,7 @@
670 panic(fmt.Errorf("cannot generate %s metdata: %v", filename, err))
671 }
672 data := metadata.Bytes()
673- path := config.JujuHomePath(filename)
674- if err := ioutil.WriteFile(path, data, 0666); err != nil {
675+ if err := ioutil.WriteFile(filename, data, 0666); err != nil {
676 return err
677 }
678 return nil
679
680=== modified file 'environs/imagemetadata/validation_test.go'
681--- environs/imagemetadata/validation_test.go 2013-09-24 08:01:52 +0000
682+++ environs/imagemetadata/validation_test.go 2013-10-16 02:57:31 +0000
683@@ -6,16 +6,14 @@
684 import (
685 gc "launchpad.net/gocheck"
686
687- "launchpad.net/juju-core/environs/config"
688 "launchpad.net/juju-core/environs/imagemetadata"
689 "launchpad.net/juju-core/environs/simplestreams"
690- coretesting "launchpad.net/juju-core/testing"
691 "launchpad.net/juju-core/testing/testbase"
692 )
693
694 type ValidateSuite struct {
695 testbase.LoggingSuite
696- home *coretesting.FakeHome
697+ metadataDir string
698 }
699
700 var _ = gc.Suite(&ValidateSuite{})
701@@ -29,7 +27,7 @@
702 Region: region,
703 Endpoint: endpoint,
704 }
705- _, err := imagemetadata.MakeBoilerplate("", series, &im, &cloudSpec, false)
706+ _, err := imagemetadata.GenerateMetadata(series, &im, &cloudSpec, s.metadataDir)
707 if err != nil {
708 return err
709 }
710@@ -38,23 +36,17 @@
711
712 func (s *ValidateSuite) SetUpTest(c *gc.C) {
713 s.LoggingSuite.SetUpTest(c)
714- s.home = coretesting.MakeEmptyFakeHome(c)
715-}
716-
717-func (s *ValidateSuite) TearDownTest(c *gc.C) {
718- s.home.Restore()
719- s.LoggingSuite.TearDownTest(c)
720+ s.metadataDir = c.MkDir()
721 }
722
723 func (s *ValidateSuite) TestMatch(c *gc.C) {
724 s.makeLocalMetadata(c, "1234", "region-2", "raring", "some-auth-url")
725- metadataDir := config.JujuHomePath("")
726 params := &simplestreams.MetadataLookupParams{
727 Region: "region-2",
728 Series: "raring",
729 Architectures: []string{"amd64"},
730 Endpoint: "some-auth-url",
731- Sources: []simplestreams.DataSource{simplestreams.NewURLDataSource("file://"+metadataDir, simplestreams.VerifySSLHostnames)},
732+ Sources: []simplestreams.DataSource{simplestreams.NewURLDataSource("file://"+s.metadataDir, simplestreams.VerifySSLHostnames)},
733 }
734 imageIds, err := imagemetadata.ValidateImageMetadata(params)
735 c.Assert(err, gc.IsNil)
736@@ -63,13 +55,12 @@
737
738 func (s *ValidateSuite) TestNoMatch(c *gc.C) {
739 s.makeLocalMetadata(c, "1234", "region-2", "raring", "some-auth-url")
740- metadataDir := config.JujuHomePath("")
741 params := &simplestreams.MetadataLookupParams{
742 Region: "region-2",
743 Series: "precise",
744 Architectures: []string{"amd64"},
745 Endpoint: "some-auth-url",
746- Sources: []simplestreams.DataSource{simplestreams.NewURLDataSource("file://"+metadataDir, simplestreams.VerifySSLHostnames)},
747+ Sources: []simplestreams.DataSource{simplestreams.NewURLDataSource("file://"+s.metadataDir, simplestreams.VerifySSLHostnames)},
748 }
749 _, err := imagemetadata.ValidateImageMetadata(params)
750 c.Assert(err, gc.Not(gc.IsNil))

Subscribers

People subscribed via source and target branches

to status/vote changes: