github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/api/common/charms/common.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 "fmt" 8 9 "github.com/juju/charm/v12" 10 "github.com/juju/charm/v12/resource" 11 "github.com/juju/errors" 12 "github.com/juju/names/v5" 13 "github.com/juju/version/v2" 14 15 "github.com/juju/juju/api/base" 16 "github.com/juju/juju/rpc/params" 17 ) 18 19 // CharmInfoClient allows access to the charms API endpoint. 20 type CharmInfoClient struct { 21 facade base.FacadeCaller 22 } 23 24 // NewCharmInfoClient creates a new client for accessing the charms API. 25 func NewCharmInfoClient(facade base.FacadeCaller) *CharmInfoClient { 26 return &CharmInfoClient{facade: facade} 27 } 28 29 // CharmInfo returns information about the requested charm. 30 func (c *CharmInfoClient) CharmInfo(charmURL string) (*CharmInfo, error) { 31 args := params.CharmURL{URL: charmURL} 32 var info params.Charm 33 if err := c.facade.FacadeCall("CharmInfo", args, &info); err != nil { 34 return nil, errors.Trace(err) 35 } 36 return convertCharm(&info) 37 } 38 39 // ApplicationCharmInfoClient allows access to the ApplicationCharmInfo endpoint. 40 type ApplicationCharmInfoClient struct { 41 facade base.FacadeCaller 42 } 43 44 // NewApplicationCharmInfoClient creates a new client for accessing the 45 // ApplicationCharmInfo API. 46 func NewApplicationCharmInfoClient(facade base.FacadeCaller) *ApplicationCharmInfoClient { 47 return &ApplicationCharmInfoClient{facade: facade} 48 } 49 50 // ApplicationCharmInfo returns information about an application's charm. 51 func (c *ApplicationCharmInfoClient) ApplicationCharmInfo(appName string) (*CharmInfo, error) { 52 args := params.Entity{Tag: names.NewApplicationTag(appName).String()} 53 var info params.Charm 54 if err := c.facade.FacadeCall("ApplicationCharmInfo", args, &info); err != nil { 55 return nil, errors.Trace(err) 56 } 57 return convertCharm(&info) 58 } 59 60 // CharmInfo holds information about a charm. 61 type CharmInfo struct { 62 Revision int 63 URL string 64 Config *charm.Config 65 Meta *charm.Meta 66 Actions *charm.Actions 67 Metrics *charm.Metrics 68 Manifest *charm.Manifest 69 LXDProfile *charm.LXDProfile 70 } 71 72 func (info *CharmInfo) Charm() charm.Charm { 73 return &charmImpl{info} 74 } 75 76 func convertCharm(info *params.Charm) (*CharmInfo, error) { 77 meta, err := convertCharmMeta(info.Meta) 78 if err != nil { 79 return nil, errors.Trace(err) 80 } 81 manifest, err := convertCharmManifest(info.Manifest) 82 if err != nil { 83 return nil, errors.Trace(err) 84 } 85 result := &CharmInfo{ 86 Revision: info.Revision, 87 URL: info.URL, 88 Config: params.FromCharmOptionMap(info.Config), 89 Meta: meta, 90 Actions: convertCharmActions(info.Actions), 91 Metrics: convertCharmMetrics(info.Metrics), 92 Manifest: manifest, 93 LXDProfile: convertCharmLXDProfile(info.LXDProfile), 94 } 95 return result, nil 96 } 97 98 func convertCharmMeta(meta *params.CharmMeta) (*charm.Meta, error) { 99 if meta == nil { 100 return nil, nil 101 } 102 minVersion, err := version.Parse(meta.MinJujuVersion) 103 if err != nil { 104 return nil, errors.Trace(err) 105 } 106 resources, err := convertCharmResourceMetaMap(meta.Resources) 107 if err != nil { 108 return nil, errors.Trace(err) 109 } 110 containers, err := convertCharmContainers(meta.Containers) 111 if err != nil { 112 return nil, errors.Trace(err) 113 } 114 result := &charm.Meta{ 115 Name: meta.Name, 116 Summary: meta.Summary, 117 Description: meta.Description, 118 Subordinate: meta.Subordinate, 119 Provides: convertCharmRelationMap(meta.Provides), 120 Requires: convertCharmRelationMap(meta.Requires), 121 Peers: convertCharmRelationMap(meta.Peers), 122 ExtraBindings: convertCharmExtraBindingMap(meta.ExtraBindings), 123 Categories: meta.Categories, 124 Tags: meta.Tags, 125 Series: meta.Series, 126 Storage: convertCharmStorageMap(meta.Storage), 127 Deployment: convertCharmDeployment(meta.Deployment), 128 Devices: convertCharmDevices(meta.Devices), 129 PayloadClasses: convertCharmPayloadClassMap(meta.PayloadClasses), 130 Resources: resources, 131 Terms: meta.Terms, 132 MinJujuVersion: minVersion, 133 Containers: containers, 134 Assumes: meta.AssumesExpr, 135 CharmUser: charm.RunAs(meta.CharmUser), 136 } 137 return result, nil 138 } 139 140 func convertCharmRelationMap(relations map[string]params.CharmRelation) map[string]charm.Relation { 141 if len(relations) == 0 { 142 return nil 143 } 144 result := make(map[string]charm.Relation) 145 for key, value := range relations { 146 result[key] = convertCharmRelation(value) 147 } 148 return result 149 } 150 151 func convertCharmRelation(relation params.CharmRelation) charm.Relation { 152 return charm.Relation{ 153 Name: relation.Name, 154 Role: charm.RelationRole(relation.Role), 155 Interface: relation.Interface, 156 Optional: relation.Optional, 157 Limit: relation.Limit, 158 Scope: charm.RelationScope(relation.Scope), 159 } 160 } 161 162 func convertCharmStorageMap(storage map[string]params.CharmStorage) map[string]charm.Storage { 163 if len(storage) == 0 { 164 return nil 165 } 166 result := make(map[string]charm.Storage) 167 for key, value := range storage { 168 result[key] = convertCharmStorage(value) 169 } 170 return result 171 } 172 173 func convertCharmStorage(storage params.CharmStorage) charm.Storage { 174 return charm.Storage{ 175 Name: storage.Name, 176 Description: storage.Description, 177 Type: charm.StorageType(storage.Type), 178 Shared: storage.Shared, 179 ReadOnly: storage.ReadOnly, 180 CountMin: storage.CountMin, 181 CountMax: storage.CountMax, 182 MinimumSize: storage.MinimumSize, 183 Location: storage.Location, 184 Properties: storage.Properties, 185 } 186 } 187 188 func convertCharmPayloadClassMap(payload map[string]params.CharmPayloadClass) map[string]charm.PayloadClass { 189 if len(payload) == 0 { 190 return nil 191 } 192 result := make(map[string]charm.PayloadClass) 193 for key, value := range payload { 194 result[key] = convertCharmPayloadClass(value) 195 } 196 return result 197 } 198 199 func convertCharmPayloadClass(payload params.CharmPayloadClass) charm.PayloadClass { 200 return charm.PayloadClass{ 201 Name: payload.Name, 202 Type: payload.Type, 203 } 204 } 205 206 func convertCharmResourceMetaMap(resources map[string]params.CharmResourceMeta) (map[string]resource.Meta, error) { 207 if len(resources) == 0 { 208 return nil, nil 209 } 210 result := make(map[string]resource.Meta) 211 for key, value := range resources { 212 converted, err := convertCharmResourceMeta(value) 213 if err != nil { 214 return nil, errors.Trace(err) 215 } 216 result[key] = converted 217 } 218 return result, nil 219 } 220 221 func convertCharmResourceMeta(meta params.CharmResourceMeta) (resource.Meta, error) { 222 resourceType, err := resource.ParseType(meta.Type) 223 if err != nil { 224 return resource.Meta{}, errors.Trace(err) 225 } 226 return resource.Meta{ 227 Name: meta.Name, 228 Type: resourceType, 229 Path: meta.Path, 230 Description: meta.Description, 231 }, nil 232 } 233 234 func convertCharmActions(actions *params.CharmActions) *charm.Actions { 235 if actions == nil { 236 return nil 237 } 238 return &charm.Actions{ 239 ActionSpecs: convertCharmActionSpecMap(actions.ActionSpecs), 240 } 241 } 242 243 func convertCharmActionSpecMap(specs map[string]params.CharmActionSpec) map[string]charm.ActionSpec { 244 if len(specs) == 0 { 245 return nil 246 } 247 result := make(map[string]charm.ActionSpec) 248 for key, value := range specs { 249 result[key] = convertCharmActionSpec(value) 250 } 251 return result 252 } 253 254 func convertCharmActionSpec(spec params.CharmActionSpec) charm.ActionSpec { 255 return charm.ActionSpec{ 256 Description: spec.Description, 257 Params: spec.Params, 258 } 259 } 260 261 func convertCharmMetrics(metrics *params.CharmMetrics) *charm.Metrics { 262 if metrics == nil { 263 return nil 264 } 265 return &charm.Metrics{ 266 Metrics: convertCharmMetricMap(metrics.Metrics), 267 Plan: convertCharmPlan(metrics.Plan), 268 } 269 } 270 271 func convertCharmPlan(plan params.CharmPlan) *charm.Plan { 272 return &charm.Plan{Required: plan.Required} 273 } 274 275 func convertCharmMetricMap(metrics map[string]params.CharmMetric) map[string]charm.Metric { 276 if len(metrics) == 0 { 277 return nil 278 } 279 result := make(map[string]charm.Metric) 280 for key, value := range metrics { 281 result[key] = convertCharmMetric(value) 282 } 283 return result 284 } 285 286 func convertCharmMetric(metric params.CharmMetric) charm.Metric { 287 return charm.Metric{ 288 Type: charm.MetricType(metric.Type), 289 Description: metric.Description, 290 } 291 } 292 293 func convertCharmExtraBindingMap(bindings map[string]string) map[string]charm.ExtraBinding { 294 if len(bindings) == 0 { 295 return nil 296 } 297 result := make(map[string]charm.ExtraBinding) 298 for key, value := range bindings { 299 result[key] = charm.ExtraBinding{value} 300 } 301 return result 302 } 303 304 func convertCharmLXDProfile(lxdProfile *params.CharmLXDProfile) *charm.LXDProfile { 305 if lxdProfile == nil { 306 return nil 307 } 308 return &charm.LXDProfile{ 309 Description: lxdProfile.Description, 310 Config: convertCharmLXDProfileConfigMap(lxdProfile.Config), 311 Devices: convertCharmLXDProfileDevicesMap(lxdProfile.Devices), 312 } 313 } 314 315 func convertCharmLXDProfileConfigMap(config map[string]string) map[string]string { 316 result := make(map[string]string, len(config)) 317 for k, v := range config { 318 result[k] = v 319 } 320 return result 321 } 322 323 func convertCharmLXDProfileDevicesMap(devices map[string]map[string]string) map[string]map[string]string { 324 result := make(map[string]map[string]string, len(devices)) 325 for k, v := range devices { 326 nested := make(map[string]string, len(v)) 327 for nk, nv := range v { 328 nested[nk] = nv 329 } 330 result[k] = nested 331 } 332 return result 333 } 334 335 func convertCharmDeployment(deployment *params.CharmDeployment) *charm.Deployment { 336 if deployment == nil { 337 return nil 338 } 339 return &charm.Deployment{ 340 DeploymentType: charm.DeploymentType(deployment.DeploymentType), 341 DeploymentMode: charm.DeploymentMode(deployment.DeploymentMode), 342 ServiceType: charm.ServiceType(deployment.ServiceType), 343 MinVersion: deployment.MinVersion, 344 } 345 } 346 347 func convertCharmDevices(devices map[string]params.CharmDevice) map[string]charm.Device { 348 if devices == nil { 349 return nil 350 } 351 results := make(map[string]charm.Device) 352 for k, v := range devices { 353 results[k] = charm.Device{ 354 Name: v.Name, 355 Description: v.Description, 356 Type: charm.DeviceType(v.Type), 357 CountMin: v.CountMin, 358 CountMax: v.CountMax, 359 } 360 } 361 return results 362 } 363 364 type charmImpl struct { 365 info *CharmInfo 366 } 367 368 func (c *charmImpl) Meta() *charm.Meta { 369 return c.info.Meta 370 } 371 372 func (c *charmImpl) Config() *charm.Config { 373 return c.info.Config 374 } 375 376 func (c *charmImpl) Metrics() *charm.Metrics { 377 return c.info.Metrics 378 } 379 380 func (c *charmImpl) Manifest() *charm.Manifest { 381 return c.info.Manifest 382 } 383 384 func (c *charmImpl) Actions() *charm.Actions { 385 return c.info.Actions 386 } 387 388 func (c *charmImpl) Revision() int { 389 return c.info.Revision 390 } 391 392 func convertCharmManifest(input *params.CharmManifest) (*charm.Manifest, error) { 393 if input == nil { 394 return nil, nil 395 } 396 res := []charm.Base(nil) 397 for _, v := range input.Bases { 398 str := fmt.Sprintf("%s@%s", v.Name, v.Channel) 399 b, err := charm.ParseBase(str, v.Architectures...) 400 if err != nil { 401 return nil, errors.Trace(err) 402 } 403 res = append(res, b) 404 } 405 return &charm.Manifest{Bases: res}, nil 406 } 407 408 func convertCharmContainers(input map[string]params.CharmContainer) (map[string]charm.Container, error) { 409 containers := map[string]charm.Container{} 410 for k, v := range input { 411 containers[k] = charm.Container{ 412 Resource: v.Resource, 413 Mounts: convertCharmMounts(v.Mounts), 414 Uid: v.Uid, 415 Gid: v.Gid, 416 } 417 } 418 if len(containers) == 0 { 419 return nil, nil 420 } 421 return containers, nil 422 } 423 424 func convertCharmMounts(input []params.CharmMount) []charm.Mount { 425 mounts := []charm.Mount(nil) 426 for _, v := range input { 427 mounts = append(mounts, charm.Mount{ 428 Storage: v.Storage, 429 Location: v.Location, 430 }) 431 } 432 return mounts 433 }