github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/charms/client.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package charms 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/utils/set" 9 "gopkg.in/juju/charm.v6-unstable" 10 "gopkg.in/juju/charm.v6-unstable/resource" 11 12 "github.com/juju/juju/apiserver/common" 13 "github.com/juju/juju/apiserver/facade" 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/permission" 16 "github.com/juju/juju/state" 17 ) 18 19 func init() { 20 common.RegisterStandardFacade("Charms", 2, NewAPI) 21 } 22 23 var getState = func(st *state.State) charmsAccess { 24 return stateShim{st} 25 } 26 27 // Charms defines the methods on the charms API end point. 28 type Charms interface { 29 List(args params.CharmsList) (params.CharmsListResult, error) 30 CharmInfo(args params.CharmURL) (params.CharmInfo, error) 31 IsMetered(args params.CharmURL) (bool, error) 32 } 33 34 // API implements the charms interface and is the concrete 35 // implementation of the api end point. 36 type API struct { 37 access charmsAccess 38 authorizer facade.Authorizer 39 } 40 41 func (a *API) checkCanRead() error { 42 canRead, err := a.authorizer.HasPermission(permission.ReadAccess, a.access.ModelTag()) 43 if err != nil { 44 return errors.Trace(err) 45 } 46 if !canRead { 47 return common.ErrPerm 48 } 49 return nil 50 } 51 52 // NewAPI returns a new charms API facade. 53 func NewAPI( 54 st *state.State, 55 resources facade.Resources, 56 authorizer facade.Authorizer, 57 ) (*API, error) { 58 59 if !authorizer.AuthClient() { 60 return nil, common.ErrPerm 61 } 62 63 return &API{ 64 access: getState(st), 65 authorizer: authorizer, 66 }, nil 67 } 68 69 // CharmInfo returns information about the requested charm. 70 // NOTE: thumper 2016-06-29, this is not a bulk call and probably should be. 71 func (a *API) CharmInfo(args params.CharmURL) (params.CharmInfo, error) { 72 if err := a.checkCanRead(); err != nil { 73 return params.CharmInfo{}, errors.Trace(err) 74 } 75 76 curl, err := charm.ParseURL(args.URL) 77 if err != nil { 78 return params.CharmInfo{}, errors.Trace(err) 79 } 80 aCharm, err := a.access.Charm(curl) 81 if err != nil { 82 return params.CharmInfo{}, errors.Trace(err) 83 } 84 info := params.CharmInfo{ 85 Revision: aCharm.Revision(), 86 URL: curl.String(), 87 Config: convertCharmConfig(aCharm.Config()), 88 Meta: convertCharmMeta(aCharm.Meta()), 89 Actions: convertCharmActions(aCharm.Actions()), 90 Metrics: convertCharmMetrics(aCharm.Metrics()), 91 } 92 return info, nil 93 } 94 95 // List returns a list of charm URLs currently in the state. 96 // If supplied parameter contains any names, the result will be filtered 97 // to return only the charms with supplied names. 98 func (a *API) List(args params.CharmsList) (params.CharmsListResult, error) { 99 if err := a.checkCanRead(); err != nil { 100 return params.CharmsListResult{}, errors.Trace(err) 101 } 102 103 charms, err := a.access.AllCharms() 104 if err != nil { 105 return params.CharmsListResult{}, errors.Annotatef(err, " listing charms ") 106 } 107 108 names := set.NewStrings(args.Names...) 109 checkName := !names.IsEmpty() 110 charmURLs := []string{} 111 for _, aCharm := range charms { 112 charmURL := aCharm.URL() 113 if checkName { 114 if !names.Contains(charmURL.Name) { 115 continue 116 } 117 } 118 charmURLs = append(charmURLs, charmURL.String()) 119 } 120 return params.CharmsListResult{CharmURLs: charmURLs}, nil 121 } 122 123 // IsMetered returns whether or not the charm is metered. 124 func (a *API) IsMetered(args params.CharmURL) (params.IsMeteredResult, error) { 125 if err := a.checkCanRead(); err != nil { 126 return params.IsMeteredResult{}, errors.Trace(err) 127 } 128 129 curl, err := charm.ParseURL(args.URL) 130 if err != nil { 131 return params.IsMeteredResult{false}, errors.Trace(err) 132 } 133 aCharm, err := a.access.Charm(curl) 134 if err != nil { 135 return params.IsMeteredResult{false}, errors.Trace(err) 136 } 137 if aCharm.Metrics() != nil && len(aCharm.Metrics().Metrics) > 0 { 138 return params.IsMeteredResult{true}, nil 139 } 140 return params.IsMeteredResult{false}, nil 141 } 142 143 func convertCharmConfig(config *charm.Config) map[string]params.CharmOption { 144 if config == nil { 145 return nil 146 } 147 result := make(map[string]params.CharmOption) 148 for key, value := range config.Options { 149 result[key] = convertCharmOption(value) 150 } 151 return result 152 } 153 154 func convertCharmOption(opt charm.Option) params.CharmOption { 155 return params.CharmOption{ 156 Type: opt.Type, 157 Description: opt.Description, 158 Default: opt.Default, 159 } 160 } 161 162 func convertCharmMeta(meta *charm.Meta) *params.CharmMeta { 163 if meta == nil { 164 return nil 165 } 166 result := ¶ms.CharmMeta{ 167 Name: meta.Name, 168 Summary: meta.Summary, 169 Description: meta.Description, 170 Subordinate: meta.Subordinate, 171 Provides: convertCharmRelationMap(meta.Provides), 172 Requires: convertCharmRelationMap(meta.Requires), 173 Peers: convertCharmRelationMap(meta.Peers), 174 ExtraBindings: convertCharmExtraBindingMap(meta.ExtraBindings), 175 Categories: meta.Categories, 176 Tags: meta.Tags, 177 Series: meta.Series, 178 Storage: convertCharmStorageMap(meta.Storage), 179 PayloadClasses: convertCharmPayloadClassMap(meta.PayloadClasses), 180 Resources: convertCharmResourceMetaMap(meta.Resources), 181 Terms: meta.Terms, 182 MinJujuVersion: meta.MinJujuVersion.String(), 183 } 184 185 return result 186 } 187 188 func convertCharmRelationMap(relations map[string]charm.Relation) map[string]params.CharmRelation { 189 if len(relations) == 0 { 190 return nil 191 } 192 result := make(map[string]params.CharmRelation) 193 for key, value := range relations { 194 result[key] = convertCharmRelation(value) 195 } 196 return result 197 } 198 199 func convertCharmRelation(relation charm.Relation) params.CharmRelation { 200 return params.CharmRelation{ 201 Name: relation.Name, 202 Role: string(relation.Role), 203 Interface: relation.Interface, 204 Optional: relation.Optional, 205 Limit: relation.Limit, 206 Scope: string(relation.Scope), 207 } 208 } 209 210 func convertCharmStorageMap(storage map[string]charm.Storage) map[string]params.CharmStorage { 211 if len(storage) == 0 { 212 return nil 213 } 214 result := make(map[string]params.CharmStorage) 215 for key, value := range storage { 216 result[key] = convertCharmStorage(value) 217 } 218 return result 219 } 220 221 func convertCharmStorage(storage charm.Storage) params.CharmStorage { 222 return params.CharmStorage{ 223 Name: storage.Name, 224 Description: storage.Description, 225 Type: string(storage.Type), 226 Shared: storage.Shared, 227 ReadOnly: storage.ReadOnly, 228 CountMin: storage.CountMin, 229 CountMax: storage.CountMax, 230 MinimumSize: storage.MinimumSize, 231 Location: storage.Location, 232 Properties: storage.Properties, 233 } 234 } 235 236 func convertCharmPayloadClassMap(payload map[string]charm.PayloadClass) map[string]params.CharmPayloadClass { 237 if len(payload) == 0 { 238 return nil 239 } 240 result := make(map[string]params.CharmPayloadClass) 241 for key, value := range payload { 242 result[key] = convertCharmPayloadClass(value) 243 } 244 return result 245 } 246 247 func convertCharmPayloadClass(payload charm.PayloadClass) params.CharmPayloadClass { 248 return params.CharmPayloadClass{ 249 Name: payload.Name, 250 Type: payload.Type, 251 } 252 } 253 254 func convertCharmResourceMetaMap(resources map[string]resource.Meta) map[string]params.CharmResourceMeta { 255 if len(resources) == 0 { 256 return nil 257 } 258 result := make(map[string]params.CharmResourceMeta) 259 for key, value := range resources { 260 result[key] = convertCharmResourceMeta(value) 261 } 262 return result 263 } 264 265 func convertCharmResourceMeta(meta resource.Meta) params.CharmResourceMeta { 266 return params.CharmResourceMeta{ 267 Name: meta.Name, 268 Type: meta.Type.String(), 269 Path: meta.Path, 270 Description: meta.Description, 271 } 272 } 273 274 func convertCharmActions(actions *charm.Actions) *params.CharmActions { 275 if actions == nil { 276 return nil 277 } 278 result := ¶ms.CharmActions{ 279 ActionSpecs: convertCharmActionSpecMap(actions.ActionSpecs), 280 } 281 282 return result 283 } 284 285 func convertCharmActionSpecMap(specs map[string]charm.ActionSpec) map[string]params.CharmActionSpec { 286 if len(specs) == 0 { 287 return nil 288 } 289 result := make(map[string]params.CharmActionSpec) 290 for key, value := range specs { 291 result[key] = convertCharmActionSpec(value) 292 } 293 return result 294 } 295 296 func convertCharmActionSpec(spec charm.ActionSpec) params.CharmActionSpec { 297 return params.CharmActionSpec{ 298 Description: spec.Description, 299 Params: spec.Params, 300 } 301 } 302 303 func convertCharmMetrics(metrics *charm.Metrics) *params.CharmMetrics { 304 if metrics == nil { 305 return nil 306 } 307 return ¶ms.CharmMetrics{ 308 Metrics: convertCharmMetricMap(metrics.Metrics), 309 Plan: convertCharmPlan(metrics.Plan), 310 } 311 } 312 313 func convertCharmPlan(plan *charm.Plan) params.CharmPlan { 314 if plan == nil { 315 return params.CharmPlan{Required: false} 316 } 317 return params.CharmPlan{Required: plan.Required} 318 } 319 320 func convertCharmMetricMap(metrics map[string]charm.Metric) map[string]params.CharmMetric { 321 if len(metrics) == 0 { 322 return nil 323 } 324 result := make(map[string]params.CharmMetric) 325 for key, value := range metrics { 326 result[key] = convertCharmMetric(value) 327 } 328 return result 329 } 330 331 func convertCharmMetric(metric charm.Metric) params.CharmMetric { 332 return params.CharmMetric{ 333 Type: string(metric.Type), 334 Description: metric.Description, 335 } 336 } 337 338 func convertCharmExtraBindingMap(bindings map[string]charm.ExtraBinding) map[string]string { 339 if len(bindings) == 0 { 340 return nil 341 } 342 result := make(map[string]string) 343 for key, value := range bindings { 344 result[key] = value.Name 345 } 346 return result 347 }