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