Merge lp:~wallyworld/juju-core/verbose-metadata-validation 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: 2376
Proposed branch: lp:~wallyworld/juju-core/verbose-metadata-validation
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 2037 lines (+389/-386)
45 files modified
cmd/plugins/juju-metadata/toolsmetadata.go (+5/-15)
cmd/plugins/juju-metadata/validateimagemetadata.go (+27/-8)
cmd/plugins/juju-metadata/validateimagemetadata_test.go (+18/-3)
cmd/plugins/juju-metadata/validatetoolsmetadata.go (+27/-7)
cmd/plugins/juju-metadata/validatetoolsmetadata_test.go (+33/-37)
environs/bootstrap/bootstrap_test.go (+2/-1)
environs/imagemetadata/generate.go (+2/-2)
environs/imagemetadata/generate_test.go (+2/-2)
environs/imagemetadata/simplestreams.go (+6/-4)
environs/imagemetadata/simplestreams_test.go (+24/-7)
environs/imagemetadata/testing/testing.go (+1/-1)
environs/imagemetadata/urls.go (+2/-2)
environs/imagemetadata/validation.go (+10/-10)
environs/imagemetadata/validation_test.go (+11/-4)
environs/instances/image_test.go (+2/-1)
environs/simplestreams/datasource.go (+12/-2)
environs/simplestreams/datasource_test.go (+4/-4)
environs/simplestreams/simplestreams.go (+27/-9)
environs/simplestreams/simplestreams_test.go (+9/-3)
environs/storage/storage.go (+11/-5)
environs/storage/storage_test.go (+6/-6)
environs/sync/sync.go (+1/-1)
environs/tools/boilerplate.go (+0/-153)
environs/tools/simplestreams.go (+12/-9)
environs/tools/simplestreams_test.go (+40/-18)
environs/tools/testing/testing.go (+1/-1)
environs/tools/tools.go (+2/-2)
environs/tools/urls.go (+2/-2)
environs/tools/validation.go (+12/-8)
environs/tools/validation_test.go (+49/-34)
provider/azure/environ.go (+3/-3)
provider/azure/instancetype.go (+1/-1)
provider/azure/instancetype_test.go (+3/-2)
provider/common/mock_test.go (+1/-1)
provider/dummy/environs.go (+2/-2)
provider/ec2/ec2.go (+2/-2)
provider/ec2/image.go (+1/-1)
provider/ec2/image_test.go (+4/-2)
provider/ec2/local_test.go (+1/-1)
provider/local/environ.go (+1/-1)
provider/maas/environ.go (+2/-2)
provider/manual/environ.go (+1/-1)
provider/openstack/image.go (+1/-1)
provider/openstack/local_test.go (+1/-1)
provider/openstack/provider.go (+5/-4)
To merge this branch: bzr merge lp:~wallyworld/juju-core/verbose-metadata-validation
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+209162@code.launchpad.net

Commit message

Add metadata to simplestreams validation

juju metadata validate-[images|tools] now prints
metadata to show how the lookup was performed.
Along the way, drive by fixes were done to delete
old code and improve existing code associated with
simplestreams.

Description of the change

Add metadata to simplestreams validation

juju metadata validate-[images|tools] now prints
metadata to show how the lookup was performed.
Along the way, drive by fixes were done to delete
old code and improve existing code associated with
simplestreams.

A picture is worth 1000 words:

ian@wallyworld:~$ juju metadata validate-images
ImageIds:
- ami-99999af0
- ami-0d9c9f64
- ami-6f969506
- ami-0b9c9f62
- ami-359c9f5c
- ami-a18c8fc8
Region: us-east-1
Resolve Metadata:
  source: default cloud images
  signed: true
  indexURL: http://cloud-images.ubuntu.com/releases/streams/v1/index.sjson
ian@wallyworld:~$ juju metadata validate-tools
Matching Tools Versions:
- 1.17.4-precise-amd64
- 1.17.4-precise-i386
Resolve Metadata:
  source: default simplestreams
  signed: false
  indexURL: https://streams.canonical.com/juju/tools/streams/v1/index.json
  mirrorURL: https://juju-dist.s3.amazonaws.com/tools

https://codereview.appspot.com/70960044/

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

Reviewers: mp+209162_code.launchpad.net,

Message:
Please take a look.

Description:
Add metadata to simplestreams validation

juju metadata validate-[images|tools] now prints
metadata to show how the lookup was performed.
Along the way, drive by fixes were done to delete
old code and improve existing code associated with
simplestreams.

A picture is worth 1000 words:

ian@wallyworld:~$ juju metadata validate-images
ImageIds:
- ami-99999af0
- ami-0d9c9f64
- ami-6f969506

- ami-0b9c9f62

- ami-359c9f5c

- ami-a18c8fc8

Region: us-east-1

Resolve Metadata:

   source: default cloud images

   signed: true

   indexURL:
http://cloud-images.ubuntu.com/releases/streams/v1/index.sjson

ian@wallyworld:~$ juju metadata validate-tools
Matching Tools Versions:
- 1.17.4-precise-amd64
- 1.17.4-precise-i386
Resolve Metadata:
   source: default simplestreams
   signed: false
   indexURL:
https://streams.canonical.com/juju/tools/streams/v1/index.json
   mirrorURL: https://juju-dist.s3.amazonaws.com/tools

https://code.launchpad.net/~wallyworld/juju-core/verbose-metadata-validation/+merge/209162

(do not edit description out of merge proposal)

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

Affected files (+330, -361 lines):
   A [revision details]
   M cmd/plugins/juju-metadata/toolsmetadata.go
   M cmd/plugins/juju-metadata/validateimagemetadata.go
   M cmd/plugins/juju-metadata/validateimagemetadata_test.go
   M cmd/plugins/juju-metadata/validatetoolsmetadata.go
   M cmd/plugins/juju-metadata/validatetoolsmetadata_test.go
   M environs/bootstrap/bootstrap_test.go
   M environs/imagemetadata/generate.go
   M environs/imagemetadata/generate_test.go
   M environs/imagemetadata/simplestreams.go
   M environs/imagemetadata/simplestreams_test.go
   M environs/imagemetadata/testing/testing.go
   M environs/imagemetadata/urls.go
   M environs/imagemetadata/validation.go
   M environs/imagemetadata/validation_test.go
   M environs/instances/image_test.go
   M environs/simplestreams/datasource.go
   M environs/simplestreams/datasource_test.go
   M environs/simplestreams/simplestreams.go
   M environs/simplestreams/simplestreams_test.go
   M environs/storage/storage.go
   M environs/storage/storage_test.go
   M environs/sync/sync.go
   D environs/tools/boilerplate.go
   M environs/tools/simplestreams.go
   M environs/tools/simplestreams_test.go
   M environs/tools/testing/testing.go
   M environs/tools/tools.go
   M environs/tools/urls.go
   M environs/tools/validation.go
   M environs/tools/validation_test.go
   M provider/azure/environ.go
   M provider/azure/instancetype.go
   M provider/azure/instancetype_test.go
   M provider/common/mock_test.go
   M provider/dummy/environs.go
   M provider/ec2/ec2.go
   M provider/ec2/image.go
   M provider/ec2/image_test.go
   M provider/ec2/local_test.go
   M provider/local/environ.go
   M provider/maas/environ.go
   M provider/manual/environ.go
   M provider/openstack/image.go
   M provider/openstack/local_test.go
   M provider/openstack/provider.go

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

On 2014/03/04 05:51:30, wallyworld wrote:
> Please take a look.

LGTM - although was wondering why the resolve information wasn't
encapsulated in the error rather than a separate parameter.

https://codereview.appspot.com/70960044/

Revision history for this message
Tim Penhey (thumper) wrote :

On 2014/03/05 01:54:16, thumper wrote:
> On 2014/03/04 05:51:30, wallyworld wrote:
> > Please take a look.

> LGTM - although was wondering why the resolve information wasn't
encapsulated in
> the error rather than a separate parameter.

Ah, we show the resolution info on success as well. nm.

https://codereview.appspot.com/70960044/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'cmd/plugins/juju-metadata/toolsmetadata.go'
--- cmd/plugins/juju-metadata/toolsmetadata.go 2014-01-31 00:14:11 +0000
+++ cmd/plugins/juju-metadata/toolsmetadata.go 2014-03-04 05:51:00 +0000
@@ -5,9 +5,6 @@
55
6import (6import (
7 "fmt"7 "fmt"
8 "net/url"
9 "path"
10 "strings"
118
12 "github.com/loggo/loggo"9 "github.com/loggo/loggo"
13 "launchpad.net/gnuflag"10 "launchpad.net/gnuflag"
@@ -60,19 +57,12 @@
60 const minorVersion = -157 const minorVersion = -1
61 toolsList, err := envtools.ReadList(sourceStorage, version.Current.Major, minorVersion)58 toolsList, err := envtools.ReadList(sourceStorage, version.Current.Major, minorVersion)
62 if err == envtools.ErrNoTools {59 if err == envtools.ErrNoTools {
63 source := envtools.DefaultBaseURL60 var source string
64 var u *url.URL61 source, err = envtools.ToolsURL(envtools.DefaultBaseURL)
65 u, err = url.Parse(source)
66 if err != nil {62 if err != nil {
67 return fmt.Errorf("invalid tools source %s: %v", source, err)63 return err
68 }64 }
69 if u.Scheme == "" {65 sourceDataSource := simplestreams.NewURLDataSource("local source", source, simplestreams.VerifySSLHostnames)
70 source = "file://" + source
71 if !strings.HasSuffix(source, "/"+storage.BaseToolsPath) {
72 source = path.Join(source, storage.BaseToolsPath)
73 }
74 }
75 sourceDataSource := simplestreams.NewURLDataSource(source, simplestreams.VerifySSLHostnames)
76 toolsList, err = envtools.FindToolsForCloud(66 toolsList, err = envtools.FindToolsForCloud(
77 []simplestreams.DataSource{sourceDataSource}, simplestreams.CloudSpec{},67 []simplestreams.DataSource{sourceDataSource}, simplestreams.CloudSpec{},
78 version.Current.Major, minorVersion, coretools.Filter{})68 version.Current.Major, minorVersion, coretools.Filter{})
7969
=== modified file 'cmd/plugins/juju-metadata/validateimagemetadata.go'
--- cmd/plugins/juju-metadata/validateimagemetadata.go 2014-02-13 02:46:58 +0000
+++ cmd/plugins/juju-metadata/validateimagemetadata.go 2014-03-04 05:51:00 +0000
@@ -22,6 +22,7 @@
22// ValidateImageMetadataCommand22// ValidateImageMetadataCommand
23type ValidateImageMetadataCommand struct {23type ValidateImageMetadataCommand struct {
24 cmd.EnvCommandBase24 cmd.EnvCommandBase
25 out cmd.Output
25 providerType string26 providerType string
26 metadataDir string27 metadataDir string
27 series string28 series string
@@ -79,6 +80,7 @@
7980
80func (c *ValidateImageMetadataCommand) SetFlags(f *gnuflag.FlagSet) {81func (c *ValidateImageMetadataCommand) SetFlags(f *gnuflag.FlagSet) {
81 c.EnvCommandBase.SetFlags(f)82 c.EnvCommandBase.SetFlags(f)
83 c.out.AddFlags(f, "smart", cmd.DefaultFormatters)
82 f.StringVar(&c.providerType, "p", "", "the provider type eg ec2, openstack")84 f.StringVar(&c.providerType, "p", "", "the provider type eg ec2, openstack")
83 f.StringVar(&c.metadataDir, "d", "", "directory where metadata files are found")85 f.StringVar(&c.metadataDir, "d", "", "directory where metadata files are found")
84 f.StringVar(&c.series, "s", "", "the series for which to validate (overrides env config series)")86 f.StringVar(&c.series, "s", "", "the series for which to validate (overrides env config series)")
@@ -180,26 +182,43 @@
180 if _, err := os.Stat(dir); err != nil {182 if _, err := os.Stat(dir); err != nil {
181 return err183 return err
182 }184 }
183 params.Sources = []simplestreams.DataSource{simplestreams.NewURLDataSource("file://"+dir, simplestreams.VerifySSLHostnames)}185 params.Sources = []simplestreams.DataSource{
186 simplestreams.NewURLDataSource(
187 "local metadata directory", "file://"+dir, simplestreams.VerifySSLHostnames),
188 }
184 }189 }
185 params.Stream = c.stream190 params.Stream = c.stream
186191
187 image_ids, err := imagemetadata.ValidateImageMetadata(params)192 image_ids, resolveInfo, err := imagemetadata.ValidateImageMetadata(params)
188 if err != nil {193 if err != nil {
194 if resolveInfo != nil {
195 metadata := map[string]interface{}{
196 "Resolve Metadata": *resolveInfo,
197 }
198 if metadataYaml, yamlErr := cmd.FormatYaml(metadata); yamlErr == nil {
199 err = fmt.Errorf("%v\n%v", err, string(metadataYaml))
200 }
201 }
189 return err202 return err
190 }203 }
191
192 if len(image_ids) > 0 {204 if len(image_ids) > 0 {
193 fmt.Fprintf(context.Stdout, "matching image ids for region %q:\n%s\n", params.Region, strings.Join(image_ids, "\n"))205 metadata := map[string]interface{}{
206 "ImageIds": image_ids,
207 "Region": params.Region,
208 "Resolve Metadata": *resolveInfo,
209 }
210 c.out.Write(context, metadata)
194 } else {211 } else {
195 var urls []string212 var sources []string
196 for _, s := range params.Sources {213 for _, s := range params.Sources {
197 url, err := s.URL("")214 url, err := s.URL("")
198 if err != nil {215 if err == nil {
199 urls = append(urls, url)216 sources = append(sources, fmt.Sprintf("- %s (%s)", s.Description(), url))
200 }217 }
201 }218 }
202 return fmt.Errorf("no matching image ids for region %s using URLs:\n%s", params.Region, strings.Join(urls, "\n"))219 return fmt.Errorf(
220 "no matching image ids for region %s using sources:\n%s",
221 params.Region, strings.Join(sources, "\n"))
203 }222 }
204 return nil223 return nil
205}224}
206225
=== modified file 'cmd/plugins/juju-metadata/validateimagemetadata_test.go'
--- cmd/plugins/juju-metadata/validateimagemetadata_test.go 2014-01-29 02:09:39 +0000
+++ cmd/plugins/juju-metadata/validateimagemetadata_test.go 2014-03-04 05:51:00 +0000
@@ -133,7 +133,9 @@
133 c.Assert(code, gc.Equals, 0)133 c.Assert(code, gc.Equals, 0)
134 errOut := ctx.Stdout.(*bytes.Buffer).String()134 errOut := ctx.Stdout.(*bytes.Buffer).String()
135 strippedOut := strings.Replace(errOut, "\n", "", -1)135 strippedOut := strings.Replace(errOut, "\n", "", -1)
136 c.Check(strippedOut, gc.Matches, `matching image ids for region "us-east-1":.*`)136 c.Check(
137 strippedOut, gc.Matches,
138 `ImageIds:.*"1234".*Region:.*us-east-1.*Resolve Metadata:.*source: local metadata directory.*`)
137}139}
138140
139func (s *ValidateImageMetadataSuite) TestEc2LocalMetadataUsingEnvironment(c *gc.C) {141func (s *ValidateImageMetadataSuite) TestEc2LocalMetadataUsingEnvironment(c *gc.C) {
@@ -169,7 +171,9 @@
169 c.Assert(code, gc.Equals, 0)171 c.Assert(code, gc.Equals, 0)
170 errOut := ctx.Stdout.(*bytes.Buffer).String()172 errOut := ctx.Stdout.(*bytes.Buffer).String()
171 strippedOut := strings.Replace(errOut, "\n", "", -1)173 strippedOut := strings.Replace(errOut, "\n", "", -1)
172 c.Check(strippedOut, gc.Matches, `matching image ids for region "us-west-1":.*`)174 c.Check(
175 strippedOut, gc.Matches,
176 `ImageIds:.*"1234".*Region:.*us-west-1.*Resolve Metadata:.*source: local metadata directory.*`)
173}177}
174178
175func (s *ValidateImageMetadataSuite) TestEc2LocalMetadataNoMatch(c *gc.C) {179func (s *ValidateImageMetadataSuite) TestEc2LocalMetadataNoMatch(c *gc.C) {
@@ -187,6 +191,9 @@
187 "-u", "https://ec2.region.amazonaws.com", "-d", s.metadataDir},191 "-u", "https://ec2.region.amazonaws.com", "-d", s.metadataDir},
188 )192 )
189 c.Assert(code, gc.Equals, 1)193 c.Assert(code, gc.Equals, 1)
194 errOut := ctx.Stderr.(*bytes.Buffer).String()
195 strippedOut := strings.Replace(errOut, "\n", "", -1)
196 c.Check(strippedOut, gc.Matches, `.*Resolve Metadata:.*`)
190}197}
191198
192func (s *ValidateImageMetadataSuite) TestOpenstackLocalMetadataWithManualParams(c *gc.C) {199func (s *ValidateImageMetadataSuite) TestOpenstackLocalMetadataWithManualParams(c *gc.C) {
@@ -200,7 +207,9 @@
200 c.Assert(code, gc.Equals, 0)207 c.Assert(code, gc.Equals, 0)
201 errOut := ctx.Stdout.(*bytes.Buffer).String()208 errOut := ctx.Stdout.(*bytes.Buffer).String()
202 strippedOut := strings.Replace(errOut, "\n", "", -1)209 strippedOut := strings.Replace(errOut, "\n", "", -1)
203 c.Check(strippedOut, gc.Matches, `matching image ids for region "region-2":.*`)210 c.Check(
211 strippedOut, gc.Matches,
212 `ImageIds:.*"1234".*Region:.*region-2.*Resolve Metadata:.*source: local metadata directory.*`)
204}213}
205214
206func (s *ValidateImageMetadataSuite) TestOpenstackLocalMetadataNoMatch(c *gc.C) {215func (s *ValidateImageMetadataSuite) TestOpenstackLocalMetadataNoMatch(c *gc.C) {
@@ -212,10 +221,16 @@
212 "-u", "some-auth-url", "-d", s.metadataDir},221 "-u", "some-auth-url", "-d", s.metadataDir},
213 )222 )
214 c.Assert(code, gc.Equals, 1)223 c.Assert(code, gc.Equals, 1)
224 errOut := ctx.Stderr.(*bytes.Buffer).String()
225 strippedOut := strings.Replace(errOut, "\n", "", -1)
226 c.Check(strippedOut, gc.Matches, `.*Resolve Metadata:.*`)
215 code = cmd.Main(227 code = cmd.Main(
216 &ValidateImageMetadataCommand{}, ctx, []string{228 &ValidateImageMetadataCommand{}, ctx, []string{
217 "-p", "openstack", "-s", "raring", "-r", "region-3",229 "-p", "openstack", "-s", "raring", "-r", "region-3",
218 "-u", "some-auth-url", "-d", s.metadataDir},230 "-u", "some-auth-url", "-d", s.metadataDir},
219 )231 )
220 c.Assert(code, gc.Equals, 1)232 c.Assert(code, gc.Equals, 1)
233 errOut = ctx.Stderr.(*bytes.Buffer).String()
234 strippedOut = strings.Replace(errOut, "\n", "", -1)
235 c.Check(strippedOut, gc.Matches, `.*Resolve Metadata:.*`)
221}236}
222237
=== modified file 'cmd/plugins/juju-metadata/validatetoolsmetadata.go'
--- cmd/plugins/juju-metadata/validatetoolsmetadata.go 2014-02-28 03:05:47 +0000
+++ cmd/plugins/juju-metadata/validatetoolsmetadata.go 2014-03-04 05:51:00 +0000
@@ -21,6 +21,7 @@
21// ValidateToolsMetadataCommand21// ValidateToolsMetadataCommand
22type ValidateToolsMetadataCommand struct {22type ValidateToolsMetadataCommand struct {
23 cmd.EnvCommandBase23 cmd.EnvCommandBase
24 out cmd.Output
24 providerType string25 providerType string
25 metadataDir string26 metadataDir string
26 series string27 series string
@@ -100,6 +101,7 @@
100101
101func (c *ValidateToolsMetadataCommand) SetFlags(f *gnuflag.FlagSet) {102func (c *ValidateToolsMetadataCommand) SetFlags(f *gnuflag.FlagSet) {
102 c.EnvCommandBase.SetFlags(f)103 c.EnvCommandBase.SetFlags(f)
104 c.out.AddFlags(f, "smart", cmd.DefaultFormatters)
103 f.StringVar(&c.providerType, "p", "", "the provider type eg ec2, openstack")105 f.StringVar(&c.providerType, "p", "", "the provider type eg ec2, openstack")
104 f.StringVar(&c.metadataDir, "d", "", "directory where metadata files are found")106 f.StringVar(&c.metadataDir, "d", "", "directory where metadata files are found")
105 f.StringVar(&c.series, "s", "", "the series for which to validate (overrides env config series)")107 f.StringVar(&c.series, "s", "", "the series for which to validate (overrides env config series)")
@@ -191,30 +193,48 @@
191 if _, err := os.Stat(c.metadataDir); err != nil {193 if _, err := os.Stat(c.metadataDir); err != nil {
192 return err194 return err
193 }195 }
194 params.Sources = []simplestreams.DataSource{simplestreams.NewURLDataSource("file://"+c.metadataDir, simplestreams.VerifySSLHostnames)}196 toolsURL, err := tools.ToolsURL(c.metadataDir)
197 if err != nil {
198 return err
199 }
200 params.Sources = []simplestreams.DataSource{simplestreams.NewURLDataSource(
201 "local metadata directory", toolsURL, simplestreams.VerifySSLHostnames),
202 }
195 }203 }
196204
197 versions, err := tools.ValidateToolsMetadata(&tools.ToolsMetadataLookupParams{205 versions, resolveInfo, err := tools.ValidateToolsMetadata(&tools.ToolsMetadataLookupParams{
198 MetadataLookupParams: *params,206 MetadataLookupParams: *params,
199 Version: c.exactVersion,207 Version: c.exactVersion,
200 Major: c.major,208 Major: c.major,
201 Minor: c.minor,209 Minor: c.minor,
202 })210 })
203 if err != nil {211 if err != nil {
212 if resolveInfo != nil {
213 metadata := map[string]interface{}{
214 "Resolve Metadata": *resolveInfo,
215 }
216 if metadataYaml, yamlErr := cmd.FormatYaml(metadata); yamlErr == nil {
217 err = fmt.Errorf("%v\n%v", err, string(metadataYaml))
218 }
219 }
204 return err220 return err
205 }221 }
206222
207 if len(versions) > 0 {223 if len(versions) > 0 {
208 fmt.Fprintf(context.Stdout, "matching tools versions:\n%s\n", strings.Join(versions, "\n"))224 metadata := map[string]interface{}{
225 "Matching Tools Versions": versions,
226 "Resolve Metadata": *resolveInfo,
227 }
228 c.out.Write(context, metadata)
209 } else {229 } else {
210 var urls []string230 var sources []string
211 for _, s := range params.Sources {231 for _, s := range params.Sources {
212 url, err := s.URL("")232 url, err := s.URL("")
213 if err != nil {233 if err == nil {
214 urls = append(urls, url)234 sources = append(sources, fmt.Sprintf("- %s (%s)", s.Description(), url))
215 }235 }
216 }236 }
217 return fmt.Errorf("no matching tools using URLs:\n%s", strings.Join(urls, "\n"))237 return fmt.Errorf("no matching tools using sources:\n%s", strings.Join(sources, "\n"))
218 }238 }
219 return nil239 return nil
220}240}
221241
=== modified file 'cmd/plugins/juju-metadata/validatetoolsmetadata_test.go'
--- cmd/plugins/juju-metadata/validatetoolsmetadata_test.go 2014-01-22 22:48:54 +0000
+++ cmd/plugins/juju-metadata/validatetoolsmetadata_test.go 2014-03-04 05:51:00 +0000
@@ -11,9 +11,8 @@
11 gc "launchpad.net/gocheck"11 gc "launchpad.net/gocheck"
1212
13 "launchpad.net/juju-core/cmd"13 "launchpad.net/juju-core/cmd"
14 "launchpad.net/juju-core/environs/simplestreams"14 "launchpad.net/juju-core/environs/filestorage"
15 "launchpad.net/juju-core/environs/tools"15 "launchpad.net/juju-core/environs/tools"
16 "launchpad.net/juju-core/juju/osenv"
17 coretesting "launchpad.net/juju-core/testing"16 coretesting "launchpad.net/juju-core/testing"
18 "launchpad.net/juju-core/testing/testbase"17 "launchpad.net/juju-core/testing/testbase"
19 "launchpad.net/juju-core/version"18 "launchpad.net/juju-core/version"
@@ -21,7 +20,8 @@
2120
22type ValidateToolsMetadataSuite struct {21type ValidateToolsMetadataSuite struct {
23 testbase.LoggingSuite22 testbase.LoggingSuite
24 home *coretesting.FakeHome23 home *coretesting.FakeHome
24 metadataDir string
25}25}
2626
27var _ = gc.Suite(&ValidateToolsMetadataSuite{})27var _ = gc.Suite(&ValidateToolsMetadataSuite{})
@@ -72,16 +72,14 @@
72}72}
7373
74func (s *ValidateToolsMetadataSuite) makeLocalMetadata(c *gc.C, version, region, series, endpoint string) error {74func (s *ValidateToolsMetadataSuite) makeLocalMetadata(c *gc.C, version, region, series, endpoint string) error {
75 tm := tools.ToolsMetadata{75 tm := []*tools.ToolsMetadata{{
76 Version: version,76 Version: version,
77 Arch: "amd64",77 Arch: "amd64",
78 Release: series,78 Release: series,
79 }79 }}
80 cloudSpec := simplestreams.CloudSpec{80 targetStorage, err := filestorage.NewFileStorageWriter(s.metadataDir, filestorage.UseDefaultTmpDir)
81 Region: region,81 c.Assert(err, gc.IsNil)
82 Endpoint: endpoint,82 err = tools.WriteMetadata(targetStorage, tm, false)
83 }
84 _, err := tools.MakeBoilerplate(&tm, &cloudSpec, false)
85 if err != nil {83 if err != nil {
86 return err84 return err
87 }85 }
@@ -91,6 +89,7 @@
91func (s *ValidateToolsMetadataSuite) SetUpTest(c *gc.C) {89func (s *ValidateToolsMetadataSuite) SetUpTest(c *gc.C) {
92 s.LoggingSuite.SetUpTest(c)90 s.LoggingSuite.SetUpTest(c)
93 s.home = coretesting.MakeFakeHome(c, metadataTestEnvConfig)91 s.home = coretesting.MakeFakeHome(c, metadataTestEnvConfig)
92 s.metadataDir = c.MkDir()
94 restore := testbase.PatchEnvironment("AWS_ACCESS_KEY_ID", "access")93 restore := testbase.PatchEnvironment("AWS_ACCESS_KEY_ID", "access")
95 s.AddCleanup(func(*gc.C) { restore() })94 s.AddCleanup(func(*gc.C) { restore() })
96 restore = testbase.PatchEnvironment("AWS_SECRET_ACCESS_KEY", "secret")95 restore = testbase.PatchEnvironment("AWS_SECRET_ACCESS_KEY", "secret")
@@ -114,14 +113,13 @@
114func (s *ValidateToolsMetadataSuite) TestEc2LocalMetadataUsingEnvironment(c *gc.C) {113func (s *ValidateToolsMetadataSuite) TestEc2LocalMetadataUsingEnvironment(c *gc.C) {
115 s.setupEc2LocalMetadata(c, "us-east-1")114 s.setupEc2LocalMetadata(c, "us-east-1")
116 ctx := coretesting.Context(c)115 ctx := coretesting.Context(c)
117 metadataDir := osenv.JujuHomePath("")
118 code := cmd.Main(116 code := cmd.Main(
119 &ValidateToolsMetadataCommand{}, ctx, []string{"-e", "ec2", "-j", "1.11.4", "-d", metadataDir},117 &ValidateToolsMetadataCommand{}, ctx, []string{"-e", "ec2", "-j", "1.11.4", "-d", s.metadataDir},
120 )118 )
121 c.Assert(code, gc.Equals, 0)119 c.Assert(code, gc.Equals, 0)
122 errOut := ctx.Stdout.(*bytes.Buffer).String()120 errOut := ctx.Stdout.(*bytes.Buffer).String()
123 strippedOut := strings.Replace(errOut, "\n", "", -1)121 strippedOut := strings.Replace(errOut, "\n", "", -1)
124 c.Check(strippedOut, gc.Matches, `matching tools versions:.*`)122 c.Check(strippedOut, gc.Matches, `Matching Tools Versions:.*Resolve Metadata.*`)
125}123}
126124
127func (s *ValidateToolsMetadataSuite) TestEc2LocalMetadataUsingIncompleteEnvironment(c *gc.C) {125func (s *ValidateToolsMetadataSuite) TestEc2LocalMetadataUsingIncompleteEnvironment(c *gc.C) {
@@ -141,123 +139,121 @@
141func (s *ValidateToolsMetadataSuite) TestEc2LocalMetadataWithManualParams(c *gc.C) {139func (s *ValidateToolsMetadataSuite) TestEc2LocalMetadataWithManualParams(c *gc.C) {
142 s.setupEc2LocalMetadata(c, "us-west-1")140 s.setupEc2LocalMetadata(c, "us-west-1")
143 ctx := coretesting.Context(c)141 ctx := coretesting.Context(c)
144 metadataDir := osenv.JujuHomePath("")
145 code := cmd.Main(142 code := cmd.Main(
146 &ValidateToolsMetadataCommand{}, ctx, []string{143 &ValidateToolsMetadataCommand{}, ctx, []string{
147 "-p", "ec2", "-s", "precise", "-r", "us-west-1", "-j", "1.11.4",144 "-p", "ec2", "-s", "precise", "-r", "us-west-1", "-j", "1.11.4",
148 "-u", "https://ec2.us-west-1.amazonaws.com", "-d", metadataDir},145 "-u", "https://ec2.us-west-1.amazonaws.com", "-d", s.metadataDir},
149 )146 )
150 c.Assert(code, gc.Equals, 0)147 c.Assert(code, gc.Equals, 0)
151 errOut := ctx.Stdout.(*bytes.Buffer).String()148 errOut := ctx.Stdout.(*bytes.Buffer).String()
152 strippedOut := strings.Replace(errOut, "\n", "", -1)149 strippedOut := strings.Replace(errOut, "\n", "", -1)
153 c.Check(strippedOut, gc.Matches, `matching tools versions:.*`)150 c.Check(strippedOut, gc.Matches, `Matching Tools Versions:.*Resolve Metadata.*`)
154}151}
155152
156func (s *ValidateToolsMetadataSuite) TestEc2LocalMetadataNoMatch(c *gc.C) {153func (s *ValidateToolsMetadataSuite) TestEc2LocalMetadataNoMatch(c *gc.C) {
157 s.setupEc2LocalMetadata(c, "us-east-1")154 s.setupEc2LocalMetadata(c, "us-east-1")
158 ctx := coretesting.Context(c)155 ctx := coretesting.Context(c)
159 metadataDir := osenv.JujuHomePath("")
160 code := cmd.Main(156 code := cmd.Main(
161 &ValidateToolsMetadataCommand{}, ctx, []string{157 &ValidateToolsMetadataCommand{}, ctx, []string{
162 "-p", "ec2", "-s", "raring", "-r", "us-west-1",158 "-p", "ec2", "-s", "raring", "-r", "us-west-1",
163 "-u", "https://ec2.us-west-1.amazonaws.com", "-d", metadataDir},159 "-u", "https://ec2.us-west-1.amazonaws.com", "-d", s.metadataDir},
164 )160 )
165 c.Assert(code, gc.Equals, 1)161 c.Assert(code, gc.Equals, 1)
166 code = cmd.Main(162 code = cmd.Main(
167 &ValidateToolsMetadataCommand{}, ctx, []string{163 &ValidateToolsMetadataCommand{}, ctx, []string{
168 "-p", "ec2", "-s", "precise", "-r", "region",164 "-p", "ec2", "-s", "precise", "-r", "region",
169 "-u", "https://ec2.region.amazonaws.com", "-d", metadataDir},165 "-u", "https://ec2.region.amazonaws.com", "-d", s.metadataDir},
170 )166 )
171 c.Assert(code, gc.Equals, 1)167 c.Assert(code, gc.Equals, 1)
168 errOut := ctx.Stderr.(*bytes.Buffer).String()
169 strippedOut := strings.Replace(errOut, "\n", "", -1)
170 c.Check(strippedOut, gc.Matches, `.*Resolve Metadata:.*`)
172}171}
173172
174func (s *ValidateToolsMetadataSuite) TestOpenstackLocalMetadataWithManualParams(c *gc.C) {173func (s *ValidateToolsMetadataSuite) TestOpenstackLocalMetadataWithManualParams(c *gc.C) {
175 s.makeLocalMetadata(c, "1.11.4", "region-2", "raring", "some-auth-url")174 s.makeLocalMetadata(c, "1.11.4", "region-2", "raring", "some-auth-url")
176 ctx := coretesting.Context(c)175 ctx := coretesting.Context(c)
177 metadataDir := osenv.JujuHomePath("")
178 code := cmd.Main(176 code := cmd.Main(
179 &ValidateToolsMetadataCommand{}, ctx, []string{177 &ValidateToolsMetadataCommand{}, ctx, []string{
180 "-p", "openstack", "-s", "raring", "-r", "region-2", "-j", "1.11.4",178 "-p", "openstack", "-s", "raring", "-r", "region-2", "-j", "1.11.4",
181 "-u", "some-auth-url", "-d", metadataDir},179 "-u", "some-auth-url", "-d", s.metadataDir},
182 )180 )
183 c.Assert(code, gc.Equals, 0)181 c.Assert(code, gc.Equals, 0)
184 errOut := ctx.Stdout.(*bytes.Buffer).String()182 errOut := ctx.Stdout.(*bytes.Buffer).String()
185 strippedOut := strings.Replace(errOut, "\n", "", -1)183 strippedOut := strings.Replace(errOut, "\n", "", -1)
186 c.Check(strippedOut, gc.Matches, `matching tools versions:.*`)184 c.Check(strippedOut, gc.Matches, `Matching Tools Versions:.*Resolve Metadata.*`)
187}185}
188186
189func (s *ValidateToolsMetadataSuite) TestOpenstackLocalMetadataNoMatch(c *gc.C) {187func (s *ValidateToolsMetadataSuite) TestOpenstackLocalMetadataNoMatch(c *gc.C) {
190 s.makeLocalMetadata(c, "1.11.4", "region-2", "raring", "some-auth-url")188 s.makeLocalMetadata(c, "1.11.4", "region-2", "raring", "some-auth-url")
191 ctx := coretesting.Context(c)189 ctx := coretesting.Context(c)
192 metadataDir := osenv.JujuHomePath("")
193 code := cmd.Main(190 code := cmd.Main(
194 &ValidateToolsMetadataCommand{}, ctx, []string{191 &ValidateToolsMetadataCommand{}, ctx, []string{
195 "-p", "openstack", "-s", "precise", "-r", "region-2",192 "-p", "openstack", "-s", "precise", "-r", "region-2",
196 "-u", "some-auth-url", "-d", metadataDir},193 "-u", "some-auth-url", "-d", s.metadataDir},
197 )194 )
198 c.Assert(code, gc.Equals, 1)195 c.Assert(code, gc.Equals, 1)
199 code = cmd.Main(196 code = cmd.Main(
200 &ValidateToolsMetadataCommand{}, ctx, []string{197 &ValidateToolsMetadataCommand{}, ctx, []string{
201 "-p", "openstack", "-s", "raring", "-r", "region-3",198 "-p", "openstack", "-s", "raring", "-r", "region-3",
202 "-u", "some-auth-url", "-d", metadataDir},199 "-u", "some-auth-url", "-d", s.metadataDir},
203 )200 )
204 c.Assert(code, gc.Equals, 1)201 c.Assert(code, gc.Equals, 1)
202 errOut := ctx.Stderr.(*bytes.Buffer).String()
203 strippedOut := strings.Replace(errOut, "\n", "", -1)
204 c.Check(strippedOut, gc.Matches, `.*Resolve Metadata:.*`)
205}205}
206206
207func (s *ValidateToolsMetadataSuite) TestDefaultVersion(c *gc.C) {207func (s *ValidateToolsMetadataSuite) TestDefaultVersion(c *gc.C) {
208 s.makeLocalMetadata(c, version.Current.Number.String(), "region-2", "raring", "some-auth-url")208 s.makeLocalMetadata(c, version.Current.Number.String(), "region-2", "raring", "some-auth-url")
209 ctx := coretesting.Context(c)209 ctx := coretesting.Context(c)
210 metadataDir := osenv.JujuHomePath("")
211 code := cmd.Main(210 code := cmd.Main(
212 &ValidateToolsMetadataCommand{}, ctx, []string{211 &ValidateToolsMetadataCommand{}, ctx, []string{
213 "-p", "openstack", "-s", "raring", "-r", "region-2",212 "-p", "openstack", "-s", "raring", "-r", "region-2",
214 "-u", "some-auth-url", "-d", metadataDir},213 "-u", "some-auth-url", "-d", s.metadataDir},
215 )214 )
216 c.Assert(code, gc.Equals, 0)215 c.Assert(code, gc.Equals, 0)
217 errOut := ctx.Stdout.(*bytes.Buffer).String()216 errOut := ctx.Stdout.(*bytes.Buffer).String()
218 strippedOut := strings.Replace(errOut, "\n", "", -1)217 strippedOut := strings.Replace(errOut, "\n", "", -1)
219 c.Check(strippedOut, gc.Matches, `matching tools versions:.*`)218 c.Check(strippedOut, gc.Matches, `Matching Tools Versions:.*Resolve Metadata.*`)
220}219}
221220
222func (s *ValidateToolsMetadataSuite) TestMajorVersionMatch(c *gc.C) {221func (s *ValidateToolsMetadataSuite) TestMajorVersionMatch(c *gc.C) {
223 s.makeLocalMetadata(c, "1.11.4", "region-2", "raring", "some-auth-url")222 s.makeLocalMetadata(c, "1.11.4", "region-2", "raring", "some-auth-url")
224 ctx := coretesting.Context(c)223 ctx := coretesting.Context(c)
225 metadataDir := osenv.JujuHomePath("")
226 code := cmd.Main(224 code := cmd.Main(
227 &ValidateToolsMetadataCommand{}, ctx, []string{225 &ValidateToolsMetadataCommand{}, ctx, []string{
228 "-p", "openstack", "-s", "raring", "-r", "region-2",226 "-p", "openstack", "-s", "raring", "-r", "region-2",
229 "-u", "some-auth-url", "-d", metadataDir, "-m", "1"},227 "-u", "some-auth-url", "-d", s.metadataDir, "-m", "1"},
230 )228 )
231 c.Assert(code, gc.Equals, 0)229 c.Assert(code, gc.Equals, 0)
232 errOut := ctx.Stdout.(*bytes.Buffer).String()230 errOut := ctx.Stdout.(*bytes.Buffer).String()
233 strippedOut := strings.Replace(errOut, "\n", "", -1)231 strippedOut := strings.Replace(errOut, "\n", "", -1)
234 c.Check(strippedOut, gc.Matches, `matching tools versions:.*`)232 c.Check(strippedOut, gc.Matches, `Matching Tools Versions:.*Resolve Metadata.*`)
235}233}
236234
237func (s *ValidateToolsMetadataSuite) TestMajorMinorVersionMatch(c *gc.C) {235func (s *ValidateToolsMetadataSuite) TestMajorMinorVersionMatch(c *gc.C) {
238 s.makeLocalMetadata(c, "1.12.1", "region-2", "raring", "some-auth-url")236 s.makeLocalMetadata(c, "1.12.1", "region-2", "raring", "some-auth-url")
239 ctx := coretesting.Context(c)237 ctx := coretesting.Context(c)
240 metadataDir := osenv.JujuHomePath("")
241 code := cmd.Main(238 code := cmd.Main(
242 &ValidateToolsMetadataCommand{}, ctx, []string{239 &ValidateToolsMetadataCommand{}, ctx, []string{
243 "-p", "openstack", "-s", "raring", "-r", "region-2",240 "-p", "openstack", "-s", "raring", "-r", "region-2",
244 "-u", "some-auth-url", "-d", metadataDir, "-m", "1.12"},241 "-u", "some-auth-url", "-d", s.metadataDir, "-m", "1.12"},
245 )242 )
246 c.Assert(code, gc.Equals, 0)243 c.Assert(code, gc.Equals, 0)
247 errOut := ctx.Stdout.(*bytes.Buffer).String()244 errOut := ctx.Stdout.(*bytes.Buffer).String()
248 strippedOut := strings.Replace(errOut, "\n", "", -1)245 strippedOut := strings.Replace(errOut, "\n", "", -1)
249 c.Check(strippedOut, gc.Matches, `matching tools versions:.*`)246 c.Check(strippedOut, gc.Matches, `Matching Tools Versions:.*Resolve Metadata.*`)
250}247}
251248
252func (s *ValidateToolsMetadataSuite) TestJustDirectory(c *gc.C) {249func (s *ValidateToolsMetadataSuite) TestJustDirectory(c *gc.C) {
253 s.makeLocalMetadata(c, version.Current.Number.String(), "region-2", "raring", "some-auth-url")250 s.makeLocalMetadata(c, version.Current.Number.String(), "region-2", "raring", "some-auth-url")
254 ctx := coretesting.Context(c)251 ctx := coretesting.Context(c)
255 metadataDir := osenv.JujuHomePath("")
256 code := cmd.Main(252 code := cmd.Main(
257 &ValidateToolsMetadataCommand{}, ctx, []string{"-s", "raring", "-d", metadataDir},253 &ValidateToolsMetadataCommand{}, ctx, []string{"-s", "raring", "-d", s.metadataDir},
258 )254 )
259 c.Assert(code, gc.Equals, 0)255 c.Assert(code, gc.Equals, 0)
260 errOut := ctx.Stdout.(*bytes.Buffer).String()256 errOut := ctx.Stdout.(*bytes.Buffer).String()
261 strippedOut := strings.Replace(errOut, "\n", "", -1)257 strippedOut := strings.Replace(errOut, "\n", "", -1)
262 c.Check(strippedOut, gc.Matches, `matching tools versions:.*`)258 c.Check(strippedOut, gc.Matches, `Matching Tools Versions:.*Resolve Metadata.*`)
263}259}
264260
=== modified file 'environs/bootstrap/bootstrap_test.go'
--- environs/bootstrap/bootstrap_test.go 2014-02-13 02:46:58 +0000
+++ environs/bootstrap/bootstrap_test.go 2014-03-04 05:51:00 +0000
@@ -218,7 +218,8 @@
218// GetToolsSources returns a list of sources which are used to search for simplestreams tools metadata.218// GetToolsSources returns a list of sources which are used to search for simplestreams tools metadata.
219func (e *bootstrapEnviron) GetToolsSources() ([]simplestreams.DataSource, error) {219func (e *bootstrapEnviron) GetToolsSources() ([]simplestreams.DataSource, error) {
220 // Add the simplestreams source off the control bucket.220 // Add the simplestreams source off the control bucket.
221 return []simplestreams.DataSource{storage.NewStorageSimpleStreamsDataSource(e.Storage(), storage.BaseToolsPath)}, nil221 return []simplestreams.DataSource{
222 storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseToolsPath)}, nil
222}223}
223224
224func newEnviron(name string, defaultKeys bool) *bootstrapEnviron {225func newEnviron(name string, defaultKeys bool) *bootstrapEnviron {
225226
=== modified file 'environs/imagemetadata/generate.go'
--- environs/imagemetadata/generate.go 2014-01-21 22:26:08 +0000
+++ environs/imagemetadata/generate.go 2014-03-04 05:51:00 +0000
@@ -34,9 +34,9 @@
34// readMetadata reads the image metadata from metadataStore.34// readMetadata reads the image metadata from metadataStore.
35func readMetadata(metadataStore storage.Storage) ([]*ImageMetadata, error) {35func readMetadata(metadataStore storage.Storage) ([]*ImageMetadata, error) {
36 // Read any existing metadata so we can merge the new tools metadata with what's there.36 // Read any existing metadata so we can merge the new tools metadata with what's there.
37 dataSource := storage.NewStorageSimpleStreamsDataSource(metadataStore, storage.BaseImagesPath)37 dataSource := storage.NewStorageSimpleStreamsDataSource("existing metadata", metadataStore, storage.BaseImagesPath)
38 imageConstraint := NewImageConstraint(simplestreams.LookupParams{})38 imageConstraint := NewImageConstraint(simplestreams.LookupParams{})
39 existingMetadata, err := Fetch(39 existingMetadata, _, err := Fetch(
40 []simplestreams.DataSource{dataSource}, simplestreams.DefaultIndexPath, imageConstraint, false)40 []simplestreams.DataSource{dataSource}, simplestreams.DefaultIndexPath, imageConstraint, false)
41 if err != nil && !errors.IsNotFoundError(err) {41 if err != nil && !errors.IsNotFoundError(err) {
42 return nil, err42 return nil, err
4343
=== modified file 'environs/imagemetadata/generate_test.go'
--- environs/imagemetadata/generate_test.go 2014-01-21 22:26:08 +0000
+++ environs/imagemetadata/generate_test.go 2014-03-04 05:51:00 +0000
@@ -28,8 +28,8 @@
28 Series: []string{series},28 Series: []string{series},
29 Arches: []string{arch},29 Arches: []string{arch},
30 })30 })
31 dataSource := storage.NewStorageSimpleStreamsDataSource(stor, "images")31 dataSource := storage.NewStorageSimpleStreamsDataSource("test datasource", stor, "images")
32 metadata, err := imagemetadata.Fetch(32 metadata, _, err := imagemetadata.Fetch(
33 []simplestreams.DataSource{dataSource}, simplestreams.DefaultIndexPath, cons, false)33 []simplestreams.DataSource{dataSource}, simplestreams.DefaultIndexPath, cons, false)
34 c.Assert(err, gc.IsNil)34 c.Assert(err, gc.IsNil)
35 c.Assert(metadata, gc.HasLen, 1)35 c.Assert(metadata, gc.HasLen, 1)
3636
=== modified file 'environs/imagemetadata/simplestreams.go'
--- environs/imagemetadata/simplestreams.go 2014-02-28 03:05:47 +0000
+++ environs/imagemetadata/simplestreams.go 2014-03-04 05:51:00 +0000
@@ -164,22 +164,24 @@
164// The base URL locations are as specified - the first location which has a file is the one used.164// The base URL locations are as specified - the first location which has a file is the one used.
165// Signed data is preferred, but if there is no signed data available and onlySigned is false,165// Signed data is preferred, but if there is no signed data available and onlySigned is false,
166// then unsigned data is used.166// then unsigned data is used.
167func Fetch(sources []simplestreams.DataSource, indexPath string, cons *ImageConstraint, onlySigned bool) ([]*ImageMetadata, error) {167func Fetch(
168 sources []simplestreams.DataSource, indexPath string, cons *ImageConstraint,
169 onlySigned bool) ([]*ImageMetadata, *simplestreams.ResolveInfo, error) {
168 params := simplestreams.ValueParams{170 params := simplestreams.ValueParams{
169 DataType: ImageIds,171 DataType: ImageIds,
170 FilterFunc: appendMatchingImages,172 FilterFunc: appendMatchingImages,
171 ValueTemplate: ImageMetadata{},173 ValueTemplate: ImageMetadata{},
172 PublicKey: simplestreamsImagesPublicKey,174 PublicKey: simplestreamsImagesPublicKey,
173 }175 }
174 items, err := simplestreams.GetMetadata(sources, indexPath, cons, onlySigned, params)176 items, resolveInfo, err := simplestreams.GetMetadata(sources, indexPath, cons, onlySigned, params)
175 if err != nil {177 if err != nil {
176 return nil, err178 return nil, resolveInfo, err
177 }179 }
178 metadata := make([]*ImageMetadata, len(items))180 metadata := make([]*ImageMetadata, len(items))
179 for i, md := range items {181 for i, md := range items {
180 metadata[i] = md.(*ImageMetadata)182 metadata[i] = md.(*ImageMetadata)
181 }183 }
182 return metadata, nil184 return metadata, resolveInfo, nil
183}185}
184186
185type imageKey struct {187type imageKey struct {
186188
=== modified file 'environs/imagemetadata/simplestreams_test.go'
--- environs/imagemetadata/simplestreams_test.go 2014-01-29 00:14:51 +0000
+++ environs/imagemetadata/simplestreams_test.go 2014-03-04 05:51:00 +0000
@@ -66,7 +66,8 @@
66func registerSimpleStreamsTests() {66func registerSimpleStreamsTests() {
67 gc.Suite(&simplestreamsSuite{67 gc.Suite(&simplestreamsSuite{
68 LocalLiveSimplestreamsSuite: sstesting.LocalLiveSimplestreamsSuite{68 LocalLiveSimplestreamsSuite: sstesting.LocalLiveSimplestreamsSuite{
69 Source: simplestreams.NewURLDataSource("test:", simplestreams.VerifySSLHostnames),69 Source: simplestreams.NewURLDataSource(
70 "test roundtripper", "test:", simplestreams.VerifySSLHostnames),
70 RequireSigned: false,71 RequireSigned: false,
71 DataType: imagemetadata.ImageIds,72 DataType: imagemetadata.ImageIds,
72 ValidConstraint: imagemetadata.NewImageConstraint(simplestreams.LookupParams{73 ValidConstraint: imagemetadata.NewImageConstraint(simplestreams.LookupParams{
@@ -84,7 +85,7 @@
8485
85func registerLiveSimpleStreamsTests(baseURL string, validImageConstraint simplestreams.LookupConstraint, requireSigned bool) {86func registerLiveSimpleStreamsTests(baseURL string, validImageConstraint simplestreams.LookupConstraint, requireSigned bool) {
86 gc.Suite(&sstesting.LocalLiveSimplestreamsSuite{87 gc.Suite(&sstesting.LocalLiveSimplestreamsSuite{
87 Source: simplestreams.NewURLDataSource(baseURL, simplestreams.VerifySSLHostnames),88 Source: simplestreams.NewURLDataSource("test", baseURL, simplestreams.VerifySSLHostnames),
88 RequireSigned: requireSigned,89 RequireSigned: requireSigned,
89 DataType: imagemetadata.ImageIds,90 DataType: imagemetadata.ImageIds,
90 ValidConstraint: validImageConstraint,91 ValidConstraint: validImageConstraint,
@@ -265,7 +266,11 @@
265 Series: []string{"precise"},266 Series: []string{"precise"},
266 Arches: t.arches,267 Arches: t.arches,
267 })268 })
268 images, err := imagemetadata.Fetch([]simplestreams.DataSource{s.Source}, simplestreams.DefaultIndexPath, imageConstraint, s.RequireSigned)269 // Add invalid datasource and check later that resolveInfo is correct.
270 invalidSource := simplestreams.NewURLDataSource("invalid", "file://invalid", simplestreams.VerifySSLHostnames)
271 images, resolveInfo, err := imagemetadata.Fetch(
272 []simplestreams.DataSource{invalidSource, s.Source}, simplestreams.DefaultIndexPath,
273 imageConstraint, s.RequireSigned)
269 if !c.Check(err, gc.IsNil) {274 if !c.Check(err, gc.IsNil) {
270 continue275 continue
271 }276 }
@@ -273,6 +278,12 @@
273 testImage.Version = t.version278 testImage.Version = t.version
274 }279 }
275 c.Check(images, gc.DeepEquals, t.images)280 c.Check(images, gc.DeepEquals, t.images)
281 c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{
282 Source: "test roundtripper",
283 Signed: s.RequireSigned,
284 IndexURL: "test:/streams/v1/index.json",
285 MirrorURL: "",
286 })
276 }287 }
277}288}
278289
@@ -364,28 +375,34 @@
364}375}
365376
366func (s *signedSuite) TestSignedImageMetadata(c *gc.C) {377func (s *signedSuite) TestSignedImageMetadata(c *gc.C) {
367 signedSource := simplestreams.NewURLDataSource("signedtest://host/signed", simplestreams.VerifySSLHostnames)378 signedSource := simplestreams.NewURLDataSource("test", "signedtest://host/signed", simplestreams.VerifySSLHostnames)
368 imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{379 imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{
369 CloudSpec: simplestreams.CloudSpec{"us-east-1", "https://ec2.us-east-1.amazonaws.com"},380 CloudSpec: simplestreams.CloudSpec{"us-east-1", "https://ec2.us-east-1.amazonaws.com"},
370 Series: []string{"precise"},381 Series: []string{"precise"},
371 Arches: []string{"amd64"},382 Arches: []string{"amd64"},
372 })383 })
373 images, err := imagemetadata.Fetch(384 images, resolveInfo, err := imagemetadata.Fetch(
374 []simplestreams.DataSource{signedSource}, simplestreams.DefaultIndexPath, imageConstraint, true)385 []simplestreams.DataSource{signedSource}, simplestreams.DefaultIndexPath, imageConstraint, true)
375 c.Assert(err, gc.IsNil)386 c.Assert(err, gc.IsNil)
376 c.Assert(len(images), gc.Equals, 1)387 c.Assert(len(images), gc.Equals, 1)
377 c.Assert(images[0].Id, gc.Equals, "ami-123456")388 c.Assert(images[0].Id, gc.Equals, "ami-123456")
389 c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{
390 Source: "test",
391 Signed: true,
392 IndexURL: "signedtest://host/signed/streams/v1/index.sjson",
393 MirrorURL: "",
394 })
378}395}
379396
380func (s *signedSuite) TestSignedImageMetadataInvalidSignature(c *gc.C) {397func (s *signedSuite) TestSignedImageMetadataInvalidSignature(c *gc.C) {
381 signedSource := simplestreams.NewURLDataSource("signedtest://host/signed", simplestreams.VerifySSLHostnames)398 signedSource := simplestreams.NewURLDataSource("test", "signedtest://host/signed", simplestreams.VerifySSLHostnames)
382 imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{399 imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{
383 CloudSpec: simplestreams.CloudSpec{"us-east-1", "https://ec2.us-east-1.amazonaws.com"},400 CloudSpec: simplestreams.CloudSpec{"us-east-1", "https://ec2.us-east-1.amazonaws.com"},
384 Series: []string{"precise"},401 Series: []string{"precise"},
385 Arches: []string{"amd64"},402 Arches: []string{"amd64"},
386 })403 })
387 imagemetadata.SetSigningPublicKey(s.origKey)404 imagemetadata.SetSigningPublicKey(s.origKey)
388 _, err := imagemetadata.Fetch(405 _, _, err := imagemetadata.Fetch(
389 []simplestreams.DataSource{signedSource}, simplestreams.DefaultIndexPath, imageConstraint, true)406 []simplestreams.DataSource{signedSource}, simplestreams.DefaultIndexPath, imageConstraint, true)
390 c.Assert(err, gc.ErrorMatches, "cannot read index data.*")407 c.Assert(err, gc.ErrorMatches, "cannot read index data.*")
391}408}
392409
=== modified file 'environs/imagemetadata/testing/testing.go'
--- environs/imagemetadata/testing/testing.go 2014-01-21 22:26:08 +0000
+++ environs/imagemetadata/testing/testing.go 2014-03-04 05:51:00 +0000
@@ -27,7 +27,7 @@
2727
28// ParseMetadataFromStorage loads ImageMetadata from the specified storage reader.28// ParseMetadataFromStorage loads ImageMetadata from the specified storage reader.
29func ParseMetadataFromStorage(c *gc.C, stor storage.StorageReader) []*imagemetadata.ImageMetadata {29func ParseMetadataFromStorage(c *gc.C, stor storage.StorageReader) []*imagemetadata.ImageMetadata {
30 source := storage.NewStorageSimpleStreamsDataSource(stor, "images")30 source := storage.NewStorageSimpleStreamsDataSource("test storage reader", stor, "images")
3131
32 // Find the simplestreams index file.32 // Find the simplestreams index file.
33 params := simplestreams.ValueParams{33 params := simplestreams.ValueParams{
3434
=== modified file 'environs/imagemetadata/urls.go'
--- environs/imagemetadata/urls.go 2014-01-29 06:45:16 +0000
+++ environs/imagemetadata/urls.go 2014-03-04 05:51:00 +0000
@@ -31,7 +31,7 @@
31 if !config.SSLHostnameVerification() {31 if !config.SSLHostnameVerification() {
32 verify = simplestreams.NoVerifySSLHostnames32 verify = simplestreams.NoVerifySSLHostnames
33 }33 }
34 sources = append(sources, simplestreams.NewURLDataSource(userURL, verify))34 sources = append(sources, simplestreams.NewURLDataSource("image-metadata-url", userURL, verify))
35 }35 }
36 if custom, ok := env.(SupportsCustomSources); ok {36 if custom, ok := env.(SupportsCustomSources); ok {
37 customSources, err := custom.GetImageSources()37 customSources, err := custom.GetImageSources()
@@ -46,7 +46,7 @@
46 return nil, err46 return nil, err
47 }47 }
48 if defaultURL != "" {48 if defaultURL != "" {
49 sources = append(sources, simplestreams.NewURLDataSource(defaultURL, simplestreams.VerifySSLHostnames))49 sources = append(sources, simplestreams.NewURLDataSource("default cloud images", defaultURL, simplestreams.VerifySSLHostnames))
50 }50 }
51 return sources, nil51 return sources, nil
52}52}
5353
=== modified file 'environs/imagemetadata/validation.go'
--- environs/imagemetadata/validation.go 2014-01-29 06:45:16 +0000
+++ environs/imagemetadata/validation.go 2014-03-04 05:51:00 +0000
@@ -11,21 +11,21 @@
1111
12// ValidateImageMetadata attempts to load image metadata for the specified cloud attributes and stream12// ValidateImageMetadata attempts to load image metadata for the specified cloud attributes and stream
13// and returns any image ids found, or an error if the metadata could not be loaded.13// and returns any image ids found, or an error if the metadata could not be loaded.
14func ValidateImageMetadata(params *simplestreams.MetadataLookupParams) ([]string, error) {14func ValidateImageMetadata(params *simplestreams.MetadataLookupParams) ([]string, *simplestreams.ResolveInfo, error) {
15 if params.Series == "" {15 if params.Series == "" {
16 return nil, fmt.Errorf("required parameter series not specified")16 return nil, nil, fmt.Errorf("required parameter series not specified")
17 }17 }
18 if params.Region == "" {18 if params.Region == "" {
19 return nil, fmt.Errorf("required parameter region not specified")19 return nil, nil, fmt.Errorf("required parameter region not specified")
20 }20 }
21 if params.Endpoint == "" {21 if params.Endpoint == "" {
22 return nil, fmt.Errorf("required parameter endpoint not specified")22 return nil, nil, fmt.Errorf("required parameter endpoint not specified")
23 }23 }
24 if len(params.Architectures) == 0 {24 if len(params.Architectures) == 0 {
25 return nil, fmt.Errorf("required parameter arches not specified")25 return nil, nil, fmt.Errorf("required parameter arches not specified")
26 }26 }
27 if len(params.Sources) == 0 {27 if len(params.Sources) == 0 {
28 return nil, fmt.Errorf("required parameter sources not specified")28 return nil, nil, fmt.Errorf("required parameter sources not specified")
29 }29 }
30 imageConstraint := NewImageConstraint(simplestreams.LookupParams{30 imageConstraint := NewImageConstraint(simplestreams.LookupParams{
31 CloudSpec: simplestreams.CloudSpec{params.Region, params.Endpoint},31 CloudSpec: simplestreams.CloudSpec{params.Region, params.Endpoint},
@@ -33,16 +33,16 @@
33 Arches: params.Architectures,33 Arches: params.Architectures,
34 Stream: params.Stream,34 Stream: params.Stream,
35 })35 })
36 matchingImages, err := Fetch(params.Sources, simplestreams.DefaultIndexPath, imageConstraint, false)36 matchingImages, resolveInfo, err := Fetch(params.Sources, simplestreams.DefaultIndexPath, imageConstraint, false)
37 if err != nil {37 if err != nil {
38 return nil, err38 return nil, resolveInfo, err
39 }39 }
40 if len(matchingImages) == 0 {40 if len(matchingImages) == 0 {
41 return nil, fmt.Errorf("no matching images found for constraint %+v", imageConstraint)41 return nil, resolveInfo, fmt.Errorf("no matching images found for constraint %+v", imageConstraint)
42 }42 }
43 image_ids := make([]string, len(matchingImages))43 image_ids := make([]string, len(matchingImages))
44 for i, im := range matchingImages {44 for i, im := range matchingImages {
45 image_ids[i] = im.Id45 image_ids[i] = im.Id
46 }46 }
47 return image_ids, nil47 return image_ids, resolveInfo, nil
48}48}
4949
=== modified file 'environs/imagemetadata/validation_test.go'
--- environs/imagemetadata/validation_test.go 2014-01-29 06:45:16 +0000
+++ environs/imagemetadata/validation_test.go 2014-03-04 05:51:00 +0000
@@ -4,6 +4,7 @@
4package imagemetadata_test4package imagemetadata_test
55
6import (6import (
7 "path"
7 "path/filepath"8 "path/filepath"
89
9 gc "launchpad.net/gocheck"10 gc "launchpad.net/gocheck"
@@ -57,11 +58,17 @@
57 Endpoint: "some-auth-url",58 Endpoint: "some-auth-url",
58 Stream: stream,59 Stream: stream,
59 Sources: []simplestreams.DataSource{60 Sources: []simplestreams.DataSource{
60 simplestreams.NewURLDataSource("file://"+metadataPath, simplestreams.VerifySSLHostnames)},61 simplestreams.NewURLDataSource("test", "file://"+metadataPath, simplestreams.VerifySSLHostnames)},
61 }62 }
62 imageIds, err := imagemetadata.ValidateImageMetadata(params)63 imageIds, resolveInfo, err := imagemetadata.ValidateImageMetadata(params)
63 c.Assert(err, gc.IsNil)64 c.Assert(err, gc.IsNil)
64 c.Assert(imageIds, gc.DeepEquals, []string{"1234"})65 c.Assert(imageIds, gc.DeepEquals, []string{"1234"})
66 c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{
67 Source: "test",
68 Signed: false,
69 IndexURL: "file://" + path.Join(metadataPath, "streams/v1/index.json"),
70 MirrorURL: "",
71 })
65}72}
6673
67func (s *ValidateSuite) TestMatch(c *gc.C) {74func (s *ValidateSuite) TestMatch(c *gc.C) {
@@ -79,9 +86,9 @@
79 Endpoint: "some-auth-url",86 Endpoint: "some-auth-url",
80 Stream: stream,87 Stream: stream,
81 Sources: []simplestreams.DataSource{88 Sources: []simplestreams.DataSource{
82 simplestreams.NewURLDataSource("file://"+s.metadataDir, simplestreams.VerifySSLHostnames)},89 simplestreams.NewURLDataSource("test", "file://"+s.metadataDir, simplestreams.VerifySSLHostnames)},
83 }90 }
84 _, err := imagemetadata.ValidateImageMetadata(params)91 _, _, err := imagemetadata.ValidateImageMetadata(params)
85 c.Assert(err, gc.Not(gc.IsNil))92 c.Assert(err, gc.Not(gc.IsNil))
86}93}
8794
8895
=== modified file 'environs/instances/image_test.go'
--- environs/instances/image_test.go 2014-01-29 00:14:51 +0000
+++ environs/instances/image_test.go 2014-03-04 05:51:00 +0000
@@ -240,7 +240,8 @@
240 Stream: t.stream,240 Stream: t.stream,
241 })241 })
242 imageMeta, err := imagemetadata.GetLatestImageIdMetadata(242 imageMeta, err := imagemetadata.GetLatestImageIdMetadata(
243 []byte(jsonImagesContent), simplestreams.NewURLDataSource("some-url", simplestreams.VerifySSLHostnames), cons)243 []byte(jsonImagesContent),
244 simplestreams.NewURLDataSource("test", "some-url", simplestreams.VerifySSLHostnames), cons)
244 c.Assert(err, gc.IsNil)245 c.Assert(err, gc.IsNil)
245 var images []Image246 var images []Image
246 for _, imageMetadata := range imageMeta {247 for _, imageMetadata := range imageMeta {
247248
=== modified file 'environs/simplestreams/datasource.go'
--- environs/simplestreams/datasource.go 2013-10-17 22:48:19 +0000
+++ environs/simplestreams/datasource.go 2014-03-04 05:51:00 +0000
@@ -15,6 +15,9 @@
1515
16// A DataSource retrieves simplestreams metadata.16// A DataSource retrieves simplestreams metadata.
17type DataSource interface {17type DataSource interface {
18 // Description describes the origin of this datasource.
19 // eg tools-metadata-url, cloud storage, keystone catalog etc.
20 Description() string
18 // Fetch loads the data at the specified relative path. It returns a reader from which21 // Fetch loads the data at the specified relative path. It returns a reader from which
19 // the data can be retrieved as well as the full URL of the file. The full URL is typically22 // the data can be retrieved as well as the full URL of the file. The full URL is typically
20 // used in log messages to help diagnose issues accessing the data.23 // used in log messages to help diagnose issues accessing the data.
@@ -43,20 +46,27 @@
4346
44// A urlDataSource retrieves data from an HTTP URL.47// A urlDataSource retrieves data from an HTTP URL.
45type urlDataSource struct {48type urlDataSource struct {
49 description string
46 baseURL string50 baseURL string
47 hostnameVerification SSLHostnameVerification51 hostnameVerification SSLHostnameVerification
48}52}
4953
50// NewURLDataSource returns a new datasource reading from the specified baseURL.54// NewURLDataSource returns a new datasource reading from the specified baseURL.
51func NewURLDataSource(baseURL string, verify SSLHostnameVerification) DataSource {55func NewURLDataSource(description, baseURL string, verify SSLHostnameVerification) DataSource {
52 return &urlDataSource{56 return &urlDataSource{
57 description: description,
53 baseURL: baseURL,58 baseURL: baseURL,
54 hostnameVerification: verify,59 hostnameVerification: verify,
55 }60 }
56}61}
5762
63// Description is defined in simplestreams.DataSource.
64func (u *urlDataSource) Description() string {
65 return u.description
66}
67
58func (u *urlDataSource) GoString() string {68func (u *urlDataSource) GoString() string {
59 return fmt.Sprintf("urlDataSource(%q)", u.baseURL)69 return fmt.Sprintf("%v: urlDataSource(%q)", u.description, u.baseURL)
60}70}
6171
62// urlJoin returns baseURL + relpath making sure to have a '/' inbetween them72// urlJoin returns baseURL + relpath making sure to have a '/' inbetween them
6373
=== modified file 'environs/simplestreams/datasource_test.go'
--- environs/simplestreams/datasource_test.go 2013-09-17 09:18:36 +0000
+++ environs/simplestreams/datasource_test.go 2014-03-04 05:51:00 +0000
@@ -24,7 +24,7 @@
24}24}
2525
26func (s *datasourceSuite) TestFetch(c *gc.C) {26func (s *datasourceSuite) TestFetch(c *gc.C) {
27 ds := simplestreams.NewURLDataSource("test:", simplestreams.VerifySSLHostnames)27 ds := simplestreams.NewURLDataSource("test", "test:", simplestreams.VerifySSLHostnames)
28 rc, url, err := ds.Fetch("streams/v1/tools_metadata.json")28 rc, url, err := ds.Fetch("streams/v1/tools_metadata.json")
29 c.Assert(err, gc.IsNil)29 c.Assert(err, gc.IsNil)
30 defer rc.Close()30 defer rc.Close()
@@ -36,7 +36,7 @@
36}36}
3737
38func (s *datasourceSuite) TestURL(c *gc.C) {38func (s *datasourceSuite) TestURL(c *gc.C) {
39 ds := simplestreams.NewURLDataSource("foo", simplestreams.VerifySSLHostnames)39 ds := simplestreams.NewURLDataSource("test", "foo", simplestreams.VerifySSLHostnames)
40 url, err := ds.URL("bar")40 url, err := ds.URL("bar")
41 c.Assert(err, gc.IsNil)41 c.Assert(err, gc.IsNil)
42 c.Assert(url, gc.Equals, "foo/bar")42 c.Assert(url, gc.Equals, "foo/bar")
@@ -64,7 +64,7 @@
64}64}
6565
66func (s *datasourceHTTPSSuite) TestNormalClientFails(c *gc.C) {66func (s *datasourceHTTPSSuite) TestNormalClientFails(c *gc.C) {
67 ds := simplestreams.NewURLDataSource(s.Server.URL, simplestreams.VerifySSLHostnames)67 ds := simplestreams.NewURLDataSource("test", s.Server.URL, simplestreams.VerifySSLHostnames)
68 url, err := ds.URL("bar")68 url, err := ds.URL("bar")
69 c.Assert(err, gc.IsNil)69 c.Assert(err, gc.IsNil)
70 c.Check(url, gc.Equals, s.Server.URL+"/bar")70 c.Check(url, gc.Equals, s.Server.URL+"/bar")
@@ -76,7 +76,7 @@
76}76}
7777
78func (s *datasourceHTTPSSuite) TestNonVerifyingClientSucceeds(c *gc.C) {78func (s *datasourceHTTPSSuite) TestNonVerifyingClientSucceeds(c *gc.C) {
79 ds := simplestreams.NewURLDataSource(s.Server.URL, simplestreams.NoVerifySSLHostnames)79 ds := simplestreams.NewURLDataSource("test", s.Server.URL, simplestreams.NoVerifySSLHostnames)
80 url, err := ds.URL("bar")80 url, err := ds.URL("bar")
81 c.Assert(err, gc.IsNil)81 c.Assert(err, gc.IsNil)
82 c.Check(url, gc.Equals, s.Server.URL+"/bar")82 c.Check(url, gc.Equals, s.Server.URL+"/bar")
8383
=== modified file 'environs/simplestreams/simplestreams.go'
--- environs/simplestreams/simplestreams.go 2014-02-25 04:47:53 +0000
+++ environs/simplestreams/simplestreams.go 2014-03-04 05:51:00 +0000
@@ -30,6 +30,13 @@
3030
31var logger = loggo.GetLogger("juju.environs.simplestreams")31var logger = loggo.GetLogger("juju.environs.simplestreams")
3232
33type ResolveInfo struct {
34 Source string `yaml:"source" json:"source"`
35 Signed bool `yaml:"signed" json:"signed"`
36 IndexURL string `yaml:"indexURL" json:"indexURL"`
37 MirrorURL string `yaml:"mirrorURL,omitempty" json:"mirrorURL,omitempty"`
38}
39
33// CloudSpec uniquely defines a specific cloud deployment.40// CloudSpec uniquely defines a specific cloud deployment.
34type CloudSpec struct {41type CloudSpec struct {
35 Region string `json:"region"`42 Region string `json:"region"`
@@ -422,12 +429,14 @@
422// GetMetadata returns metadata records matching the specified constraint,looking in each source for signed metadata.429// GetMetadata returns metadata records matching the specified constraint,looking in each source for signed metadata.
423// If onlySigned is false and no signed metadata is found in a source, the source is used to look for unsigned metadata.430// If onlySigned is false and no signed metadata is found in a source, the source is used to look for unsigned metadata.
424// Each source is tried in turn until at least one signed (or unsigned) match is found.431// Each source is tried in turn until at least one signed (or unsigned) match is found.
425func GetMetadata(sources []DataSource, baseIndexPath string, cons LookupConstraint, onlySigned bool, params ValueParams) (items []interface{}, err error) {432func GetMetadata(
433 sources []DataSource, baseIndexPath string, cons LookupConstraint, onlySigned bool,
434 params ValueParams) (items []interface{}, resolveInfo *ResolveInfo, err error) {
426 for _, source := range sources {435 for _, source := range sources {
427 items, err = getMaybeSignedMetadata(source, baseIndexPath, cons, true, params)436 items, resolveInfo, err = getMaybeSignedMetadata(source, baseIndexPath, cons, true, params)
428 // If no items are found using signed metadata, check unsigned.437 // If no items are found using signed metadata, check unsigned.
429 if err != nil && len(items) == 0 && !onlySigned {438 if err != nil && len(items) == 0 && !onlySigned {
430 items, err = getMaybeSignedMetadata(source, baseIndexPath, cons, false, params)439 items, resolveInfo, err = getMaybeSignedMetadata(source, baseIndexPath, cons, false, params)
431 }440 }
432 if err == nil {441 if err == nil {
433 break442 break
@@ -437,11 +446,14 @@
437 // no matching products is an internal error only446 // no matching products is an internal error only
438 err = nil447 err = nil
439 }448 }
440 return items, err449 return items, resolveInfo, err
441}450}
442451
443// getMaybeSignedMetadata returns metadata records matching the specified constraint.452// getMaybeSignedMetadata returns metadata records matching the specified constraint.
444func getMaybeSignedMetadata(source DataSource, baseIndexPath string, cons LookupConstraint, signed bool, params ValueParams) ([]interface{}, error) {453func getMaybeSignedMetadata(source DataSource, baseIndexPath string, cons LookupConstraint,
454 signed bool, params ValueParams) ([]interface{}, *ResolveInfo, error) {
455
456 resolveInfo := &ResolveInfo{}
445 indexPath := baseIndexPath + UnsignedSuffix457 indexPath := baseIndexPath + UnsignedSuffix
446 if signed {458 if signed {
447 indexPath = baseIndexPath + signedSuffix459 indexPath = baseIndexPath + signedSuffix
@@ -453,25 +465,31 @@
453 // So the best we can do is use the relative path for the URL when logging messages.465 // So the best we can do is use the relative path for the URL when logging messages.
454 indexURL = indexPath466 indexURL = indexPath
455 }467 }
468 resolveInfo.Source = source.Description()
469 resolveInfo.Signed = signed
470 resolveInfo.IndexURL = indexURL
456 indexRef, err := GetIndexWithFormat(source, indexPath, "index:1.0", signed, cons.Params().CloudSpec, params)471 indexRef, err := GetIndexWithFormat(source, indexPath, "index:1.0", signed, cons.Params().CloudSpec, params)
457 if err != nil {472 if err != nil {
458 if errors.IsNotFoundError(err) || errors.IsUnauthorizedError(err) {473 if errors.IsNotFoundError(err) || errors.IsUnauthorizedError(err) {
459 logger.Debugf("cannot load index %q: %v", indexURL, err)474 logger.Debugf("cannot load index %q: %v", indexURL, err)
460 }475 }
461 return nil, err476 return nil, resolveInfo, err
462 }477 }
463 logger.Debugf("read metadata index at %q", indexURL)478 logger.Debugf("read metadata index at %q", indexURL)
464 items, err = indexRef.getLatestMetadataWithFormat(cons, "products:1.0", signed)479 items, err = indexRef.getLatestMetadataWithFormat(cons, "products:1.0", signed)
465 if err != nil {480 if err != nil {
466 if errors.IsNotFoundError(err) {481 if errors.IsNotFoundError(err) {
467 logger.Debugf("skipping index because of error getting latest metadata %q: %v", indexURL, err)482 logger.Debugf("skipping index because of error getting latest metadata %q: %v", indexURL, err)
468 return nil, err483 return nil, resolveInfo, err
469 }484 }
470 if _, ok := err.(*noMatchingProductsError); ok {485 if _, ok := err.(*noMatchingProductsError); ok {
471 logger.Debugf("%v", err)486 logger.Debugf("%v", err)
472 }487 }
473 }488 }
474 return items, err489 if indexRef.Source.Description() == "mirror" {
490 resolveInfo.MirrorURL = indexRef.Source.(*urlDataSource).baseURL
491 }
492 return items, resolveInfo, err
475}493}
476494
477// fetchData gets all the data from the given source located at the specified path.495// fetchData gets all the data from the given source located at the specified path.
@@ -534,7 +552,7 @@
534 source, mirrors, params.DataType, params.MirrorContentId, cloudSpec, requireSigned, params.PublicKey)552 source, mirrors, params.DataType, params.MirrorContentId, cloudSpec, requireSigned, params.PublicKey)
535 if err == nil {553 if err == nil {
536 logger.Debugf("using mirrored products path: %s", path.Join(mirrorInfo.MirrorURL, mirrorInfo.Path))554 logger.Debugf("using mirrored products path: %s", path.Join(mirrorInfo.MirrorURL, mirrorInfo.Path))
537 indexRef.Source = NewURLDataSource(mirrorInfo.MirrorURL, VerifySSLHostnames)555 indexRef.Source = NewURLDataSource("mirror", mirrorInfo.MirrorURL, VerifySSLHostnames)
538 indexRef.MirroredProductsPath = mirrorInfo.Path556 indexRef.MirroredProductsPath = mirrorInfo.Path
539 } else {557 } else {
540 logger.Debugf("no mirror information available for %s: %v", cloudSpec, err)558 logger.Debugf("no mirror information available for %s: %v", cloudSpec, err)
541559
=== modified file 'environs/simplestreams/simplestreams_test.go'
--- environs/simplestreams/simplestreams_test.go 2014-02-11 03:40:13 +0000
+++ environs/simplestreams/simplestreams_test.go 2014-03-04 05:51:00 +0000
@@ -27,7 +27,7 @@
27func registerSimpleStreamsTests() {27func registerSimpleStreamsTests() {
28 gc.Suite(&simplestreamsSuite{28 gc.Suite(&simplestreamsSuite{
29 LocalLiveSimplestreamsSuite: sstesting.LocalLiveSimplestreamsSuite{29 LocalLiveSimplestreamsSuite: sstesting.LocalLiveSimplestreamsSuite{
30 Source: simplestreams.NewURLDataSource("test:", simplestreams.VerifySSLHostnames),30 Source: simplestreams.NewURLDataSource("test", "test:", simplestreams.VerifySSLHostnames),
31 RequireSigned: false,31 RequireSigned: false,
32 DataType: "image-ids",32 DataType: "image-ids",
33 ValidConstraint: sstesting.NewTestConstraint(simplestreams.LookupParams{33 ValidConstraint: sstesting.NewTestConstraint(simplestreams.LookupParams{
@@ -316,7 +316,7 @@
316func (s *simplestreamsSuite) TestGetMetadataNoMatching(c *gc.C) {316func (s *simplestreamsSuite) TestGetMetadataNoMatching(c *gc.C) {
317 source := &countingSource{317 source := &countingSource{
318 DataSource: simplestreams.NewURLDataSource(318 DataSource: simplestreams.NewURLDataSource(
319 "test:/daily", simplestreams.VerifySSLHostnames,319 "test", "test:/daily", simplestreams.VerifySSLHostnames,
320 ),320 ),
321 }321 }
322 sources := []simplestreams.DataSource{source, source, source}322 sources := []simplestreams.DataSource{source, source, source}
@@ -330,7 +330,7 @@
330 Arches: []string{"not-a-real-arch"}, // never matches330 Arches: []string{"not-a-real-arch"}, // never matches
331 })331 })
332332
333 items, err := simplestreams.GetMetadata(333 items, resolveInfo, err := simplestreams.GetMetadata(
334 sources,334 sources,
335 simplestreams.DefaultIndexPath,335 simplestreams.DefaultIndexPath,
336 constraint,336 constraint,
@@ -339,6 +339,12 @@
339 )339 )
340 c.Assert(err, gc.IsNil)340 c.Assert(err, gc.IsNil)
341 c.Assert(items, gc.HasLen, 0)341 c.Assert(items, gc.HasLen, 0)
342 c.Assert(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{
343 Source: "test",
344 Signed: false,
345 IndexURL: "test:/daily/streams/v1/index.json",
346 MirrorURL: "",
347 })
342348
343 // There should be 2 calls to each data-source:349 // There should be 2 calls to each data-source:
344 // one for .sjson, one for .json.350 // one for .sjson, one for .json.
345351
=== modified file 'environs/storage/storage.go'
--- environs/storage/storage.go 2014-01-09 05:27:24 +0000
+++ environs/storage/storage.go 2014-03-04 05:51:00 +0000
@@ -72,9 +72,10 @@
7272
73// A storageSimpleStreamsDataSource retrieves data from a StorageReader.73// A storageSimpleStreamsDataSource retrieves data from a StorageReader.
74type storageSimpleStreamsDataSource struct {74type storageSimpleStreamsDataSource struct {
75 basePath string75 description string
76 storage StorageReader76 basePath string
77 allowRetry bool77 storage StorageReader
78 allowRetry bool
78}79}
7980
80// TestingGetAllowRetry is used in tests which need to see if allowRetry has been81// TestingGetAllowRetry is used in tests which need to see if allowRetry has been
@@ -87,8 +88,8 @@
87}88}
8889
89// NewStorageSimpleStreamsDataSource returns a new datasource reading from the specified storage.90// NewStorageSimpleStreamsDataSource returns a new datasource reading from the specified storage.
90func NewStorageSimpleStreamsDataSource(storage StorageReader, basePath string) simplestreams.DataSource {91func NewStorageSimpleStreamsDataSource(description string, storage StorageReader, basePath string) simplestreams.DataSource {
91 return &storageSimpleStreamsDataSource{basePath, storage, false}92 return &storageSimpleStreamsDataSource{description, basePath, storage, false}
92}93}
9394
94func (s *storageSimpleStreamsDataSource) relpath(storagePath string) string {95func (s *storageSimpleStreamsDataSource) relpath(storagePath string) string {
@@ -99,6 +100,11 @@
99 return relpath100 return relpath
100}101}
101102
103// Description is defined in simplestreams.DataSource.
104func (s *storageSimpleStreamsDataSource) Description() string {
105 return s.description
106}
107
102// Fetch is defined in simplestreams.DataSource.108// Fetch is defined in simplestreams.DataSource.
103func (s *storageSimpleStreamsDataSource) Fetch(path string) (io.ReadCloser, string, error) {109func (s *storageSimpleStreamsDataSource) Fetch(path string) (io.ReadCloser, string, error) {
104 relpath := s.relpath(path)110 relpath := s.relpath(path)
105111
=== modified file 'environs/storage/storage_test.go'
--- environs/storage/storage_test.go 2014-02-13 02:46:58 +0000
+++ environs/storage/storage_test.go 2014-03-04 05:51:00 +0000
@@ -57,7 +57,7 @@
57func (s *datasourceSuite) TestFetch(c *gc.C) {57func (s *datasourceSuite) TestFetch(c *gc.C) {
58 sampleData := "hello world"58 sampleData := "hello world"
59 s.stor.Put("foo/bar/data.txt", bytes.NewReader([]byte(sampleData)), int64(len(sampleData)))59 s.stor.Put("foo/bar/data.txt", bytes.NewReader([]byte(sampleData)), int64(len(sampleData)))
60 ds := storage.NewStorageSimpleStreamsDataSource(s.stor, "")60 ds := storage.NewStorageSimpleStreamsDataSource("test datasource", s.stor, "")
61 rc, url, err := ds.Fetch("foo/bar/data.txt")61 rc, url, err := ds.Fetch("foo/bar/data.txt")
62 c.Assert(err, gc.IsNil)62 c.Assert(err, gc.IsNil)
63 defer rc.Close()63 defer rc.Close()
@@ -69,7 +69,7 @@
69func (s *datasourceSuite) TestFetchWithBasePath(c *gc.C) {69func (s *datasourceSuite) TestFetchWithBasePath(c *gc.C) {
70 sampleData := "hello world"70 sampleData := "hello world"
71 s.stor.Put("base/foo/bar/data.txt", bytes.NewReader([]byte(sampleData)), int64(len(sampleData)))71 s.stor.Put("base/foo/bar/data.txt", bytes.NewReader([]byte(sampleData)), int64(len(sampleData)))
72 ds := storage.NewStorageSimpleStreamsDataSource(s.stor, "base")72 ds := storage.NewStorageSimpleStreamsDataSource("test datasource", s.stor, "base")
73 rc, url, err := ds.Fetch("foo/bar/data.txt")73 rc, url, err := ds.Fetch("foo/bar/data.txt")
74 c.Assert(err, gc.IsNil)74 c.Assert(err, gc.IsNil)
75 defer rc.Close()75 defer rc.Close()
@@ -80,7 +80,7 @@
8080
81func (s *datasourceSuite) TestFetchWithRetry(c *gc.C) {81func (s *datasourceSuite) TestFetchWithRetry(c *gc.C) {
82 stor := &fakeStorage{shouldRetry: true}82 stor := &fakeStorage{shouldRetry: true}
83 ds := storage.NewStorageSimpleStreamsDataSource(stor, "base")83 ds := storage.NewStorageSimpleStreamsDataSource("test datasource", stor, "base")
84 ds.SetAllowRetry(true)84 ds.SetAllowRetry(true)
85 _, _, err := ds.Fetch("foo/bar/data.txt")85 _, _, err := ds.Fetch("foo/bar/data.txt")
86 c.Assert(err, gc.ErrorMatches, "an error")86 c.Assert(err, gc.ErrorMatches, "an error")
@@ -92,7 +92,7 @@
92 // NB shouldRetry below is true indicating the fake storage is capable of92 // NB shouldRetry below is true indicating the fake storage is capable of
93 // retrying, not that it will retry.93 // retrying, not that it will retry.
94 stor := &fakeStorage{shouldRetry: true}94 stor := &fakeStorage{shouldRetry: true}
95 ds := storage.NewStorageSimpleStreamsDataSource(stor, "base")95 ds := storage.NewStorageSimpleStreamsDataSource("test datasource", stor, "base")
96 _, _, err := ds.Fetch("foo/bar/data.txt")96 _, _, err := ds.Fetch("foo/bar/data.txt")
97 c.Assert(err, gc.ErrorMatches, "an error")97 c.Assert(err, gc.ErrorMatches, "an error")
98 c.Assert(stor.getName, gc.Equals, "base/foo/bar/data.txt")98 c.Assert(stor.getName, gc.Equals, "base/foo/bar/data.txt")
@@ -102,7 +102,7 @@
102func (s *datasourceSuite) TestURL(c *gc.C) {102func (s *datasourceSuite) TestURL(c *gc.C) {
103 sampleData := "hello world"103 sampleData := "hello world"
104 s.stor.Put("bar/data.txt", bytes.NewReader([]byte(sampleData)), int64(len(sampleData)))104 s.stor.Put("bar/data.txt", bytes.NewReader([]byte(sampleData)), int64(len(sampleData)))
105 ds := storage.NewStorageSimpleStreamsDataSource(s.stor, "")105 ds := storage.NewStorageSimpleStreamsDataSource("test datasource", s.stor, "")
106 url, err := ds.URL("bar")106 url, err := ds.URL("bar")
107 c.Assert(err, gc.IsNil)107 c.Assert(err, gc.IsNil)
108 expectedURL, _ := s.stor.URL("bar")108 expectedURL, _ := s.stor.URL("bar")
@@ -112,7 +112,7 @@
112func (s *datasourceSuite) TestURLWithBasePath(c *gc.C) {112func (s *datasourceSuite) TestURLWithBasePath(c *gc.C) {
113 sampleData := "hello world"113 sampleData := "hello world"
114 s.stor.Put("base/bar/data.txt", bytes.NewReader([]byte(sampleData)), int64(len(sampleData)))114 s.stor.Put("base/bar/data.txt", bytes.NewReader([]byte(sampleData)), int64(len(sampleData)))
115 ds := storage.NewStorageSimpleStreamsDataSource(s.stor, "base")115 ds := storage.NewStorageSimpleStreamsDataSource("test datasource", s.stor, "base")
116 url, err := ds.URL("bar")116 url, err := ds.URL("bar")
117 c.Assert(err, gc.IsNil)117 c.Assert(err, gc.IsNil)
118 expectedURL, _ := s.stor.URL("base/bar")118 expectedURL, _ := s.stor.URL("base/bar")
119119
=== modified file 'environs/sync/sync.go'
--- environs/sync/sync.go 2014-01-30 14:11:27 +0000
+++ environs/sync/sync.go 2014-03-04 05:51:00 +0000
@@ -140,7 +140,7 @@
140 return nil, err140 return nil, err
141 }141 }
142 logger.Infof("using sync tools source: %v", sourceURL)142 logger.Infof("using sync tools source: %v", sourceURL)
143 return simplestreams.NewURLDataSource(sourceURL, simplestreams.VerifySSLHostnames), nil143 return simplestreams.NewURLDataSource("sync tools source", sourceURL, simplestreams.VerifySSLHostnames), nil
144}144}
145145
146// copyTools copies a set of tools from the source to the target.146// copyTools copies a set of tools from the source to the target.
147147
=== removed file 'environs/tools/boilerplate.go'
--- environs/tools/boilerplate.go 2014-01-22 22:48:54 +0000
+++ environs/tools/boilerplate.go 1970-01-01 00:00:00 +0000
@@ -1,153 +0,0 @@
1// Copyright 2013 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.
3
4package tools
5
6import (
7 "bytes"
8 "fmt"
9 "io/ioutil"
10 "os"
11 "path/filepath"
12 "text/template"
13 "time"
14
15 "launchpad.net/juju-core/environs/simplestreams"
16 "launchpad.net/juju-core/juju/osenv"
17)
18
19const (
20 defaultIndexFileName = "index.json"
21 defaultToolsFileName = "toolsmetadata.json"
22 streamsDir = "streams/v1"
23)
24
25// Boilerplate generates some basic simplestreams metadata using the specified cloud and tools details.
26func Boilerplate(tm *ToolsMetadata, cloudSpec *simplestreams.CloudSpec) ([]string, error) {
27 return MakeBoilerplate(tm, cloudSpec, true)
28}
29
30// MakeBoilerplate exists so it can be called by tests. See Boilerplate above. It provides an option to retain
31// the streams directories when writing the generated metadata files.
32func MakeBoilerplate(tm *ToolsMetadata, cloudSpec *simplestreams.CloudSpec, flattenPath bool) ([]string, error) {
33 indexFileName := defaultIndexFileName
34 toolsFileName := defaultToolsFileName
35 now := time.Now()
36 imparams := toolsMetadataParams{
37 ToolsBinarySize: tm.Size,
38 ToolsBinaryPath: tm.Path,
39 ToolsBinarySHA256: tm.SHA256,
40 Version: tm.Version,
41 Arch: tm.Arch,
42 Series: tm.Release,
43 Region: cloudSpec.Region,
44 URL: cloudSpec.Endpoint,
45 Path: streamsDir,
46 ToolsFileName: toolsFileName,
47 Updated: now.Format(time.RFC1123Z),
48 VersionKey: now.Format("20060102"),
49 }
50
51 var err error
52 imparams.SeriesVersion, err = simplestreams.SeriesVersion(tm.Release)
53 if err != nil {
54 return nil, fmt.Errorf("invalid series %q", tm.Release)
55 }
56
57 if !flattenPath {
58 streamsPath := osenv.JujuHomePath(streamsDir)
59 if err := os.MkdirAll(streamsPath, 0755); err != nil {
60 return nil, err
61 }
62 indexFileName = filepath.Join(streamsDir, indexFileName)
63 toolsFileName = filepath.Join(streamsDir, toolsFileName)
64 }
65 err = writeJsonFile(imparams, indexFileName, indexBoilerplate)
66 if err != nil {
67 return nil, err
68 }
69 err = writeJsonFile(imparams, toolsFileName, productBoilerplate)
70 if err != nil {
71 return nil, err
72 }
73 return []string{indexFileName, toolsFileName}, nil
74}
75
76type toolsMetadataParams struct {
77 ToolsBinaryPath string
78 ToolsBinarySize int64
79 ToolsBinarySHA256 string
80 Region string
81 URL string
82 Updated string
83 Arch string
84 Path string
85 Series string
86 SeriesVersion string
87 Version string
88 VersionKey string
89 ToolsFileName string
90}
91
92func writeJsonFile(imparams toolsMetadataParams, filename, boilerplate string) error {
93 t := template.Must(template.New("").Parse(boilerplate))
94 var metadata bytes.Buffer
95 if err := t.Execute(&metadata, imparams); err != nil {
96 panic(fmt.Errorf("cannot generate %s metdata: %v", filename, err))
97 }
98 data := metadata.Bytes()
99 path := osenv.JujuHomePath(filename)
100 if err := ioutil.WriteFile(path, data, 0666); err != nil {
101 return err
102 }
103 return nil
104}
105
106var indexBoilerplate = `
107{
108 "index": {
109 "com.ubuntu.juju:custom": {
110 "updated": "{{.Updated}}",
111 "cloudname": "custom",
112 "datatype": "content-download",
113 "format": "products:1.0",
114 "products": [
115 "com.ubuntu.juju:{{.SeriesVersion}}:{{.Arch}}"
116 ],
117 "path": "{{.Path}}/{{.ToolsFileName}}"
118 }
119 },
120 "updated": "{{.Updated}}",
121 "format": "index:1.0"
122}
123`
124
125var productBoilerplate = `
126{
127 "content_id": "com.ubuntu.juju:custom",
128 "format": "products:1.0",
129 "updated": "{{.Updated}}",
130 "datatype": "content-download",
131 "products": {
132 "com.ubuntu.juju:{{.SeriesVersion}}:{{.Arch}}": {
133 "release": "{{.Series}}",
134 "arch": "{{.Arch}}",
135 "versions": {
136 "{{.VersionKey}}": {
137 "items": {
138 "{{.Series}}{{.Version}}": {
139 "version": "{{.Version}}",
140 "size": {{.ToolsBinarySize}},
141 "path": "{{.ToolsBinaryPath}}",
142 "ftype": "tar.gz",
143 "sha256": "{{.ToolsBinarySHA256}}"
144 }
145 },
146 "pubname": "juju-{{.Series}}-{{.Arch}}-{{.VersionKey}}",
147 "label": "custom"
148 }
149 }
150 }
151 }
152}
153`
1540
=== modified file 'environs/tools/simplestreams.go'
--- environs/tools/simplestreams.go 2014-01-30 06:21:03 +0000
+++ environs/tools/simplestreams.go 2014-03-04 05:51:00 +0000
@@ -102,9 +102,8 @@
102}102}
103103
104// NewVersionedToolsConstraint returns a ToolsConstraint for a tools with a specific version.104// NewVersionedToolsConstraint returns a ToolsConstraint for a tools with a specific version.
105func NewVersionedToolsConstraint(vers string, params simplestreams.LookupParams) *ToolsConstraint {105func NewVersionedToolsConstraint(vers version.Number, params simplestreams.LookupParams) *ToolsConstraint {
106 versNum := version.MustParse(vers)106 return &ToolsConstraint{LookupParams: params, Version: vers}
107 return &ToolsConstraint{LookupParams: params, Version: versNum}
108}107}
109108
110// NewGeneralToolsConstraint returns a ToolsConstraint for tools with matching major/minor version numbers.109// NewGeneralToolsConstraint returns a ToolsConstraint for tools with matching major/minor version numbers.
@@ -168,7 +167,10 @@
168// The base URL locations are as specified - the first location which has a file is the one used.167// The base URL locations are as specified - the first location which has a file is the one used.
169// Signed data is preferred, but if there is no signed data available and onlySigned is false,168// Signed data is preferred, but if there is no signed data available and onlySigned is false,
170// then unsigned data is used.169// then unsigned data is used.
171func Fetch(sources []simplestreams.DataSource, indexPath string, cons *ToolsConstraint, onlySigned bool) ([]*ToolsMetadata, error) {170func Fetch(
171 sources []simplestreams.DataSource, indexPath string, cons *ToolsConstraint,
172 onlySigned bool) ([]*ToolsMetadata, *simplestreams.ResolveInfo, error) {
173
172 params := simplestreams.ValueParams{174 params := simplestreams.ValueParams{
173 DataType: ContentDownload,175 DataType: ContentDownload,
174 FilterFunc: appendMatchingTools,176 FilterFunc: appendMatchingTools,
@@ -176,15 +178,15 @@
176 ValueTemplate: ToolsMetadata{},178 ValueTemplate: ToolsMetadata{},
177 PublicKey: simplestreamsToolsPublicKey,179 PublicKey: simplestreamsToolsPublicKey,
178 }180 }
179 items, err := simplestreams.GetMetadata(sources, indexPath, cons, onlySigned, params)181 items, resolveInfo, err := simplestreams.GetMetadata(sources, indexPath, cons, onlySigned, params)
180 if err != nil {182 if err != nil {
181 return nil, err183 return nil, nil, err
182 }184 }
183 metadata := make([]*ToolsMetadata, len(items))185 metadata := make([]*ToolsMetadata, len(items))
184 for i, md := range items {186 for i, md := range items {
185 metadata[i] = md.(*ToolsMetadata)187 metadata[i] = md.(*ToolsMetadata)
186 }188 }
187 return metadata, nil189 return metadata, resolveInfo, nil
188}190}
189191
190// appendMatchingTools updates matchingTools with tools metadata records from tools which belong to the192// appendMatchingTools updates matchingTools with tools metadata records from tools which belong to the
@@ -311,12 +313,13 @@
311313
312// ReadMetadata returns the tools metadata from the given storage.314// ReadMetadata returns the tools metadata from the given storage.
313func ReadMetadata(store storage.StorageReader) ([]*ToolsMetadata, error) {315func ReadMetadata(store storage.StorageReader) ([]*ToolsMetadata, error) {
314 dataSource := storage.NewStorageSimpleStreamsDataSource(store, storage.BaseToolsPath)316 dataSource := storage.NewStorageSimpleStreamsDataSource("existing metadata", store, storage.BaseToolsPath)
315 toolsConstraint, err := makeToolsConstraint(simplestreams.CloudSpec{}, -1, -1, coretools.Filter{})317 toolsConstraint, err := makeToolsConstraint(simplestreams.CloudSpec{}, -1, -1, coretools.Filter{})
316 if err != nil {318 if err != nil {
317 return nil, err319 return nil, err
318 }320 }
319 metadata, err := Fetch([]simplestreams.DataSource{dataSource}, simplestreams.DefaultIndexPath, toolsConstraint, false)321 metadata, _, err := Fetch(
322 []simplestreams.DataSource{dataSource}, simplestreams.DefaultIndexPath, toolsConstraint, false)
320 if err != nil && !errors.IsNotFoundError(err) {323 if err != nil && !errors.IsNotFoundError(err) {
321 return nil, err324 return nil, err
322 }325 }
323326
=== modified file 'environs/tools/simplestreams_test.go'
--- environs/tools/simplestreams_test.go 2014-01-30 06:21:03 +0000
+++ environs/tools/simplestreams_test.go 2014-03-04 05:51:00 +0000
@@ -62,7 +62,7 @@
62 t.Fatalf("Unknown vendor %s. Must be one of %s", *vendor, keys)62 t.Fatalf("Unknown vendor %s. Must be one of %s", *vendor, keys)
63 }63 }
64 registerLiveSimpleStreamsTests(testData.baseURL,64 registerLiveSimpleStreamsTests(testData.baseURL,
65 tools.NewVersionedToolsConstraint("1.13.0", simplestreams.LookupParams{65 tools.NewVersionedToolsConstraint(version.MustParse("1.13.0"), simplestreams.LookupParams{
66 CloudSpec: testData.validCloudSpec,66 CloudSpec: testData.validCloudSpec,
67 Series: []string{version.Current.Series},67 Series: []string{version.Current.Series},
68 Arches: []string{"amd64"},68 Arches: []string{"amd64"},
@@ -74,10 +74,10 @@
74func registerSimpleStreamsTests() {74func registerSimpleStreamsTests() {
75 gc.Suite(&simplestreamsSuite{75 gc.Suite(&simplestreamsSuite{
76 LocalLiveSimplestreamsSuite: sstesting.LocalLiveSimplestreamsSuite{76 LocalLiveSimplestreamsSuite: sstesting.LocalLiveSimplestreamsSuite{
77 Source: simplestreams.NewURLDataSource("test:", simplestreams.VerifySSLHostnames),77 Source: simplestreams.NewURLDataSource("test", "test:", simplestreams.VerifySSLHostnames),
78 RequireSigned: false,78 RequireSigned: false,
79 DataType: tools.ContentDownload,79 DataType: tools.ContentDownload,
80 ValidConstraint: tools.NewVersionedToolsConstraint("1.13.0", simplestreams.LookupParams{80 ValidConstraint: tools.NewVersionedToolsConstraint(version.MustParse("1.13.0"), simplestreams.LookupParams{
81 CloudSpec: simplestreams.CloudSpec{81 CloudSpec: simplestreams.CloudSpec{
82 Region: "us-east-1",82 Region: "us-east-1",
83 Endpoint: "https://ec2.us-east-1.amazonaws.com",83 Endpoint: "https://ec2.us-east-1.amazonaws.com",
@@ -92,7 +92,7 @@
9292
93func registerLiveSimpleStreamsTests(baseURL string, validToolsConstraint simplestreams.LookupConstraint, requireSigned bool) {93func registerLiveSimpleStreamsTests(baseURL string, validToolsConstraint simplestreams.LookupConstraint, requireSigned bool) {
94 gc.Suite(&sstesting.LocalLiveSimplestreamsSuite{94 gc.Suite(&sstesting.LocalLiveSimplestreamsSuite{
95 Source: simplestreams.NewURLDataSource(baseURL, simplestreams.VerifySSLHostnames),95 Source: simplestreams.NewURLDataSource("test", baseURL, simplestreams.VerifySSLHostnames),
96 RequireSigned: requireSigned,96 RequireSigned: requireSigned,
97 DataType: tools.ContentDownload,97 DataType: tools.ContentDownload,
98 ValidConstraint: validToolsConstraint,98 ValidConstraint: validToolsConstraint,
@@ -238,14 +238,18 @@
238 Arches: t.arches,238 Arches: t.arches,
239 })239 })
240 } else {240 } else {
241 toolsConstraint = tools.NewVersionedToolsConstraint(t.version, simplestreams.LookupParams{241 toolsConstraint = tools.NewVersionedToolsConstraint(version.MustParse(t.version),
242 CloudSpec: simplestreams.CloudSpec{"us-east-1", "https://ec2.us-east-1.amazonaws.com"},242 simplestreams.LookupParams{
243 Series: []string{t.series},243 CloudSpec: simplestreams.CloudSpec{"us-east-1", "https://ec2.us-east-1.amazonaws.com"},
244 Arches: t.arches,244 Series: []string{t.series},
245 })245 Arches: t.arches,
246 })
246 }247 }
247 tools, err := tools.Fetch(248 // Add invalid datasource and check later that resolveInfo is correct.
248 []simplestreams.DataSource{s.Source}, simplestreams.DefaultIndexPath, toolsConstraint, s.RequireSigned)249 invalidSource := simplestreams.NewURLDataSource("invalid", "file://invalid", simplestreams.VerifySSLHostnames)
250 tools, resolveInfo, err := tools.Fetch(
251 []simplestreams.DataSource{invalidSource, s.Source},
252 simplestreams.DefaultIndexPath, toolsConstraint, s.RequireSigned)
249 if !c.Check(err, gc.IsNil) {253 if !c.Check(err, gc.IsNil) {
250 continue254 continue
251 }255 }
@@ -254,6 +258,12 @@
254 c.Assert(err, gc.IsNil)258 c.Assert(err, gc.IsNil)
255 }259 }
256 c.Check(tools, gc.DeepEquals, t.tools)260 c.Check(tools, gc.DeepEquals, t.tools)
261 c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{
262 Source: "test",
263 Signed: s.RequireSigned,
264 IndexURL: "test:/streams/v1/index.json",
265 MirrorURL: "",
266 })
257 }267 }
258}268}
259269
@@ -263,7 +273,7 @@
263 Series: []string{"precise"},273 Series: []string{"precise"},
264 Arches: []string{"amd64"},274 Arches: []string{"amd64"},
265 })275 })
266 toolsMetadata, err := tools.Fetch(276 toolsMetadata, resolveInfo, err := tools.Fetch(
267 []simplestreams.DataSource{s.Source}, simplestreams.DefaultIndexPath, toolsConstraint, s.RequireSigned)277 []simplestreams.DataSource{s.Source}, simplestreams.DefaultIndexPath, toolsConstraint, s.RequireSigned)
268 c.Assert(err, gc.IsNil)278 c.Assert(err, gc.IsNil)
269 c.Assert(len(toolsMetadata), gc.Equals, 1)279 c.Assert(len(toolsMetadata), gc.Equals, 1)
@@ -280,6 +290,12 @@
280 }290 }
281 c.Assert(err, gc.IsNil)291 c.Assert(err, gc.IsNil)
282 c.Assert(toolsMetadata[0], gc.DeepEquals, expectedMetadata)292 c.Assert(toolsMetadata[0], gc.DeepEquals, expectedMetadata)
293 c.Assert(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{
294 Source: "test",
295 Signed: s.RequireSigned,
296 IndexURL: "test:/streams/v1/index.json",
297 MirrorURL: "test:/",
298 })
283}299}
284300
285func assertMetadataMatches(c *gc.C, storageDir string, toolList coretools.List, metadata []*tools.ToolsMetadata) {301func assertMetadataMatches(c *gc.C, storageDir string, toolList coretools.List, metadata []*tools.ToolsMetadata) {
@@ -396,7 +412,7 @@
396var _ = gc.Suite(&productSpecSuite{})412var _ = gc.Suite(&productSpecSuite{})
397413
398func (s *productSpecSuite) TestId(c *gc.C) {414func (s *productSpecSuite) TestId(c *gc.C) {
399 toolsConstraint := tools.NewVersionedToolsConstraint("1.13.0", simplestreams.LookupParams{415 toolsConstraint := tools.NewVersionedToolsConstraint(version.MustParse("1.13.0"), simplestreams.LookupParams{
400 Series: []string{"precise"},416 Series: []string{"precise"},
401 Arches: []string{"amd64"},417 Arches: []string{"amd64"},
402 })418 })
@@ -406,7 +422,7 @@
406}422}
407423
408func (s *productSpecSuite) TestIdMultiArch(c *gc.C) {424func (s *productSpecSuite) TestIdMultiArch(c *gc.C) {
409 toolsConstraint := tools.NewVersionedToolsConstraint("1.11.3", simplestreams.LookupParams{425 toolsConstraint := tools.NewVersionedToolsConstraint(version.MustParse("1.11.3"), simplestreams.LookupParams{
410 Series: []string{"precise"},426 Series: []string{"precise"},
411 Arches: []string{"amd64", "arm"},427 Arches: []string{"amd64", "arm"},
412 })428 })
@@ -418,7 +434,7 @@
418}434}
419435
420func (s *productSpecSuite) TestIdMultiSeries(c *gc.C) {436func (s *productSpecSuite) TestIdMultiSeries(c *gc.C) {
421 toolsConstraint := tools.NewVersionedToolsConstraint("1.11.3", simplestreams.LookupParams{437 toolsConstraint := tools.NewVersionedToolsConstraint(version.MustParse("1.11.3"), simplestreams.LookupParams{
422 Series: []string{"precise", "raring"},438 Series: []string{"precise", "raring"},
423 Arches: []string{"amd64"},439 Arches: []string{"amd64"},
424 })440 })
@@ -752,17 +768,23 @@
752}768}
753769
754func (s *signedSuite) TestSignedToolsMetadata(c *gc.C) {770func (s *signedSuite) TestSignedToolsMetadata(c *gc.C) {
755 signedSource := simplestreams.NewURLDataSource("signedtest://host/signed", simplestreams.VerifySSLHostnames)771 signedSource := simplestreams.NewURLDataSource("test", "signedtest://host/signed", simplestreams.VerifySSLHostnames)
756 toolsConstraint := tools.NewVersionedToolsConstraint("1.13.0", simplestreams.LookupParams{772 toolsConstraint := tools.NewVersionedToolsConstraint(version.MustParse("1.13.0"), simplestreams.LookupParams{
757 CloudSpec: simplestreams.CloudSpec{"us-east-1", "https://ec2.us-east-1.amazonaws.com"},773 CloudSpec: simplestreams.CloudSpec{"us-east-1", "https://ec2.us-east-1.amazonaws.com"},
758 Series: []string{"precise"},774 Series: []string{"precise"},
759 Arches: []string{"amd64"},775 Arches: []string{"amd64"},
760 })776 })
761 toolsMetadata, err := tools.Fetch(777 toolsMetadata, resolveInfo, err := tools.Fetch(
762 []simplestreams.DataSource{signedSource}, simplestreams.DefaultIndexPath, toolsConstraint, true)778 []simplestreams.DataSource{signedSource}, simplestreams.DefaultIndexPath, toolsConstraint, true)
763 c.Assert(err, gc.IsNil)779 c.Assert(err, gc.IsNil)
764 c.Assert(len(toolsMetadata), gc.Equals, 1)780 c.Assert(len(toolsMetadata), gc.Equals, 1)
765 c.Assert(toolsMetadata[0].Path, gc.Equals, "tools/releases/20130806/juju-1.13.1-precise-amd64.tgz")781 c.Assert(toolsMetadata[0].Path, gc.Equals, "tools/releases/20130806/juju-1.13.1-precise-amd64.tgz")
782 c.Assert(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{
783 Source: "test",
784 Signed: true,
785 IndexURL: "signedtest://host/signed/streams/v1/index.sjson",
786 MirrorURL: "",
787 })
766}788}
767789
768var unsignedIndex = `790var unsignedIndex = `
769791
=== modified file 'environs/tools/testing/testing.go'
--- environs/tools/testing/testing.go 2014-01-30 14:11:27 +0000
+++ environs/tools/testing/testing.go 2014-03-04 05:51:00 +0000
@@ -83,7 +83,7 @@
8383
84// ParseMetadataFromStorage loads ToolsMetadata from the specified storage reader.84// ParseMetadataFromStorage loads ToolsMetadata from the specified storage reader.
85func ParseMetadataFromStorage(c *gc.C, stor storage.StorageReader, expectMirrors bool) []*tools.ToolsMetadata {85func ParseMetadataFromStorage(c *gc.C, stor storage.StorageReader, expectMirrors bool) []*tools.ToolsMetadata {
86 source := storage.NewStorageSimpleStreamsDataSource(stor, "tools")86 source := storage.NewStorageSimpleStreamsDataSource("test storage reader", stor, "tools")
87 params := simplestreams.ValueParams{87 params := simplestreams.ValueParams{
88 DataType: tools.ContentDownload,88 DataType: tools.ContentDownload,
89 ValueTemplate: tools.ToolsMetadata{},89 ValueTemplate: tools.ToolsMetadata{},
9090
=== modified file 'environs/tools/tools.go'
--- environs/tools/tools.go 2014-02-28 03:05:47 +0000
+++ environs/tools/tools.go 2014-03-04 05:51:00 +0000
@@ -32,7 +32,7 @@
32 if majorMismatch || minorMismacth {32 if majorMismatch || minorMismacth {
33 return nil, coretools.ErrNoMatches33 return nil, coretools.ErrNoMatches
34 }34 }
35 toolsConstraint = NewVersionedToolsConstraint(filter.Number.String(),35 toolsConstraint = NewVersionedToolsConstraint(filter.Number,
36 simplestreams.LookupParams{CloudSpec: cloudSpec})36 simplestreams.LookupParams{CloudSpec: cloudSpec})
37 } else {37 } else {
38 toolsConstraint = NewGeneralToolsConstraint(majorVersion, minorVersion, filter.Released,38 toolsConstraint = NewGeneralToolsConstraint(majorVersion, minorVersion, filter.Released,
@@ -117,7 +117,7 @@
117 if err != nil {117 if err != nil {
118 return nil, err118 return nil, err
119 }119 }
120 toolsMetadata, err := Fetch(sources, simplestreams.DefaultIndexPath, toolsConstraint, false)120 toolsMetadata, _, err := Fetch(sources, simplestreams.DefaultIndexPath, toolsConstraint, false)
121 if err != nil {121 if err != nil {
122 if errors.IsNotFoundError(err) {122 if errors.IsNotFoundError(err) {
123 err = ErrNoTools123 err = ErrNoTools
124124
=== modified file 'environs/tools/urls.go'
--- environs/tools/urls.go 2014-01-20 23:32:00 +0000
+++ environs/tools/urls.go 2014-03-04 05:51:00 +0000
@@ -39,7 +39,7 @@
39 if !config.SSLHostnameVerification() {39 if !config.SSLHostnameVerification() {
40 verify = simplestreams.NoVerifySSLHostnames40 verify = simplestreams.NoVerifySSLHostnames
41 }41 }
42 sources = append(sources, simplestreams.NewURLDataSource(userURL, verify))42 sources = append(sources, simplestreams.NewURLDataSource("tools-metadata-url", userURL, verify))
43 }43 }
44 if custom, ok := env.(SupportsCustomSources); ok {44 if custom, ok := env.(SupportsCustomSources); ok {
45 customSources, err := custom.GetToolsSources()45 customSources, err := custom.GetToolsSources()
@@ -54,7 +54,7 @@
54 return nil, err54 return nil, err
55 }55 }
56 if defaultURL != "" {56 if defaultURL != "" {
57 sources = append(sources, simplestreams.NewURLDataSource(defaultURL, simplestreams.VerifySSLHostnames))57 sources = append(sources, simplestreams.NewURLDataSource("default simplestreams", defaultURL, simplestreams.VerifySSLHostnames))
58 }58 }
59 for _, source := range sources {59 for _, source := range sources {
60 source.SetAllowRetry(allowRetry)60 source.SetAllowRetry(allowRetry)
6161
=== modified file 'environs/tools/validation.go'
--- environs/tools/validation.go 2013-09-20 18:46:47 +0000
+++ environs/tools/validation.go 2014-03-04 05:51:00 +0000
@@ -20,12 +20,12 @@
2020
21// ValidateToolsMetadata attempts to load tools metadata for the specified cloud attributes and returns21// ValidateToolsMetadata attempts to load tools metadata for the specified cloud attributes and returns
22// any tools versions found, or an error if the metadata could not be loaded.22// any tools versions found, or an error if the metadata could not be loaded.
23func ValidateToolsMetadata(params *ToolsMetadataLookupParams) ([]string, error) {23func ValidateToolsMetadata(params *ToolsMetadataLookupParams) ([]string, *simplestreams.ResolveInfo, error) {
24 if len(params.Architectures) == 0 {24 if len(params.Architectures) == 0 {
25 return nil, fmt.Errorf("required parameter arches not specified")25 return nil, nil, fmt.Errorf("required parameter arches not specified")
26 }26 }
27 if len(params.Sources) == 0 {27 if len(params.Sources) == 0 {
28 return nil, fmt.Errorf("required parameter sources not specified")28 return nil, nil, fmt.Errorf("required parameter sources not specified")
29 }29 }
30 if params.Version == "" && params.Major == 0 {30 if params.Version == "" && params.Major == 0 {
31 params.Version = version.Current.Number.String()31 params.Version = version.Current.Number.String()
@@ -38,23 +38,27 @@
38 Arches: params.Architectures,38 Arches: params.Architectures,
39 })39 })
40 } else {40 } else {
41 toolsConstraint = NewVersionedToolsConstraint(params.Version, simplestreams.LookupParams{41 versNum, err := version.Parse(params.Version)
42 if err != nil {
43 return nil, nil, err
44 }
45 toolsConstraint = NewVersionedToolsConstraint(versNum, simplestreams.LookupParams{
42 CloudSpec: simplestreams.CloudSpec{params.Region, params.Endpoint},46 CloudSpec: simplestreams.CloudSpec{params.Region, params.Endpoint},
43 Series: []string{params.Series},47 Series: []string{params.Series},
44 Arches: params.Architectures,48 Arches: params.Architectures,
45 })49 })
46 }50 }
47 matchingTools, err := Fetch(params.Sources, simplestreams.DefaultIndexPath, toolsConstraint, false)51 matchingTools, resolveInfo, err := Fetch(params.Sources, simplestreams.DefaultIndexPath, toolsConstraint, false)
48 if err != nil {52 if err != nil {
49 return nil, err53 return nil, resolveInfo, err
50 }54 }
51 if len(matchingTools) == 0 {55 if len(matchingTools) == 0 {
52 return nil, fmt.Errorf("no matching tools found for constraint %+v", toolsConstraint)56 return nil, resolveInfo, fmt.Errorf("no matching tools found for constraint %+v", toolsConstraint)
53 }57 }
54 versions := make([]string, len(matchingTools))58 versions := make([]string, len(matchingTools))
55 for i, tm := range matchingTools {59 for i, tm := range matchingTools {
56 vers := version.Binary{version.MustParse(tm.Version), tm.Release, tm.Arch}60 vers := version.Binary{version.MustParse(tm.Version), tm.Release, tm.Arch}
57 versions[i] = vers.String()61 versions[i] = vers.String()
58 }62 }
59 return versions, nil63 return versions, resolveInfo, nil
60}64}
6165
=== modified file 'environs/tools/validation_test.go'
--- environs/tools/validation_test.go 2014-01-22 22:48:54 +0000
+++ environs/tools/validation_test.go 2014-03-04 05:51:00 +0000
@@ -4,23 +4,24 @@
4package tools4package tools
55
6import (6import (
7 "path"
8
7 gc "launchpad.net/gocheck"9 gc "launchpad.net/gocheck"
810
11 "launchpad.net/juju-core/environs/filestorage"
9 "launchpad.net/juju-core/environs/simplestreams"12 "launchpad.net/juju-core/environs/simplestreams"
10 "launchpad.net/juju-core/juju/osenv"
11 coretesting "launchpad.net/juju-core/testing"
12 "launchpad.net/juju-core/testing/testbase"13 "launchpad.net/juju-core/testing/testbase"
13)14)
1415
15type ValidateSuite struct {16type ValidateSuite struct {
16 testbase.LoggingSuite17 testbase.LoggingSuite
17 home *coretesting.FakeHome18 metadataDir string
18}19}
1920
20var _ = gc.Suite(&ValidateSuite{})21var _ = gc.Suite(&ValidateSuite{})
2122
22func (s *ValidateSuite) makeLocalMetadata(c *gc.C, version, region, series, endpoint string) error {23func (s *ValidateSuite) makeLocalMetadata(c *gc.C, version, series string) error {
23 tm := ToolsMetadata{24 tm := []*ToolsMetadata{{
24 Version: version,25 Version: version,
25 Release: series,26 Release: series,
26 Arch: "amd64",27 Arch: "amd64",
@@ -28,31 +29,26 @@
28 Size: 1234,29 Size: 1234,
29 FileType: "tar.gz",30 FileType: "tar.gz",
30 SHA256: "f65a92b3b41311bdf398663ee1c5cd0c",31 SHA256: "f65a92b3b41311bdf398663ee1c5cd0c",
31 }32 }}
32 cloudSpec := simplestreams.CloudSpec{33
33 Region: region,34 stor, err := filestorage.NewFileStorageWriter(s.metadataDir, filestorage.UseDefaultTmpDir)
34 Endpoint: endpoint,35 c.Assert(err, gc.IsNil)
35 }36 err = WriteMetadata(stor, tm, false)
36 _, err := MakeBoilerplate(&tm, &cloudSpec, false)37 c.Assert(err, gc.IsNil)
37 if err != nil {
38 return err
39 }
40 return nil38 return nil
41}39}
4240
43func (s *ValidateSuite) SetUpTest(c *gc.C) {41func (s *ValidateSuite) SetUpTest(c *gc.C) {
44 s.LoggingSuite.SetUpTest(c)42 s.LoggingSuite.SetUpTest(c)
45 s.home = coretesting.MakeEmptyFakeHome(c)43 s.metadataDir = c.MkDir()
46}44}
4745
48func (s *ValidateSuite) TearDownTest(c *gc.C) {46func (s *ValidateSuite) toolsURL() string {
49 s.home.Restore()47 return "file://" + path.Join(s.metadataDir, "tools")
50 s.LoggingSuite.TearDownTest(c)
51}48}
5249
53func (s *ValidateSuite) TestExactVersionMatch(c *gc.C) {50func (s *ValidateSuite) TestExactVersionMatch(c *gc.C) {
54 s.makeLocalMetadata(c, "1.11.2", "region-2", "raring", "some-auth-url")51 s.makeLocalMetadata(c, "1.11.2", "raring")
55 metadataDir := osenv.JujuHomePath("")
56 params := &ToolsMetadataLookupParams{52 params := &ToolsMetadataLookupParams{
57 Version: "1.11.2",53 Version: "1.11.2",
58 MetadataLookupParams: simplestreams.MetadataLookupParams{54 MetadataLookupParams: simplestreams.MetadataLookupParams{
@@ -60,17 +56,23 @@
60 Series: "raring",56 Series: "raring",
61 Architectures: []string{"amd64"},57 Architectures: []string{"amd64"},
62 Endpoint: "some-auth-url",58 Endpoint: "some-auth-url",
63 Sources: []simplestreams.DataSource{simplestreams.NewURLDataSource("file://"+metadataDir, simplestreams.VerifySSLHostnames)},59 Sources: []simplestreams.DataSource{
60 simplestreams.NewURLDataSource("test", s.toolsURL(), simplestreams.VerifySSLHostnames)},
64 },61 },
65 }62 }
66 versions, err := ValidateToolsMetadata(params)63 versions, resolveInfo, err := ValidateToolsMetadata(params)
67 c.Assert(err, gc.IsNil)64 c.Assert(err, gc.IsNil)
68 c.Assert(versions, gc.DeepEquals, []string{"1.11.2-raring-amd64"})65 c.Assert(versions, gc.DeepEquals, []string{"1.11.2-raring-amd64"})
66 c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{
67 Source: "test",
68 Signed: false,
69 IndexURL: "file://" + path.Join(s.metadataDir, "tools/streams/v1/index.json"),
70 MirrorURL: "",
71 })
69}72}
7073
71func (s *ValidateSuite) TestMajorVersionMatch(c *gc.C) {74func (s *ValidateSuite) TestMajorVersionMatch(c *gc.C) {
72 s.makeLocalMetadata(c, "1.11.2", "region-2", "raring", "some-auth-url")75 s.makeLocalMetadata(c, "1.11.2", "raring")
73 metadataDir := osenv.JujuHomePath("")
74 params := &ToolsMetadataLookupParams{76 params := &ToolsMetadataLookupParams{
75 Major: 1,77 Major: 1,
76 Minor: -1,78 Minor: -1,
@@ -79,17 +81,23 @@
79 Series: "raring",81 Series: "raring",
80 Architectures: []string{"amd64"},82 Architectures: []string{"amd64"},
81 Endpoint: "some-auth-url",83 Endpoint: "some-auth-url",
82 Sources: []simplestreams.DataSource{simplestreams.NewURLDataSource("file://"+metadataDir, simplestreams.VerifySSLHostnames)},84 Sources: []simplestreams.DataSource{
85 simplestreams.NewURLDataSource("test", s.toolsURL(), simplestreams.VerifySSLHostnames)},
83 },86 },
84 }87 }
85 versions, err := ValidateToolsMetadata(params)88 versions, resolveInfo, err := ValidateToolsMetadata(params)
86 c.Assert(err, gc.IsNil)89 c.Assert(err, gc.IsNil)
87 c.Assert(versions, gc.DeepEquals, []string{"1.11.2-raring-amd64"})90 c.Assert(versions, gc.DeepEquals, []string{"1.11.2-raring-amd64"})
91 c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{
92 Source: "test",
93 Signed: false,
94 IndexURL: "file://" + path.Join(s.metadataDir, "tools/streams/v1/index.json"),
95 MirrorURL: "",
96 })
88}97}
8998
90func (s *ValidateSuite) TestMajorMinorVersionMatch(c *gc.C) {99func (s *ValidateSuite) TestMajorMinorVersionMatch(c *gc.C) {
91 s.makeLocalMetadata(c, "1.11.2", "region-2", "raring", "some-auth-url")100 s.makeLocalMetadata(c, "1.11.2", "raring")
92 metadataDir := osenv.JujuHomePath("")
93 params := &ToolsMetadataLookupParams{101 params := &ToolsMetadataLookupParams{
94 Major: 1,102 Major: 1,
95 Minor: 11,103 Minor: 11,
@@ -98,17 +106,23 @@
98 Series: "raring",106 Series: "raring",
99 Architectures: []string{"amd64"},107 Architectures: []string{"amd64"},
100 Endpoint: "some-auth-url",108 Endpoint: "some-auth-url",
101 Sources: []simplestreams.DataSource{simplestreams.NewURLDataSource("file://"+metadataDir, simplestreams.VerifySSLHostnames)},109 Sources: []simplestreams.DataSource{
110 simplestreams.NewURLDataSource("test", s.toolsURL(), simplestreams.VerifySSLHostnames)},
102 },111 },
103 }112 }
104 versions, err := ValidateToolsMetadata(params)113 versions, resolveInfo, err := ValidateToolsMetadata(params)
105 c.Assert(err, gc.IsNil)114 c.Assert(err, gc.IsNil)
106 c.Assert(versions, gc.DeepEquals, []string{"1.11.2-raring-amd64"})115 c.Assert(versions, gc.DeepEquals, []string{"1.11.2-raring-amd64"})
116 c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{
117 Source: "test",
118 Signed: false,
119 IndexURL: "file://" + path.Join(s.metadataDir, "tools/streams/v1/index.json"),
120 MirrorURL: "",
121 })
107}122}
108123
109func (s *ValidateSuite) TestNoMatch(c *gc.C) {124func (s *ValidateSuite) TestNoMatch(c *gc.C) {
110 s.makeLocalMetadata(c, "1.11.2", "region-2", "raring", "some-auth-url")125 s.makeLocalMetadata(c, "1.11.2", "raring")
111 metadataDir := osenv.JujuHomePath("")
112 params := &ToolsMetadataLookupParams{126 params := &ToolsMetadataLookupParams{
113 Version: "1.11.2",127 Version: "1.11.2",
114 MetadataLookupParams: simplestreams.MetadataLookupParams{128 MetadataLookupParams: simplestreams.MetadataLookupParams{
@@ -116,9 +130,10 @@
116 Series: "precise",130 Series: "precise",
117 Architectures: []string{"amd64"},131 Architectures: []string{"amd64"},
118 Endpoint: "some-auth-url",132 Endpoint: "some-auth-url",
119 Sources: []simplestreams.DataSource{simplestreams.NewURLDataSource("file://"+metadataDir, simplestreams.VerifySSLHostnames)},133 Sources: []simplestreams.DataSource{
134 simplestreams.NewURLDataSource("test", s.toolsURL(), simplestreams.VerifySSLHostnames)},
120 },135 },
121 }136 }
122 _, err := ValidateToolsMetadata(params)137 _, _, err := ValidateToolsMetadata(params)
123 c.Assert(err, gc.Not(gc.IsNil))138 c.Assert(err, gc.Not(gc.IsNil))
124}139}
125140
=== modified file 'provider/azure/environ.go'
--- provider/azure/environ.go 2014-02-18 03:18:16 +0000
+++ provider/azure/environ.go 2014-03-04 05:51:00 +0000
@@ -860,9 +860,9 @@
860// GetImageSources returns a list of sources which are used to search for simplestreams image metadata.860// GetImageSources returns a list of sources which are used to search for simplestreams image metadata.
861func (env *azureEnviron) GetImageSources() ([]simplestreams.DataSource, error) {861func (env *azureEnviron) GetImageSources() ([]simplestreams.DataSource, error) {
862 sources := make([]simplestreams.DataSource, 1+len(baseURLs))862 sources := make([]simplestreams.DataSource, 1+len(baseURLs))
863 sources[0] = storage.NewStorageSimpleStreamsDataSource(env.Storage(), storage.BaseImagesPath)863 sources[0] = storage.NewStorageSimpleStreamsDataSource("cloud storage", env.Storage(), storage.BaseImagesPath)
864 for i, url := range baseURLs {864 for i, url := range baseURLs {
865 sources[i+1] = simplestreams.NewURLDataSource(url, simplestreams.VerifySSLHostnames)865 sources[i+1] = simplestreams.NewURLDataSource("Azure base URL", url, simplestreams.VerifySSLHostnames)
866 }866 }
867 return sources, nil867 return sources, nil
868}868}
@@ -871,7 +871,7 @@
871func (env *azureEnviron) GetToolsSources() ([]simplestreams.DataSource, error) {871func (env *azureEnviron) GetToolsSources() ([]simplestreams.DataSource, error) {
872 // Add the simplestreams source off the control bucket.872 // Add the simplestreams source off the control bucket.
873 sources := []simplestreams.DataSource{873 sources := []simplestreams.DataSource{
874 storage.NewStorageSimpleStreamsDataSource(env.Storage(), storage.BaseToolsPath)}874 storage.NewStorageSimpleStreamsDataSource("cloud storage", env.Storage(), storage.BaseToolsPath)}
875 return sources, nil875 return sources, nil
876}876}
877877
878878
=== modified file 'provider/azure/instancetype.go'
--- provider/azure/instancetype.go 2014-01-29 06:45:16 +0000
+++ provider/azure/instancetype.go 2014-03-04 05:51:00 +0000
@@ -142,7 +142,7 @@
142 return nil, err142 return nil, err
143 }143 }
144 indexPath := simplestreams.DefaultIndexPath144 indexPath := simplestreams.DefaultIndexPath
145 images, err := fetchImageMetadata(sources, indexPath, constraint, signedImageDataOnly)145 images, _, err := fetchImageMetadata(sources, indexPath, constraint, signedImageDataOnly)
146 if len(images) == 0 || errors.IsNotFoundError(err) {146 if len(images) == 0 || errors.IsNotFoundError(err) {
147 return nil, fmt.Errorf("no OS images found for location %q, series %q, architectures %q (and endpoint: %q)", location, series, arches, endpoint)147 return nil, fmt.Errorf("no OS images found for location %q, series %q, architectures %q (and endpoint: %q)", location, series, arches, endpoint)
148 } else if err != nil {148 } else if err != nil {
149149
=== modified file 'provider/azure/instancetype_test.go'
--- provider/azure/instancetype_test.go 2014-01-29 06:45:16 +0000
+++ provider/azure/instancetype_test.go 2014-03-04 05:51:00 +0000
@@ -482,8 +482,9 @@
482// It returns a cleanup function, which you must call when done.482// It returns a cleanup function, which you must call when done.
483func patchFetchImageMetadata(cannedResponse []*imagemetadata.ImageMetadata, cannedError error) func() {483func patchFetchImageMetadata(cannedResponse []*imagemetadata.ImageMetadata, cannedError error) func() {
484 original := fetchImageMetadata484 original := fetchImageMetadata
485 fetchImageMetadata = func([]simplestreams.DataSource, string, *imagemetadata.ImageConstraint, bool) ([]*imagemetadata.ImageMetadata, error) {485 fetchImageMetadata = func([]simplestreams.DataSource, string, *imagemetadata.ImageConstraint, bool) (
486 return cannedResponse, cannedError486 []*imagemetadata.ImageMetadata, *simplestreams.ResolveInfo, error) {
487 return cannedResponse, nil, cannedError
487 }488 }
488 return func() { fetchImageMetadata = original }489 return func() { fetchImageMetadata = original }
489}490}
490491
=== modified file 'provider/common/mock_test.go'
--- provider/common/mock_test.go 2013-10-15 04:38:40 +0000
+++ provider/common/mock_test.go 2014-03-04 05:51:00 +0000
@@ -72,7 +72,7 @@
72 if env.getToolsSources != nil {72 if env.getToolsSources != nil {
73 return env.getToolsSources()73 return env.getToolsSources()
74 }74 }
75 datasource := storage.NewStorageSimpleStreamsDataSource(env.Storage(), storage.BaseToolsPath)75 datasource := storage.NewStorageSimpleStreamsDataSource("test cloud storage", env.Storage(), storage.BaseToolsPath)
76 return []simplestreams.DataSource{datasource}, nil76 return []simplestreams.DataSource{datasource}, nil
77}77}
7878
7979
=== modified file 'provider/dummy/environs.go'
--- provider/dummy/environs.go 2014-02-20 08:23:40 +0000
+++ provider/dummy/environs.go 2014-03-04 05:51:00 +0000
@@ -529,13 +529,13 @@
529// GetImageSources returns a list of sources which are used to search for simplestreams image metadata.529// GetImageSources returns a list of sources which are used to search for simplestreams image metadata.
530func (e *environ) GetImageSources() ([]simplestreams.DataSource, error) {530func (e *environ) GetImageSources() ([]simplestreams.DataSource, error) {
531 return []simplestreams.DataSource{531 return []simplestreams.DataSource{
532 storage.NewStorageSimpleStreamsDataSource(e.Storage(), storage.BaseImagesPath)}, nil532 storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseImagesPath)}, nil
533}533}
534534
535// GetToolsSources returns a list of sources which are used to search for simplestreams tools metadata.535// GetToolsSources returns a list of sources which are used to search for simplestreams tools metadata.
536func (e *environ) GetToolsSources() ([]simplestreams.DataSource, error) {536func (e *environ) GetToolsSources() ([]simplestreams.DataSource, error) {
537 return []simplestreams.DataSource{537 return []simplestreams.DataSource{
538 storage.NewStorageSimpleStreamsDataSource(e.Storage(), storage.BaseToolsPath)}, nil538 storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseToolsPath)}, nil
539}539}
540540
541func (e *environ) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) error {541func (e *environ) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) error {
542542
=== modified file 'provider/ec2/ec2.go'
--- provider/ec2/ec2.go 2014-02-28 03:05:47 +0000
+++ provider/ec2/ec2.go 2014-03-04 05:51:00 +0000
@@ -1057,7 +1057,7 @@
1057func (e *environ) GetImageSources() ([]simplestreams.DataSource, error) {1057func (e *environ) GetImageSources() ([]simplestreams.DataSource, error) {
1058 // Add the simplestreams source off the control bucket.1058 // Add the simplestreams source off the control bucket.
1059 sources := []simplestreams.DataSource{1059 sources := []simplestreams.DataSource{
1060 storage.NewStorageSimpleStreamsDataSource(e.Storage(), storage.BaseImagesPath)}1060 storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseImagesPath)}
1061 return sources, nil1061 return sources, nil
1062}1062}
10631063
@@ -1065,6 +1065,6 @@
1065func (e *environ) GetToolsSources() ([]simplestreams.DataSource, error) {1065func (e *environ) GetToolsSources() ([]simplestreams.DataSource, error) {
1066 // Add the simplestreams source off the control bucket.1066 // Add the simplestreams source off the control bucket.
1067 sources := []simplestreams.DataSource{1067 sources := []simplestreams.DataSource{
1068 storage.NewStorageSimpleStreamsDataSource(e.Storage(), storage.BaseToolsPath)}1068 storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseToolsPath)}
1069 return sources, nil1069 return sources, nil
1070}1070}
10711071
=== modified file 'provider/ec2/image.go'
--- provider/ec2/image.go 2014-01-29 00:14:51 +0000
+++ provider/ec2/image.go 2014-03-04 05:51:00 +0000
@@ -48,7 +48,7 @@
48 Arches: ic.Arches,48 Arches: ic.Arches,
49 Stream: stream,49 Stream: stream,
50 })50 })
51 matchingImages, err := imagemetadata.Fetch(51 matchingImages, _, err := imagemetadata.Fetch(
52 sources, simplestreams.DefaultIndexPath, imageConstraint, signedImageDataOnly)52 sources, simplestreams.DefaultIndexPath, imageConstraint, signedImageDataOnly)
53 if err != nil {53 if err != nil {
54 return nil, err54 return nil, err
5555
=== modified file 'provider/ec2/image_test.go'
--- provider/ec2/image_test.go 2014-02-14 01:03:03 +0000
+++ provider/ec2/image_test.go 2014-03-04 05:51:00 +0000
@@ -128,7 +128,8 @@
128 c.Logf("test %d", i)128 c.Logf("test %d", i)
129 stor := ebsStorage129 stor := ebsStorage
130 spec, err := findInstanceSpec(130 spec, err := findInstanceSpec(
131 []simplestreams.DataSource{simplestreams.NewURLDataSource("test:", simplestreams.VerifySSLHostnames)},131 []simplestreams.DataSource{
132 simplestreams.NewURLDataSource("test", "test:", simplestreams.VerifySSLHostnames)},
132 "released",133 "released",
133 &instances.InstanceConstraint{134 &instances.InstanceConstraint{
134 Region: "test",135 Region: "test",
@@ -169,7 +170,8 @@
169 for i, t := range findInstanceSpecErrorTests {170 for i, t := range findInstanceSpecErrorTests {
170 c.Logf("test %d", i)171 c.Logf("test %d", i)
171 _, err := findInstanceSpec(172 _, err := findInstanceSpec(
172 []simplestreams.DataSource{simplestreams.NewURLDataSource("test:", simplestreams.VerifySSLHostnames)},173 []simplestreams.DataSource{
174 simplestreams.NewURLDataSource("test", "test:", simplestreams.VerifySSLHostnames)},
173 "released",175 "released",
174 &instances.InstanceConstraint{176 &instances.InstanceConstraint{
175 Region: "test",177 Region: "test",
176178
=== modified file 'provider/ec2/local_test.go'
--- provider/ec2/local_test.go 2014-02-20 08:23:40 +0000
+++ provider/ec2/local_test.go 2014-03-04 05:51:00 +0000
@@ -383,7 +383,7 @@
383 params.Endpoint = "https://ec2.endpoint.com"383 params.Endpoint = "https://ec2.endpoint.com"
384 params.Sources, err = imagemetadata.GetMetadataSources(env)384 params.Sources, err = imagemetadata.GetMetadataSources(env)
385 c.Assert(err, gc.IsNil)385 c.Assert(err, gc.IsNil)
386 image_ids, err := imagemetadata.ValidateImageMetadata(params)386 image_ids, _, err := imagemetadata.ValidateImageMetadata(params)
387 c.Assert(err, gc.IsNil)387 c.Assert(err, gc.IsNil)
388 sort.Strings(image_ids)388 sort.Strings(image_ids)
389 c.Assert(image_ids, gc.DeepEquals, []string{"ami-00000033", "ami-00000034", "ami-00000035"})389 c.Assert(image_ids, gc.DeepEquals, []string{"ami-00000033", "ami-00000034", "ami-00000035"})
390390
=== modified file 'provider/local/environ.go'
--- provider/local/environ.go 2014-02-28 09:13:21 +0000
+++ provider/local/environ.go 2014-03-04 05:51:00 +0000
@@ -65,7 +65,7 @@
65func (e *localEnviron) GetToolsSources() ([]simplestreams.DataSource, error) {65func (e *localEnviron) GetToolsSources() ([]simplestreams.DataSource, error) {
66 // Add the simplestreams source off the control bucket.66 // Add the simplestreams source off the control bucket.
67 return []simplestreams.DataSource{67 return []simplestreams.DataSource{
68 storage.NewStorageSimpleStreamsDataSource(e.Storage(), storage.BaseToolsPath)}, nil68 storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseToolsPath)}, nil
69}69}
7070
71// Name is specified in the Environ interface.71// Name is specified in the Environ interface.
7272
=== modified file 'provider/maas/environ.go'
--- provider/maas/environ.go 2013-12-19 09:15:40 +0000
+++ provider/maas/environ.go 2014-03-04 05:51:00 +0000
@@ -421,12 +421,12 @@
421func (e *maasEnviron) GetImageSources() ([]simplestreams.DataSource, error) {421func (e *maasEnviron) GetImageSources() ([]simplestreams.DataSource, error) {
422 // Add the simplestreams source off the control bucket.422 // Add the simplestreams source off the control bucket.
423 return []simplestreams.DataSource{423 return []simplestreams.DataSource{
424 storage.NewStorageSimpleStreamsDataSource(e.Storage(), storage.BaseImagesPath)}, nil424 storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseImagesPath)}, nil
425}425}
426426
427// GetToolsSources returns a list of sources which are used to search for simplestreams tools metadata.427// GetToolsSources returns a list of sources which are used to search for simplestreams tools metadata.
428func (e *maasEnviron) GetToolsSources() ([]simplestreams.DataSource, error) {428func (e *maasEnviron) GetToolsSources() ([]simplestreams.DataSource, error) {
429 // Add the simplestreams source off the control bucket.429 // Add the simplestreams source off the control bucket.
430 return []simplestreams.DataSource{430 return []simplestreams.DataSource{
431 storage.NewStorageSimpleStreamsDataSource(e.Storage(), storage.BaseToolsPath)}, nil431 storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseToolsPath)}, nil
432}432}
433433
=== modified file 'provider/manual/environ.go'
--- provider/manual/environ.go 2014-02-20 08:23:40 +0000
+++ provider/manual/environ.go 2014-03-04 05:51:00 +0000
@@ -200,7 +200,7 @@
200func (e *manualEnviron) GetToolsSources() ([]simplestreams.DataSource, error) {200func (e *manualEnviron) GetToolsSources() ([]simplestreams.DataSource, error) {
201 // Add the simplestreams source off private storage.201 // Add the simplestreams source off private storage.
202 return []simplestreams.DataSource{202 return []simplestreams.DataSource{
203 storage.NewStorageSimpleStreamsDataSource(e.Storage(), storage.BaseToolsPath),203 storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseToolsPath),
204 }, nil204 }, nil
205}205}
206206
207207
=== modified file 'provider/openstack/image.go'
--- provider/openstack/image.go 2014-01-29 06:45:16 +0000
+++ provider/openstack/image.go 2014-03-04 05:51:00 +0000
@@ -43,7 +43,7 @@
43 return nil, err43 return nil, err
44 }44 }
45 // TODO (wallyworld): use an env parameter (default true) to mandate use of only signed image metadata.45 // TODO (wallyworld): use an env parameter (default true) to mandate use of only signed image metadata.
46 matchingImages, err := imagemetadata.Fetch(sources, simplestreams.DefaultIndexPath, imageConstraint, false)46 matchingImages, _, err := imagemetadata.Fetch(sources, simplestreams.DefaultIndexPath, imageConstraint, false)
47 if err != nil {47 if err != nil {
48 return nil, err48 return nil, err
49 }49 }
5050
=== modified file 'provider/openstack/local_test.go'
--- provider/openstack/local_test.go 2014-02-20 08:23:40 +0000
+++ provider/openstack/local_test.go 2014-03-04 05:51:00 +0000
@@ -605,7 +605,7 @@
605 params.Sources, err = imagemetadata.GetMetadataSources(env)605 params.Sources, err = imagemetadata.GetMetadataSources(env)
606 c.Assert(err, gc.IsNil)606 c.Assert(err, gc.IsNil)
607 params.Series = "raring"607 params.Series = "raring"
608 image_ids, err := imagemetadata.ValidateImageMetadata(params)608 image_ids, _, err := imagemetadata.ValidateImageMetadata(params)
609 c.Assert(err, gc.IsNil)609 c.Assert(err, gc.IsNil)
610 c.Assert(image_ids, gc.DeepEquals, []string{"id-y"})610 c.Assert(image_ids, gc.DeepEquals, []string{"id-y"})
611}611}
612612
=== modified file 'provider/openstack/provider.go'
--- provider/openstack/provider.go 2014-02-28 03:05:47 +0000
+++ provider/openstack/provider.go 2014-03-04 05:51:00 +0000
@@ -585,7 +585,7 @@
585 }585 }
586 // Add the simplestreams source off the control bucket.586 // Add the simplestreams source off the control bucket.
587 e.imageSources = append(e.imageSources, storage.NewStorageSimpleStreamsDataSource(587 e.imageSources = append(e.imageSources, storage.NewStorageSimpleStreamsDataSource(
588 e.Storage(), storage.BaseImagesPath))588 "cloud storage", e.Storage(), storage.BaseImagesPath))
589 // Add the simplestreams base URL from keystone if it is defined.589 // Add the simplestreams base URL from keystone if it is defined.
590 productStreamsURL, err := e.client.MakeServiceURL("product-streams", nil)590 productStreamsURL, err := e.client.MakeServiceURL("product-streams", nil)
591 if err == nil {591 if err == nil {
@@ -593,7 +593,7 @@
593 if !e.Config().SSLHostnameVerification() {593 if !e.Config().SSLHostnameVerification() {
594 verify = simplestreams.NoVerifySSLHostnames594 verify = simplestreams.NoVerifySSLHostnames
595 }595 }
596 source := simplestreams.NewURLDataSource(productStreamsURL, verify)596 source := simplestreams.NewURLDataSource("keystone catalog", productStreamsURL, verify)
597 e.imageSources = append(e.imageSources, source)597 e.imageSources = append(e.imageSources, source)
598 }598 }
599 return e.imageSources, nil599 return e.imageSources, nil
@@ -618,11 +618,12 @@
618 verify = simplestreams.NoVerifySSLHostnames618 verify = simplestreams.NoVerifySSLHostnames
619 }619 }
620 // Add the simplestreams source off the control bucket.620 // Add the simplestreams source off the control bucket.
621 e.toolsSources = append(e.toolsSources, storage.NewStorageSimpleStreamsDataSource(e.Storage(), storage.BaseToolsPath))621 e.toolsSources = append(e.toolsSources, storage.NewStorageSimpleStreamsDataSource(
622 "cloud storage", e.Storage(), storage.BaseToolsPath))
622 // Add the simplestreams base URL from keystone if it is defined.623 // Add the simplestreams base URL from keystone if it is defined.
623 toolsURL, err := e.client.MakeServiceURL("juju-tools", nil)624 toolsURL, err := e.client.MakeServiceURL("juju-tools", nil)
624 if err == nil {625 if err == nil {
625 source := simplestreams.NewURLDataSource(toolsURL, verify)626 source := simplestreams.NewURLDataSource("keystone catalog", toolsURL, verify)
626 e.toolsSources = append(e.toolsSources, source)627 e.toolsSources = append(e.toolsSources, source)
627 }628 }
628 return e.toolsSources, nil629 return e.toolsSources, nil

Subscribers

People subscribed via source and target branches

to status/vote changes: