// setPrivateMetadataSources sets the default tools metadata source // for tools syncing, and adds an image metadata source after verifying // the contents. func setPrivateMetadataSources(metadataDir string) ([]*imagemetadata.ImageMetadata, error) { logger.Infof("Setting default tools and image metadata sources: %s", metadataDir) tools.DefaultBaseURL = metadataDir imageMetadataDir := filepath.Join(metadataDir, storage.BaseImagesPath) if _, err := os.Stat(imageMetadataDir); err != nil { if !os.IsNotExist(err) { return nil, errors.Annotate(err, "cannot access image metadata") } return nil, nil } baseURL := fmt.Sprintf("file://%s", filepath.ToSlash(imageMetadataDir)) publicKey, _ := simplestreams.UserPublicSigningKey() datasource := simplestreams.NewURLSignedDataSource("bootstrap metadata", baseURL, publicKey, utils.NoVerifySSLHostnames, simplestreams.CUSTOM_CLOUD_DATA, false) // Read the image metadata, as we'll want to upload it to the environment. imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{}) existingMetadata, _, err := imagemetadata.Fetch([]simplestreams.DataSource{datasource}, imageConstraint) if err != nil && !errors.IsNotFound(err) { return nil, errors.Annotate(err, "cannot read image metadata") } // Add an image metadata datasource for constraint validation, etc. environs.RegisterUserImageDataSourceFunc("bootstrap metadata", func(environs.Environ) (simplestreams.DataSource, error) { return datasource, nil }) logger.Infof("custom image metadata added to search path") return existingMetadata, nil }
// FindInstanceSpec returns an InstanceSpec satisfying the supplied instanceConstraint. func (env *joyentEnviron) FindInstanceSpec(ic *instances.InstanceConstraint) (*instances.InstanceSpec, error) { // Require at least one VCPU so we get KVM rather than smart package. if ic.Constraints.CpuCores == nil { ic.Constraints.CpuCores = &defaultCpuCores } allInstanceTypes, err := env.listInstanceTypes() if err != nil { return nil, err } imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{ic.Region, env.Ecfg().SdcUrl()}, Series: []string{ic.Series}, Arches: ic.Arches, }) sources, err := environs.ImageMetadataSources(env) if err != nil { return nil, err } matchingImages, _, err := imagemetadata.Fetch(sources, imageConstraint, signedImageDataOnly) if err != nil { return nil, err } images := instances.ImageMetadataToImages(matchingImages) spec, err := instances.FindInstanceSpec(images, ic, allInstanceTypes) if err != nil { return nil, err } return spec, nil }
func SetImageMetadata(env environs.Environ, series, arches []string, out *[]*imagemetadata.ImageMetadata) error { hasRegion, ok := env.(simplestreams.HasRegion) if !ok { return nil } sources, err := environs.ImageMetadataSources(env) if err != nil { return errors.Trace(err) } region, err := hasRegion.Region() if err != nil { return errors.Trace(err) } imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: region, Series: series, Arches: arches, Stream: env.Config().ImageStream(), }) imageMetadata, _, err := imagemetadata.Fetch(sources, imageConstraint) if err != nil { return errors.Trace(err) } *out = imageMetadata return 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, simplestreams.DEFAULT_CLOUD_DATA, s.RequireSigned) images, resolveInfo, err := imagemetadata.Fetch( []simplestreams.DataSource{invalidSource, s.Source}, imageConstraint) if !c.Check(err, jc.ErrorIsNil) { 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: "", }) } }
// setPrivateMetadataSources sets the default tools metadata source // for tools syncing, and adds an image metadata source after verifying // the contents. func setPrivateMetadataSources(env environs.Environ, metadataDir string) ([]*imagemetadata.ImageMetadata, error) { logger.Infof("Setting default tools and image metadata sources: %s", metadataDir) tools.DefaultBaseURL = metadataDir imageMetadataDir := filepath.Join(metadataDir, storage.BaseImagesPath) if _, err := os.Stat(imageMetadataDir); err != nil { if !os.IsNotExist(err) { return nil, errors.Annotate(err, "cannot access image metadata") } return nil, nil } baseURL := fmt.Sprintf("file://%s", filepath.ToSlash(imageMetadataDir)) datasource := simplestreams.NewURLDataSource("bootstrap metadata", baseURL, utils.NoVerifySSLHostnames) // Read the image metadata, as we'll want to upload it to the environment. imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{}) existingMetadata, _, err := imagemetadata.Fetch( []simplestreams.DataSource{datasource}, imageConstraint, false) if err != nil && !errors.IsNotFound(err) { return nil, errors.Annotate(err, "cannot read image metadata") } // Add an image metadata datasource for constraint validation, etc. // TODO (anastasiamac 2015-09-26) Delete when search path is modified to look // into state first. environs.RegisterUserImageDataSourceFunc("bootstrap metadata", func(environs.Environ) (simplestreams.DataSource, error) { return datasource, nil }) logger.Infof("custom image metadata added to search path") return existingMetadata, nil }
func (s *signedSuite) TestSignedImageMetadataInvalidSignature(c *gc.C) { signedSource := simplestreams.NewURLDataSource("test", "test://host/signed", utils.VerifySSLHostnames, simplestreams.DEFAULT_CLOUD_DATA, true) 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}, imageConstraint) c.Assert(err, gc.ErrorMatches, "cannot read index data.*") }
func assertFetch(c *gc.C, stor storage.Storage, series, arch, region, endpoint, id string) { cons := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{region, endpoint}, Series: []string{series}, Arches: []string{arch}, }) dataSource := storage.NewStorageSimpleStreamsDataSource("test datasource", stor, "images") metadata, _, err := imagemetadata.Fetch([]simplestreams.DataSource{dataSource}, cons, false) c.Assert(err, jc.ErrorIsNil) c.Assert(metadata, gc.HasLen, 1) c.Assert(metadata[0].Id, gc.Equals, id) }
func assertFetch(c *gc.C, stor storage.Storage, series, arch, region, endpoint string, ids ...string) { cons := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{region, endpoint}, Series: []string{series}, Arches: []string{arch}, }) dataSource := storage.NewStorageSimpleStreamsDataSource("test datasource", stor, "images", simplestreams.DEFAULT_CLOUD_DATA, false) metadata, _, err := imagemetadata.Fetch([]simplestreams.DataSource{dataSource}, cons) c.Assert(err, jc.ErrorIsNil) c.Assert(metadata, gc.HasLen, len(ids)) for i, id := range ids { c.Assert(metadata[i].Id, gc.Equals, id) } }
// SupportedArchitectures returns all the image architectures for env matching the constraints. func SupportedArchitectures(env environs.Environ, imageConstraint *imagemetadata.ImageConstraint) ([]string, error) { sources, err := environs.ImageMetadataSources(env) if err != nil { return nil, err } matchingImages, _, err := imagemetadata.Fetch(sources, imageConstraint, false) if err != nil { return nil, err } var arches = set.NewStrings() for _, im := range matchingImages { arches.Add(im.Arch) } return arches.Values(), nil }
func (api *API) retrievePublished() error { envCfg, err := api.metadata.ModelConfig() if err != nil { return errors.Annotatef(err, "getting environ config") } env, err := environs.New(envCfg) if err != nil { return errors.Annotatef(err, "getting environ") } sources, err := environs.ImageMetadataSources(env) if err != nil { return errors.Annotatef(err, "getting cloud specific image metadata sources") } cons := envmetadata.NewImageConstraint(simplestreams.LookupParams{}) if inst, ok := env.(simplestreams.HasRegion); !ok { // Published image metadata for some providers are in simple streams. // Providers that do not rely on simplestreams, don't need to do anything here. return nil } else { // If we can determine current region, // we want only metadata specific to this region. cloud, err := inst.Region() if err != nil { return errors.Annotatef(err, "getting cloud specific region information") } cons.CloudSpec = cloud } // We want all relevant metadata from all data sources. for _, source := range sources { logger.Debugf("looking in data source %v", source.Description()) metadata, info, err := envmetadata.Fetch([]simplestreams.DataSource{source}, cons) if err != nil { // Do not stop looking in other data sources if there is an issue here. logger.Errorf("encountered %v while getting published images metadata from %v", err, source.Description()) continue } err = api.saveAll(info, source.Priority(), metadata) if err != nil { // Do not stop looking in other data sources if there is an issue here. logger.Errorf("encountered %v while saving published images metadata from %v", err, source.Description()) } } return nil }
// saveCustomImageMetadata reads the custom image metadata from disk, // and saves it in state server. func (c *BootstrapCommand) saveCustomImageMetadata(st *state.State) error { logger.Debugf("saving custom image metadata from %q", c.ImageMetadataDir) baseURL := fmt.Sprintf("file://%s", filepath.ToSlash(c.ImageMetadataDir)) datasource := simplestreams.NewURLDataSource("bootstrap metadata", baseURL, utils.NoVerifySSLHostnames) // Read the image metadata, as we'll want to upload it to the environment. imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{}) existingMetadata, _, err := imagemetadata.Fetch( []simplestreams.DataSource{datasource}, imageConstraint, false) if err != nil && !errors.IsNotFound(err) { return errors.Annotate(err, "cannot read image metadata") } if len(existingMetadata) == 0 { return nil } msg := "" for _, one := range existingMetadata { m := cloudimagemetadata.Metadata{ cloudimagemetadata.MetadataAttributes{ Stream: one.Stream, Region: one.RegionName, Arch: one.Arch, VirtType: one.VirtType, RootStorageType: one.Storage, Source: "custom", }, one.Id, } s, err := seriesFromVersion(one.Version) if err != nil { return errors.Annotatef(err, "cannot determine series for version %v", one.Version) } m.Series = s err = st.CloudImageMetadataStorage.SaveMetadata(m) if err != nil { return errors.Annotatef(err, "cannot cache image metadata %v", m) } } if len(msg) > 0 { return errors.New(msg) } return nil }
func (s *signedSuite) TestSignedImageMetadata(c *gc.C) { signedSource := simplestreams.NewURLSignedDataSource("test", "test://host/signed", sstesting.SignedMetadataPublicKey, utils.VerifySSLHostnames, simplestreams.DEFAULT_CLOUD_DATA, true) 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}, imageConstraint) c.Assert(err, jc.ErrorIsNil) 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: "test://host/signed/streams/v1/index.sjson", MirrorURL: "", }) }
// findInstanceSpec returns an InstanceSpec satisfying the supplied instanceConstraint. func findInstanceSpec( sources []simplestreams.DataSource, stream string, ic *instances.InstanceConstraint) (*instances.InstanceSpec, error) { // If the instance type is set, don't also set a default CPU power // as this is implied. cons := ic.Constraints if cons.CpuPower == nil && (cons.InstanceType == nil || *cons.InstanceType == "") { ic.Constraints.CpuPower = instances.CpuPower(defaultCpuPower) } ec2Region := allRegions[ic.Region] imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{ic.Region, ec2Region.EC2Endpoint}, Series: []string{ic.Series}, Arches: ic.Arches, Stream: stream, }) matchingImages, _, err := imagemetadata.Fetch(sources, imageConstraint, signedImageDataOnly) if err != nil { return nil, err } if len(matchingImages) == 0 { logger.Warningf("no matching image meta data for constraints: %v", ic) } suitableImages := filterImages(matchingImages, ic) images := instances.ImageMetadataToImages(suitableImages) // Make a copy of the known EC2 instance types, filling in the cost for the specified region. regionCosts := allRegionCosts[ic.Region] if len(regionCosts) == 0 && len(allRegionCosts) > 0 { return nil, fmt.Errorf("no instance types found in %s", ic.Region) } var itypesWithCosts []instances.InstanceType for _, itype := range allInstanceTypes { cost, ok := regionCosts[itype.Name] if !ok { continue } itWithCost := itype itWithCost.Cost = cost itypesWithCosts = append(itypesWithCosts, itWithCost) } return instances.FindInstanceSpec(images, ic, itypesWithCosts) }
// findInstanceSpec returns an image and instance type satisfying the constraint. // The instance type comes from querying the flavors supported by the deployment. func findInstanceSpec(e *environ, ic *instances.InstanceConstraint) (*instances.InstanceSpec, error) { // first construct all available instance types from the supported flavors. nova := e.nova() flavors, err := nova.ListFlavorsDetail() if err != nil { return nil, err } allInstanceTypes := []instances.InstanceType{} for _, flavor := range flavors { instanceType := instances.InstanceType{ Id: flavor.Id, Name: flavor.Name, Arches: ic.Arches, Mem: uint64(flavor.RAM), CpuCores: uint64(flavor.VCPUs), RootDisk: uint64(flavor.Disk * 1024), // tags not currently supported on openstack } allInstanceTypes = append(allInstanceTypes, instanceType) } imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{ic.Region, e.ecfg().authURL()}, Series: []string{ic.Series}, Arches: ic.Arches, Stream: e.Config().ImageStream(), }) sources, err := imagemetadata.GetMetadataSources(e) if err != nil { return nil, err } // TODO (wallyworld): use an env parameter (default true) to mandate use of only signed image metadata. matchingImages, _, err := imagemetadata.Fetch(sources, simplestreams.DefaultIndexPath, imageConstraint, false) if err != nil { return nil, err } images := instances.ImageMetadataToImages(matchingImages) spec, err := instances.FindInstanceSpec(images, ic, allInstanceTypes) if err != nil { return nil, err } return spec, nil }
// storeImageMetadataFromFiles puts image metadata found in sources into state. func storeImageMetadataFromFiles(st *state.State, env environs.Environ, source simplestreams.DataSource) error { // Read the image metadata, as we'll want to upload it to the environment. imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{}) if inst, ok := env.(simplestreams.HasRegion); ok { // If we can determine current region, // we want only metadata specific to this region. cloud, err := inst.Region() if err != nil { return err } imageConstraint.CloudSpec = cloud } existingMetadata, info, err := imagemetadata.Fetch([]simplestreams.DataSource{source}, imageConstraint) if err != nil && !errors.IsNotFound(err) { return errors.Annotate(err, "cannot read image metadata") } return storeImageMetadataInState(st, info.Source, source.Priority(), existingMetadata) }
// findMatchingImages queries simplestreams for OS images that match the given // requirements. // // If it finds no matching images, that's an error. func findMatchingImages(e *azureEnviron, location, series string, arches []string) ([]*imagemetadata.ImageMetadata, error) { endpoint := getEndpoint(location) constraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{location, endpoint}, Series: []string{series}, Arches: arches, Stream: e.Config().ImageStream(), }) sources, err := environs.ImageMetadataSources(e) if err != nil { return nil, err } images, _, err := imagemetadata.Fetch(sources, constraint, signedImageDataOnly) if len(images) == 0 || errors.IsNotFound(err) { return nil, fmt.Errorf("no OS images found for location %q, series %q, architectures %q (and endpoint: %q)", location, series, arches, endpoint) } else if err != nil { return nil, err } return images, nil }
func (api *API) retrievePublished() ([]*envmetadata.ImageMetadata, error) { // Get environ envCfg, err := api.metadata.EnvironConfig() env, err := environs.New(envCfg) if err != nil { return nil, errors.Trace(err) } // Get all images metadata sources for this environ. sources, err := environs.ImageMetadataSources(env) if err != nil { return nil, err } // We want all metadata. cons := envmetadata.NewImageConstraint(simplestreams.LookupParams{}) metadata, _, err := envmetadata.Fetch(sources, cons, false) if err != nil { return nil, err } return metadata, nil }
// storeImageMetadataFromFiles puts image metadata found in sources into state. // Declared as var to facilitate tests. var storeImageMetadataFromFiles = func(st *state.State, env environs.Environ, source simplestreams.DataSource) error { // Read the image metadata, as we'll want to upload it to the environment. imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{}) if inst, ok := env.(simplestreams.HasRegion); ok { // If we can determine current region, // we want only metadata specific to this region. cloud, err := inst.Region() if err != nil { return err } imageConstraint.CloudSpec = cloud } existingMetadata, info, err := imagemetadata.Fetch([]simplestreams.DataSource{source}, imageConstraint) if err != nil && !errors.IsNotFound(err) { return errors.Annotate(err, "cannot read image metadata") } return storeImageMetadataInState(st, info.Source, source.Priority(), existingMetadata) } // storeImageMetadataInState writes image metadata into state store. func storeImageMetadataInState(st *state.State, source string, priority int, existingMetadata []*imagemetadata.ImageMetadata) error { if len(existingMetadata) == 0 { return nil } metadataState := make([]cloudimagemetadata.Metadata, len(existingMetadata)) for i, one := range existingMetadata { m := cloudimagemetadata.Metadata{ cloudimagemetadata.MetadataAttributes{
// imageMetadataFromDataSources finds image metadata that match specified criteria in existing data sources. func (p *ProvisionerAPI) imageMetadataFromDataSources(env environs.Environ, constraint *imagemetadata.ImageConstraint) ([]params.CloudImageMetadata, error) { sources, err := environs.ImageMetadataSources(env) if err != nil { return nil, errors.Trace(err) } cfg := env.Config() toModel := func(m *imagemetadata.ImageMetadata, mSeries string, source string, priority int) cloudimagemetadata.Metadata { result := cloudimagemetadata.Metadata{ MetadataAttributes: cloudimagemetadata.MetadataAttributes{ Region: m.RegionName, Arch: m.Arch, VirtType: m.VirtType, RootStorageType: m.Storage, Source: source, Series: mSeries, Stream: m.Stream, Version: m.Version, }, Priority: priority, ImageId: m.Id, } // TODO (anastasiamac 2016-08-24) This is a band-aid solution. // Once correct value is read from simplestreams, this needs to go. // Bug# 1616295 if result.Stream == "" { result.Stream = constraint.Stream } if result.Stream == "" { result.Stream = cfg.ImageStream() } return result } var metadataState []cloudimagemetadata.Metadata for _, source := range sources { logger.Debugf("looking in data source %v", source.Description()) found, info, err := imagemetadata.Fetch([]simplestreams.DataSource{source}, constraint) if err != nil { // Do not stop looking in other data sources if there is an issue here. logger.Warningf("encountered %v while getting published images metadata from %v", err, source.Description()) continue } for _, m := range found { mSeries, err := series.VersionSeries(m.Version) if err != nil { logger.Warningf("could not determine series for image id %s: %v", m.Id, err) continue } metadataState = append(metadataState, toModel(m, mSeries, info.Source, source.Priority())) } } if len(metadataState) > 0 { if err := p.st.CloudImageMetadataStorage.SaveMetadata(metadataState); err != nil { // No need to react here, just take note logger.Warningf("failed to save published image metadata: %v", err) } } // Since we've fallen through to data sources search and have saved all needed images into controller, // let's try to get them from controller to avoid duplication of conversion logic here. all, err := p.imageMetadataFromState(constraint) if err != nil { return nil, errors.Annotate(err, "could not read metadata from controller after saving it there from data sources") } if len(all) == 0 { return nil, errors.NotFoundf("image metadata for series %v, arch %v", constraint.Series, constraint.Arches) } return all, nil }
// bootstrapImageMetadata returns the image metadata to use for bootstrapping // the given environment. If the environment provider does not make use of // simplestreams, no metadata will be returned. // // If a bootstrap image ID is specified, image metadata will be synthesised // using that image ID, and the architecture and series specified by the // initiator. In addition, the custom image metadata that is saved into the // state database will have the synthesised image metadata added to it. func bootstrapImageMetadata( environ environs.Environ, availableTools coretools.List, bootstrapImageId string, customImageMetadata *[]*imagemetadata.ImageMetadata, ) ([]*imagemetadata.ImageMetadata, error) { hasRegion, ok := environ.(simplestreams.HasRegion) if !ok { if bootstrapImageId != "" { // We only support specifying image IDs for providers // that use simplestreams for now. return nil, errors.NotSupportedf( "specifying bootstrap image for %q provider", environ.Config().Type(), ) } // No region, no metadata. return nil, nil } region, err := hasRegion.Region() if err != nil { return nil, errors.Trace(err) } if bootstrapImageId != "" { arches := availableTools.Arches() if len(arches) != 1 { return nil, errors.NotValidf("multiple architectures with bootstrap image") } allSeries := availableTools.AllSeries() if len(allSeries) != 1 { return nil, errors.NotValidf("multiple series with bootstrap image") } seriesVersion, err := series.SeriesVersion(allSeries[0]) if err != nil { return nil, errors.Trace(err) } // The returned metadata does not have information about the // storage or virtualisation type. Any provider that wants to // filter on those properties should allow for empty values. meta := &imagemetadata.ImageMetadata{ Id: bootstrapImageId, Arch: arches[0], Version: seriesVersion, RegionName: region.Region, Endpoint: region.Endpoint, Stream: environ.Config().ImageStream(), } *customImageMetadata = append(*customImageMetadata, meta) return []*imagemetadata.ImageMetadata{meta}, nil } // For providers that support making use of simplestreams // image metadata, search public image metadata. We need // to pass this onto Bootstrap for selecting images. sources, err := environs.ImageMetadataSources(environ) if err != nil { return nil, errors.Trace(err) } imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: region, Series: availableTools.AllSeries(), Arches: availableTools.Arches(), Stream: environ.Config().ImageStream(), }) logger.Debugf("constraints for image metadata lookup %v", imageConstraint) // Get image metadata from all data sources. // Since order of data source matters, order of image metadata matters too. Append is important here. var publicImageMetadata []*imagemetadata.ImageMetadata for _, source := range sources { sourceMetadata, _, err := imagemetadata.Fetch([]simplestreams.DataSource{source}, imageConstraint) if err != nil { logger.Debugf("ignoring image metadata in %s: %v", source.Description(), err) // Just keep looking... continue } logger.Debugf("found %d image metadata in %s", len(sourceMetadata), source.Description()) publicImageMetadata = append(publicImageMetadata, sourceMetadata...) } logger.Debugf("found %d image metadata from all image data sources", len(publicImageMetadata)) if len(publicImageMetadata) == 0 { return nil, errors.New("no image metadata found") } return publicImageMetadata, nil }
"github.com/juju/juju/instance" "github.com/juju/juju/tools" ) // // Imlementation of InstanceBroker: methods for starting and stopping instances. // var findInstanceImage = func(env *environ, ic *imagemetadata.ImageConstraint) (*imagemetadata.ImageMetadata, error) { sources, err := environs.ImageMetadataSources(env) if err != nil { return nil, err } matchingImages, _, err := imagemetadata.Fetch(sources, ic, false) if err != nil { return nil, err } if len(matchingImages) == 0 { return nil, errors.New("no matching image meta data") } return matchingImages[0], nil } // MaintainInstance is specified in the InstanceBroker interface. func (*environ) MaintainInstance(args environs.StartInstanceParams) error { return nil }
// bootstrapImageMetadata returns the image metadata to use for bootstrapping // the given environment. If the environment provider does not make use of // simplestreams, no metadata will be returned. // // If a bootstrap image ID is specified, image metadat will be synthesised // using that image ID, and the architecture and series specified by the // initiator. In addition, the custom image metadat that is saved into the // state database will have the synthesised image metadata added to it. func bootstrapImageMetadata( environ environs.Environ, availableTools coretools.List, bootstrapImageId string, customImageMetadata *[]*imagemetadata.ImageMetadata, ) ([]*imagemetadata.ImageMetadata, error) { hasRegion, ok := environ.(simplestreams.HasRegion) if !ok { if bootstrapImageId != "" { // We only support specifying image IDs for providers // that use simplestreams for now. return nil, errors.NotSupportedf( "specifying bootstrap image for %q provider", environ.Config().Type(), ) } // No region, no metadata. return nil, nil } region, err := hasRegion.Region() if err != nil { return nil, errors.Trace(err) } if bootstrapImageId != "" { arches := availableTools.Arches() if len(arches) != 1 { return nil, errors.NotValidf("multiple architectures with bootstrap image") } allSeries := availableTools.AllSeries() if len(allSeries) != 1 { return nil, errors.NotValidf("multiple series with bootstrap image") } seriesVersion, err := series.SeriesVersion(allSeries[0]) if err != nil { return nil, errors.Trace(err) } // The returned metadata does not have information about the // storage or virtualisation type. Any provider that wants to // filter on those properties should allow for empty values. meta := &imagemetadata.ImageMetadata{ Id: bootstrapImageId, Arch: arches[0], Version: seriesVersion, RegionName: region.Region, Endpoint: region.Endpoint, Stream: environ.Config().ImageStream(), } *customImageMetadata = append(*customImageMetadata, meta) return []*imagemetadata.ImageMetadata{meta}, nil } // For providers that support making use of simplestreams // image metadata, search public image metadata. We need // to pass this onto Bootstrap for selecting images. sources, err := environs.ImageMetadataSources(environ) if err != nil { return nil, errors.Trace(err) } imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: region, Series: availableTools.AllSeries(), Arches: availableTools.Arches(), Stream: environ.Config().ImageStream(), }) publicImageMetadata, _, err := imagemetadata.Fetch(sources, imageConstraint) if err != nil { return nil, errors.Annotate(err, "searching image metadata") } return publicImageMetadata, nil }