github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/apiserver/imagemetadata/metadata.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package imagemetadata 5 6 import ( 7 "fmt" 8 "strings" 9 10 "github.com/juju/loggo" 11 "github.com/juju/utils/series" 12 13 "github.com/juju/errors" 14 "github.com/juju/juju/apiserver/common" 15 "github.com/juju/juju/apiserver/params" 16 "github.com/juju/juju/environs" 17 envmetadata "github.com/juju/juju/environs/imagemetadata" 18 "github.com/juju/juju/environs/simplestreams" 19 "github.com/juju/juju/state" 20 "github.com/juju/juju/state/cloudimagemetadata" 21 ) 22 23 var logger = loggo.GetLogger("juju.apiserver.imagemetadata") 24 25 func init() { 26 common.RegisterStandardFacade("ImageMetadata", 1, NewAPI) 27 } 28 29 // API is the concrete implementation of the api end point 30 // for loud image metadata manipulations. 31 type API struct { 32 metadata metadataAcess 33 authorizer common.Authorizer 34 } 35 36 // createAPI returns a new image metadata API facade. 37 func createAPI( 38 st metadataAcess, 39 resources *common.Resources, 40 authorizer common.Authorizer, 41 ) (*API, error) { 42 if !authorizer.AuthClient() && !authorizer.AuthEnvironManager() { 43 return nil, common.ErrPerm 44 } 45 46 return &API{ 47 metadata: st, 48 authorizer: authorizer, 49 }, nil 50 } 51 52 // NewAPI returns a new cloud image metadata API facade. 53 func NewAPI( 54 st *state.State, 55 resources *common.Resources, 56 authorizer common.Authorizer, 57 ) (*API, error) { 58 return createAPI(getState(st), resources, authorizer) 59 } 60 61 // List returns all found cloud image metadata that satisfy 62 // given filter. 63 // Returned list contains metadata for custom images first, then public. 64 func (api *API) List(filter params.ImageMetadataFilter) (params.ListCloudImageMetadataResult, error) { 65 found, err := api.metadata.FindMetadata(cloudimagemetadata.MetadataFilter{ 66 Region: filter.Region, 67 Series: filter.Series, 68 Arches: filter.Arches, 69 Stream: filter.Stream, 70 VirtType: filter.VirtType, 71 RootStorageType: filter.RootStorageType, 72 }) 73 if err != nil { 74 return params.ListCloudImageMetadataResult{}, common.ServerError(err) 75 } 76 77 var all []params.CloudImageMetadata 78 addAll := func(ms []cloudimagemetadata.Metadata) { 79 for _, m := range ms { 80 all = append(all, parseMetadataToParams(m)) 81 } 82 } 83 84 // First return metadata for custom images, then public. 85 // No other source for cloud images should exist at the moment. 86 // Once new source is identified, the order of returned metadata 87 // may need to be changed. 88 addAll(found[cloudimagemetadata.Custom]) 89 addAll(found[cloudimagemetadata.Public]) 90 91 return params.ListCloudImageMetadataResult{Result: all}, nil 92 } 93 94 // Save stores given cloud image metadata. 95 // It supports bulk calls. 96 func (api *API) Save(metadata params.MetadataSaveParams) (params.ErrorResults, error) { 97 all := make([]params.ErrorResult, len(metadata.Metadata)) 98 for i, one := range metadata.Metadata { 99 err := api.metadata.SaveMetadata(parseMetadataFromParams(one)) 100 all[i] = params.ErrorResult{Error: common.ServerError(err)} 101 } 102 return params.ErrorResults{Results: all}, nil 103 } 104 105 func parseMetadataToParams(p cloudimagemetadata.Metadata) params.CloudImageMetadata { 106 result := params.CloudImageMetadata{ 107 ImageId: p.ImageId, 108 Stream: p.Stream, 109 Region: p.Region, 110 Series: p.Series, 111 Arch: p.Arch, 112 VirtType: p.VirtType, 113 RootStorageType: p.RootStorageType, 114 RootStorageSize: p.RootStorageSize, 115 Source: string(p.Source), 116 } 117 return result 118 } 119 120 func parseMetadataFromParams(p params.CloudImageMetadata) cloudimagemetadata.Metadata { 121 122 parseSource := func(s string) cloudimagemetadata.SourceType { 123 switch cloudimagemetadata.SourceType(strings.ToLower(s)) { 124 case cloudimagemetadata.Public: 125 return cloudimagemetadata.Public 126 case cloudimagemetadata.Custom: 127 return cloudimagemetadata.Custom 128 default: 129 panic(fmt.Sprintf("unknown cloud image metadata source %q", s)) 130 } 131 } 132 133 result := cloudimagemetadata.Metadata{ 134 cloudimagemetadata.MetadataAttributes{ 135 Stream: p.Stream, 136 Region: p.Region, 137 Series: p.Series, 138 Arch: p.Arch, 139 VirtType: p.VirtType, 140 RootStorageType: p.RootStorageType, 141 RootStorageSize: p.RootStorageSize, 142 Source: parseSource(p.Source), 143 }, 144 p.ImageId, 145 } 146 if p.Stream == "" { 147 result.Stream = "released" 148 } 149 return result 150 } 151 152 // UpdateFromPublishedImages retrieves currently published image metadata and 153 // updates stored ones accordingly. 154 func (api *API) UpdateFromPublishedImages() error { 155 published, err := api.retrievePublished() 156 if err != nil { 157 return errors.Annotatef(err, "getting published images metadata") 158 } 159 err = api.saveAll(published) 160 return errors.Annotatef(err, "saving published images metadata") 161 } 162 163 func (api *API) retrievePublished() ([]*envmetadata.ImageMetadata, error) { 164 // Get environ 165 envCfg, err := api.metadata.EnvironConfig() 166 env, err := environs.New(envCfg) 167 if err != nil { 168 return nil, errors.Trace(err) 169 } 170 171 // Get all images metadata sources for this environ. 172 sources, err := environs.ImageMetadataSources(env) 173 if err != nil { 174 return nil, err 175 } 176 177 // We want all metadata. 178 cons := envmetadata.NewImageConstraint(simplestreams.LookupParams{}) 179 metadata, _, err := envmetadata.Fetch(sources, cons, false) 180 if err != nil { 181 return nil, err 182 } 183 return metadata, nil 184 } 185 186 func (api *API) saveAll(published []*envmetadata.ImageMetadata) error { 187 // Store converted metadata. 188 // Note that whether the metadata actually needs 189 // to be stored will be determined within this call. 190 errs, err := api.Save(convertToParams(published)) 191 if err != nil { 192 return errors.Annotatef(err, "saving published images metadata") 193 } 194 return processErrors(errs.Results) 195 } 196 197 // convertToParams converts environment-specific images metadata to structured metadata format. 198 var convertToParams = func(published []*envmetadata.ImageMetadata) params.MetadataSaveParams { 199 metadata := make([]params.CloudImageMetadata, len(published)) 200 for i, p := range published { 201 metadata[i] = params.CloudImageMetadata{ 202 Source: "public", 203 ImageId: p.Id, 204 Stream: p.Stream, 205 Region: p.RegionName, 206 Arch: p.Arch, 207 VirtType: p.VirtType, 208 RootStorageType: p.Storage, 209 } 210 // Translate version (eg.14.04) to a series (eg. "trusty") 211 s, err := series.VersionSeries(p.Version) 212 if err != nil { 213 logger.Warningf("could not determine series for image id %s: %v", p.Id, err) 214 continue 215 } 216 metadata[i].Series = s 217 } 218 219 return params.MetadataSaveParams{Metadata: metadata} 220 } 221 222 func processErrors(errs []params.ErrorResult) error { 223 msgs := []string{} 224 for _, e := range errs { 225 if e.Error != nil && e.Error.Message != "" { 226 msgs = append(msgs, e.Error.Message) 227 } 228 } 229 if len(msgs) != 0 { 230 return errors.Errorf("saving some image metadata:\n%v", strings.Join(msgs, "\n")) 231 } 232 return nil 233 }