// GetMetadataSourcesWithRetries returns the sources to use when looking for // simplestreams tools metadata. If env implements SupportsCustomSurces, // the sources returned from that method will also be considered. // The sources are configured to use retries according to the value of allowRetry. func GetMetadataSourcesWithRetries(env environs.ConfigGetter, allowRetry bool) ([]simplestreams.DataSource, error) { var sources []simplestreams.DataSource config := env.Config() if userURL, ok := config.ToolsURL(); ok { verify := utils.VerifySSLHostnames if !config.SSLHostnameVerification() { verify = utils.NoVerifySSLHostnames } sources = append(sources, simplestreams.NewURLDataSource("tools-metadata-url", userURL, verify)) } if custom, ok := env.(SupportsCustomSources); ok { customSources, err := custom.GetToolsSources() if err != nil { return nil, err } sources = append(sources, customSources...) } defaultURL, err := ToolsURL(DefaultBaseURL) if err != nil { return nil, err } if defaultURL != "" { sources = append(sources, simplestreams.NewURLDataSource("default simplestreams", defaultURL, utils.VerifySSLHostnames)) } for _, source := range sources { source.SetAllowRetry(allowRetry) } return sources, nil }
// GetMetadataSources returns the sources to use when looking for // simplestreams image id metadata for the given stream. If env implements // SupportsCustomSources, the sources returned from that method will also // be considered. func GetMetadataSources(env environs.ConfigGetter) ([]simplestreams.DataSource, error) { var sources []simplestreams.DataSource config := env.Config() if userURL, ok := config.ImageMetadataURL(); ok { verify := utils.VerifySSLHostnames if !config.SSLHostnameVerification() { verify = utils.NoVerifySSLHostnames } sources = append(sources, simplestreams.NewURLDataSource("image-metadata-url", userURL, verify)) } if custom, ok := env.(SupportsCustomSources); ok { customSources, err := custom.GetImageSources() if err != nil { return nil, err } sources = append(sources, customSources...) } defaultURL, err := ImageMetadataURL(DefaultBaseURL, config.ImageStream()) if err != nil { return nil, err } if defaultURL != "" { sources = append(sources, simplestreams.NewURLDataSource("default cloud images", defaultURL, utils.VerifySSLHostnames)) } return sources, nil }
func (s *simplestreamsSuite) TestFetch(c *gc.C) { for i, t := range fetchTests { c.Logf("test %d", i) cloudSpec := simplestreams.CloudSpec{t.region, "https://ec2.us-east-1.amazonaws.com"} if t.region == "" { cloudSpec = simplestreams.EmptyCloudSpec } imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: cloudSpec, Series: []string{"precise"}, Arches: t.arches, }) // Add invalid datasource and check later that resolveInfo is correct. invalidSource := simplestreams.NewURLDataSource("invalid", "file://invalid", utils.VerifySSLHostnames) images, resolveInfo, err := imagemetadata.Fetch( []simplestreams.DataSource{invalidSource, s.Source}, simplestreams.DefaultIndexPath, imageConstraint, s.RequireSigned) if !c.Check(err, gc.IsNil) { continue } for _, testImage := range t.images { testImage.Version = t.version } c.Check(images, gc.DeepEquals, t.images) c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{ Source: "test roundtripper", Signed: s.RequireSigned, IndexURL: "test:/streams/v1/index.json", MirrorURL: "", }) } }
func registerLiveSimpleStreamsTests(baseURL string, validImageConstraint simplestreams.LookupConstraint, requireSigned bool) { gc.Suite(&sstesting.LocalLiveSimplestreamsSuite{ Source: simplestreams.NewURLDataSource("test", baseURL, utils.VerifySSLHostnames), RequireSigned: requireSigned, DataType: imagemetadata.ImageIds, ValidConstraint: validImageConstraint, }) }
func registerLiveSimpleStreamsTests(baseURL string, validToolsConstraint simplestreams.LookupConstraint, requireSigned bool) { gc.Suite(&sstesting.LocalLiveSimplestreamsSuite{ Source: simplestreams.NewURLDataSource("test", baseURL, utils.VerifySSLHostnames), RequireSigned: requireSigned, DataType: tools.ContentDownload, ValidConstraint: validToolsConstraint, }) }
func (s *datasourceSuite) TestFetch(c *gc.C) { ds := simplestreams.NewURLDataSource("test", "test:", utils.VerifySSLHostnames) rc, url, err := ds.Fetch("streams/v1/tools_metadata.json") c.Assert(err, gc.IsNil) defer rc.Close() c.Assert(url, gc.Equals, "test:/streams/v1/tools_metadata.json") data, err := ioutil.ReadAll(rc) cloudMetadata, err := simplestreams.ParseCloudMetadata(data, "products:1.0", url, imagemetadata.ImageMetadata{}) c.Assert(err, gc.IsNil) c.Assert(len(cloudMetadata.Products), jc.GreaterThan, 0) }
func (s *datasourceHTTPSSuite) TestNormalClientFails(c *gc.C) { ds := simplestreams.NewURLDataSource("test", s.Server.URL, utils.VerifySSLHostnames) url, err := ds.URL("bar") c.Assert(err, gc.IsNil) c.Check(url, gc.Equals, s.Server.URL+"/bar") reader, _, err := ds.Fetch("bar") // The underlying failure is a x509: certificate signed by unknown authority // However, the urlDataSource abstraction hides that as a simple NotFound c.Assert(err, gc.ErrorMatches, "invalid URL \".*/bar\" not found") c.Check(reader, gc.IsNil) }
// selectSourceDatasource returns a storage reader based on the source setting. func selectSourceDatasource(syncContext *SyncContext) (simplestreams.DataSource, error) { source := syncContext.Source if source == "" { source = envtools.DefaultBaseURL } sourceURL, err := envtools.ToolsURL(source) if err != nil { return nil, err } logger.Infof("using sync tools source: %v", sourceURL) return simplestreams.NewURLDataSource("sync tools source", sourceURL, utils.VerifySSLHostnames), nil }
func (s *signedSuite) TestSignedImageMetadataInvalidSignature(c *gc.C) { signedSource := simplestreams.NewURLDataSource("test", "signedtest://host/signed", utils.VerifySSLHostnames) imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{"us-east-1", "https://ec2.us-east-1.amazonaws.com"}, Series: []string{"precise"}, Arches: []string{"amd64"}, }) imagemetadata.SetSigningPublicKey(s.origKey) _, _, err := imagemetadata.Fetch( []simplestreams.DataSource{signedSource}, simplestreams.DefaultIndexPath, imageConstraint, true) c.Assert(err, gc.ErrorMatches, "cannot read index data.*") }
func (s *ValidateSuite) assertNoMatch(c *gc.C, stream string) { s.makeLocalMetadata(c, "1234", "region-2", "raring", "some-auth-url", stream) params := &simplestreams.MetadataLookupParams{ Region: "region-2", Series: "precise", Architectures: []string{"amd64"}, Endpoint: "some-auth-url", Stream: stream, Sources: []simplestreams.DataSource{ simplestreams.NewURLDataSource("test", "file://"+s.metadataDir, utils.VerifySSLHostnames)}, } _, _, err := imagemetadata.ValidateImageMetadata(params) c.Assert(err, gc.Not(gc.IsNil)) }
func (s *datasourceHTTPSSuite) TestNonVerifyingClientSucceeds(c *gc.C) { ds := simplestreams.NewURLDataSource("test", s.Server.URL, utils.NoVerifySSLHostnames) url, err := ds.URL("bar") c.Assert(err, gc.IsNil) c.Check(url, gc.Equals, s.Server.URL+"/bar") reader, _, err := ds.Fetch("bar") // The underlying failure is a x509: certificate signed by unknown authority // However, the urlDataSource abstraction hides that as a simple NotFound c.Assert(err, gc.IsNil) defer reader.Close() byteContent, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) c.Check(string(byteContent), gc.Equals, "Greetings!\n") }
func (s *ValidateSuite) TestNoMatch(c *gc.C) { s.makeLocalMetadata(c, "1.11.2", "raring") params := &ToolsMetadataLookupParams{ Version: "1.11.2", MetadataLookupParams: simplestreams.MetadataLookupParams{ Region: "region-2", Series: "precise", Architectures: []string{"amd64"}, Endpoint: "some-auth-url", Sources: []simplestreams.DataSource{ simplestreams.NewURLDataSource("test", s.toolsURL(), utils.VerifySSLHostnames)}, }, } _, _, err := ValidateToolsMetadata(params) c.Assert(err, gc.Not(gc.IsNil)) }
func (s *specSuite) TestFindInstanceSpecErrors(c *gc.C) { for i, t := range findInstanceSpecErrorTests { c.Logf("test %d", i) _, err := findInstanceSpec( []simplestreams.DataSource{ simplestreams.NewURLDataSource("test", "test:", utils.VerifySSLHostnames)}, "released", &instances.InstanceConstraint{ Region: "test", Series: t.series, Arches: t.arches, Constraints: constraints.MustParse(t.cons), }) c.Check(err, gc.ErrorMatches, t.err) } }
func (s *imageSuite) TestFindInstanceSpec(c *gc.C) { for _, t := range findInstanceSpecTests { c.Logf("test: %v", t.desc) t.init() cons := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{t.region, "ep"}, Series: []string{"precise"}, Arches: t.arches, Stream: t.stream, }) imageMeta, err := imagemetadata.GetLatestImageIdMetadata( []byte(jsonImagesContent), simplestreams.NewURLDataSource("test", "some-url", utils.VerifySSLHostnames), cons) c.Assert(err, gc.IsNil) var images []Image for _, imageMetadata := range imageMeta { im := *imageMetadata images = append(images, Image{ Id: im.Id, VirtType: im.VirtType, Arch: im.Arch, }) } imageCons := constraints.MustParse(t.constraints) spec, err := FindInstanceSpec(images, &InstanceConstraint{ Series: "precise", Region: t.region, Arches: t.arches, Constraints: imageCons, }, t.instanceTypes) if t.err != "" { c.Check(err, gc.ErrorMatches, t.err) continue } else { if !c.Check(err, gc.IsNil) { continue } c.Check(spec.Image.Id, gc.Equals, t.imageId) if len(t.instanceTypes) == 1 { c.Check(spec.InstanceType, gc.DeepEquals, t.instanceTypes[0]) } if imageCons.HasInstanceType() { c.Assert(spec.InstanceType.Name, gc.Equals, *imageCons.InstanceType) } } } }
func registerSimpleStreamsTests() { gc.Suite(&simplestreamsSuite{ LocalLiveSimplestreamsSuite: sstesting.LocalLiveSimplestreamsSuite{ Source: simplestreams.NewURLDataSource("test", "test:", utils.VerifySSLHostnames), RequireSigned: false, DataType: "image-ids", ValidConstraint: sstesting.NewTestConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{ Region: "us-east-1", Endpoint: "https://ec2.us-east-1.amazonaws.com", }, Series: []string{"precise"}, Arches: []string{"amd64", "arm"}, }), }, }) }
func registerSimpleStreamsTests() { gc.Suite(&simplestreamsSuite{ LocalLiveSimplestreamsSuite: sstesting.LocalLiveSimplestreamsSuite{ Source: simplestreams.NewURLDataSource("test", "test:", utils.VerifySSLHostnames), RequireSigned: false, DataType: tools.ContentDownload, ValidConstraint: tools.NewVersionedToolsConstraint(version.MustParse("1.13.0"), simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{ Region: "us-east-1", Endpoint: "https://ec2.us-east-1.amazonaws.com", }, Series: []string{"precise"}, Arches: []string{"amd64", "arm"}, }), }, }) gc.Suite(&signedSuite{}) }
func (s *signedSuite) TestSignedImageMetadata(c *gc.C) { signedSource := simplestreams.NewURLDataSource("test", "signedtest://host/signed", utils.VerifySSLHostnames) imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{"us-east-1", "https://ec2.us-east-1.amazonaws.com"}, Series: []string{"precise"}, Arches: []string{"amd64"}, }) images, resolveInfo, err := imagemetadata.Fetch( []simplestreams.DataSource{signedSource}, simplestreams.DefaultIndexPath, imageConstraint, true) c.Assert(err, gc.IsNil) c.Assert(len(images), gc.Equals, 1) c.Assert(images[0].Id, gc.Equals, "ami-123456") c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{ Source: "test", Signed: true, IndexURL: "signedtest://host/signed/streams/v1/index.sjson", MirrorURL: "", }) }
func (s *signedSuite) TestSignedToolsMetadata(c *gc.C) { signedSource := simplestreams.NewURLDataSource("test", "signedtest://host/signed", utils.VerifySSLHostnames) toolsConstraint := tools.NewVersionedToolsConstraint(version.MustParse("1.13.0"), simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{"us-east-1", "https://ec2.us-east-1.amazonaws.com"}, Series: []string{"precise"}, Arches: []string{"amd64"}, }) toolsMetadata, resolveInfo, err := tools.Fetch( []simplestreams.DataSource{signedSource}, simplestreams.DefaultIndexPath, toolsConstraint, true) c.Assert(err, gc.IsNil) c.Assert(len(toolsMetadata), gc.Equals, 1) c.Assert(toolsMetadata[0].Path, gc.Equals, "tools/releases/20130806/juju-1.13.1-precise-amd64.tgz") c.Assert(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{ Source: "test", Signed: true, IndexURL: "signedtest://host/signed/streams/v1/index.sjson", MirrorURL: "", }) }
func (s *specSuite) TestFindInstanceSpec(c *gc.C) { for i, test := range findInstanceSpecTests { c.Logf("\ntest %d: %q; %q; %q", i, test.series, test.arches, test.cons) stor := ebsStorage spec, err := findInstanceSpec( []simplestreams.DataSource{ simplestreams.NewURLDataSource("test", "test:", utils.VerifySSLHostnames)}, "released", &instances.InstanceConstraint{ Region: "test", Series: test.series, Arches: test.arches, Constraints: constraints.MustParse(test.cons), Storage: &stor, }) c.Assert(err, gc.IsNil) c.Check(spec.InstanceType.Name, gc.Equals, test.itype) c.Check(spec.Image.Id, gc.Equals, test.image) } }
func (c *ToolsMetadataCommand) Run(context *cmd.Context) error { loggo.RegisterWriter("toolsmetadata", cmd.NewCommandLogWriter("juju.environs.tools", context.Stdout, context.Stderr), loggo.INFO) defer loggo.RemoveWriter("toolsmetadata") if c.metadataDir == "" { c.metadataDir = osenv.JujuHome() } else { c.metadataDir = context.AbsPath(c.metadataDir) } sourceStorage, err := filestorage.NewFileStorageReader(c.metadataDir) if err != nil { return err } fmt.Fprintf(context.Stdout, "Finding tools in %s\n", c.metadataDir) const minorVersion = -1 toolsList, err := envtools.ReadList(sourceStorage, version.Current.Major, minorVersion) if err == envtools.ErrNoTools { var source string source, err = envtools.ToolsURL(envtools.DefaultBaseURL) if err != nil { return err } sourceDataSource := simplestreams.NewURLDataSource("local source", source, utils.VerifySSLHostnames) toolsList, err = envtools.FindToolsForCloud( []simplestreams.DataSource{sourceDataSource}, simplestreams.CloudSpec{}, version.Current.Major, minorVersion, coretools.Filter{}) } if err != nil { return err } targetStorage, err := filestorage.NewFileStorageWriter(c.metadataDir) if err != nil { return err } writeMirrors := envtools.DoNotWriteMirrors if c.public { writeMirrors = envtools.WriteMirrors } return mergeAndWriteMetadata(targetStorage, toolsList, writeMirrors) }
func (s *ValidateSuite) assertMatch(c *gc.C, stream string) { s.makeLocalMetadata(c, "1234", "region-2", "raring", "some-auth-url", stream) metadataPath := filepath.Join(s.metadataDir, "images") params := &simplestreams.MetadataLookupParams{ Region: "region-2", Series: "raring", Architectures: []string{"amd64"}, Endpoint: "some-auth-url", Stream: stream, Sources: []simplestreams.DataSource{ simplestreams.NewURLDataSource("test", "file://"+metadataPath, utils.VerifySSLHostnames)}, } imageIds, resolveInfo, err := imagemetadata.ValidateImageMetadata(params) c.Assert(err, gc.IsNil) c.Assert(imageIds, gc.DeepEquals, []string{"1234"}) c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{ Source: "test", Signed: false, IndexURL: "file://" + path.Join(metadataPath, "streams/v1/index.json"), MirrorURL: "", }) }
func (s *simplestreamsSuite) TestFetch(c *gc.C) { for i, t := range fetchTests { c.Logf("test %d", i) var toolsConstraint *tools.ToolsConstraint if t.version == "" { toolsConstraint = tools.NewGeneralToolsConstraint(t.major, t.minor, t.released, simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{"us-east-1", "https://ec2.us-east-1.amazonaws.com"}, Series: []string{t.series}, Arches: t.arches, }) } else { toolsConstraint = tools.NewVersionedToolsConstraint(version.MustParse(t.version), simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{"us-east-1", "https://ec2.us-east-1.amazonaws.com"}, Series: []string{t.series}, Arches: t.arches, }) } // Add invalid datasource and check later that resolveInfo is correct. invalidSource := simplestreams.NewURLDataSource("invalid", "file://invalid", utils.VerifySSLHostnames) tools, resolveInfo, err := tools.Fetch( []simplestreams.DataSource{invalidSource, s.Source}, simplestreams.DefaultIndexPath, toolsConstraint, s.RequireSigned) if !c.Check(err, gc.IsNil) { continue } for _, tm := range t.tools { tm.FullPath, err = s.Source.URL(tm.Path) c.Assert(err, gc.IsNil) } c.Check(tools, gc.DeepEquals, t.tools) c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{ Source: "test", Signed: s.RequireSigned, IndexURL: "test:/streams/v1/index.json", MirrorURL: "", }) } }
func (s *ValidateSuite) TestExactVersionMatch(c *gc.C) { s.makeLocalMetadata(c, "1.11.2", "raring") params := &ToolsMetadataLookupParams{ Version: "1.11.2", MetadataLookupParams: simplestreams.MetadataLookupParams{ Region: "region-2", Series: "raring", Architectures: []string{"amd64"}, Endpoint: "some-auth-url", Sources: []simplestreams.DataSource{ simplestreams.NewURLDataSource("test", s.toolsURL(), utils.VerifySSLHostnames)}, }, } versions, resolveInfo, err := ValidateToolsMetadata(params) c.Assert(err, gc.IsNil) c.Assert(versions, gc.DeepEquals, []string{"1.11.2-raring-amd64"}) c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{ Source: "test", Signed: false, IndexURL: "file://" + path.Join(s.metadataDir, "tools/streams/v1/index.json"), MirrorURL: "", }) }
func (s *simplestreamsSuite) TestGetMetadataNoMatching(c *gc.C) { source := &countingSource{ DataSource: simplestreams.NewURLDataSource( "test", "test:/daily", utils.VerifySSLHostnames, ), } sources := []simplestreams.DataSource{source, source, source} params := simplestreams.ValueParams{DataType: "image-ids"} constraint := sstesting.NewTestConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{ Region: "us-east-1", Endpoint: "https://ec2.us-east-1.amazonaws.com", }, Series: []string{"precise"}, Arches: []string{"not-a-real-arch"}, // never matches }) items, resolveInfo, err := simplestreams.GetMetadata( sources, simplestreams.DefaultIndexPath, constraint, false, params, ) c.Assert(err, gc.IsNil) c.Assert(items, gc.HasLen, 0) c.Assert(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{ Source: "test", Signed: false, IndexURL: "test:/daily/streams/v1/index.json", MirrorURL: "", }) // There should be 2 calls to each data-source: // one for .sjson, one for .json. c.Assert(source.count, gc.Equals, 2*len(sources)) }
func (c *ValidateToolsMetadataCommand) Run(context *cmd.Context) error { var params *simplestreams.MetadataLookupParams if c.providerType == "" { store, err := configstore.Default() if err != nil { return err } environ, err := environs.PrepareFromName(c.EnvName, context, store) if err == nil { mdLookup, ok := environ.(simplestreams.MetadataValidator) if !ok { return fmt.Errorf("%s provider does not support tools metadata validation", environ.Config().Type()) } params, err = mdLookup.MetadataLookupParams(c.region) if err != nil { return err } params.Sources, err = tools.GetMetadataSources(environ) if err != nil { return err } } else { if c.metadataDir == "" { return err } params = &simplestreams.MetadataLookupParams{ Architectures: arch.AllSupportedArches, } } } else { prov, err := environs.Provider(c.providerType) if err != nil { return err } mdLookup, ok := prov.(simplestreams.MetadataValidator) if !ok { return fmt.Errorf("%s provider does not support tools metadata validation", c.providerType) } params, err = mdLookup.MetadataLookupParams(c.region) if err != nil { return err } } if c.series != "" { params.Series = c.series } if c.region != "" { params.Region = c.region } if c.endpoint != "" { params.Endpoint = c.endpoint } if c.metadataDir != "" { if _, err := os.Stat(c.metadataDir); err != nil { return err } toolsURL, err := tools.ToolsURL(c.metadataDir) if err != nil { return err } params.Sources = []simplestreams.DataSource{simplestreams.NewURLDataSource( "local metadata directory", toolsURL, utils.VerifySSLHostnames), } } versions, resolveInfo, err := tools.ValidateToolsMetadata(&tools.ToolsMetadataLookupParams{ MetadataLookupParams: *params, Version: c.exactVersion, Major: c.major, Minor: c.minor, }) if err != nil { if resolveInfo != nil { metadata := map[string]interface{}{ "Resolve Metadata": *resolveInfo, } if metadataYaml, yamlErr := cmd.FormatYaml(metadata); yamlErr == nil { err = fmt.Errorf("%v\n%v", err, string(metadataYaml)) } } return err } if len(versions) > 0 { metadata := map[string]interface{}{ "Matching Tools Versions": versions, "Resolve Metadata": *resolveInfo, } c.out.Write(context, metadata) } else { var sources []string for _, s := range params.Sources { url, err := s.URL("") if err == nil { sources = append(sources, fmt.Sprintf("- %s (%s)", s.Description(), url)) } } return fmt.Errorf("no matching tools using sources:\n%s", strings.Join(sources, "\n")) } return nil }
func (c *ValidateImageMetadataCommand) Run(context *cmd.Context) error { var params *simplestreams.MetadataLookupParams if c.providerType == "" { store, err := configstore.Default() if err != nil { return err } environ, err := environs.PrepareFromName(c.EnvName, context, store) if err != nil { return err } mdLookup, ok := environ.(simplestreams.MetadataValidator) if !ok { return fmt.Errorf("%s provider does not support image metadata validation", environ.Config().Type()) } params, err = mdLookup.MetadataLookupParams(c.region) if err != nil { return err } oes := &overrideEnvStream{environ, c.stream} params.Sources, err = imagemetadata.GetMetadataSources(oes) if err != nil { return err } } else { prov, err := environs.Provider(c.providerType) if err != nil { return err } mdLookup, ok := prov.(simplestreams.MetadataValidator) if !ok { return fmt.Errorf("%s provider does not support image metadata validation", c.providerType) } params, err = mdLookup.MetadataLookupParams(c.region) if err != nil { return err } } if c.series != "" { params.Series = c.series } if c.region != "" { params.Region = c.region } if c.endpoint != "" { params.Endpoint = c.endpoint } if c.metadataDir != "" { dir := filepath.Join(c.metadataDir, "images") if _, err := os.Stat(dir); err != nil { return err } params.Sources = []simplestreams.DataSource{ simplestreams.NewURLDataSource( "local metadata directory", "file://"+dir, utils.VerifySSLHostnames), } } params.Stream = c.stream image_ids, resolveInfo, err := imagemetadata.ValidateImageMetadata(params) if err != nil { if resolveInfo != nil { metadata := map[string]interface{}{ "Resolve Metadata": *resolveInfo, } if metadataYaml, yamlErr := cmd.FormatYaml(metadata); yamlErr == nil { err = fmt.Errorf("%v\n%v", err, string(metadataYaml)) } } return err } if len(image_ids) > 0 { metadata := map[string]interface{}{ "ImageIds": image_ids, "Region": params.Region, "Resolve Metadata": *resolveInfo, } c.out.Write(context, metadata) } else { var sources []string for _, s := range params.Sources { url, err := s.URL("") if err == nil { sources = append(sources, fmt.Sprintf("- %s (%s)", s.Description(), url)) } } return fmt.Errorf( "no matching image ids for region %s using sources:\n%s", params.Region, strings.Join(sources, "\n")) } return nil }
func (s *datasourceSuite) TestURL(c *gc.C) { ds := simplestreams.NewURLDataSource("test", "foo", utils.VerifySSLHostnames) url, err := ds.URL("bar") c.Assert(err, gc.IsNil) c.Assert(url, gc.Equals, "foo/bar") }