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  }