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 := &params.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 := &params.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 &params.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 &params.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  }