github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/client/application/get.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package application
     5  
     6  import (
     7  	"github.com/juju/charm/v12"
     8  	"github.com/juju/schema"
     9  	"gopkg.in/juju/environschema.v1"
    10  
    11  	apiservererrors "github.com/juju/juju/apiserver/errors"
    12  	"github.com/juju/juju/caas"
    13  	corebase "github.com/juju/juju/core/base"
    14  	corecharm "github.com/juju/juju/core/charm"
    15  	"github.com/juju/juju/core/config"
    16  	"github.com/juju/juju/core/constraints"
    17  	"github.com/juju/juju/core/model"
    18  	"github.com/juju/juju/rpc/params"
    19  )
    20  
    21  // Get returns the charm configuration for an application.
    22  func (api *APIBase) Get(args params.ApplicationGet) (params.ApplicationGetResults, error) {
    23  	return api.getConfig(args, describe)
    24  }
    25  
    26  // Get returns the charm configuration for an application.
    27  func (api *APIBase) getConfig(
    28  	args params.ApplicationGet,
    29  	describe func(settings charm.Settings, config *charm.Config) map[string]interface{},
    30  ) (params.ApplicationGetResults, error) {
    31  	if err := api.checkCanRead(); err != nil {
    32  		return params.ApplicationGetResults{}, err
    33  	}
    34  
    35  	app, err := api.backend.Application(args.ApplicationName)
    36  	if err != nil {
    37  		return params.ApplicationGetResults{}, err
    38  	}
    39  
    40  	// We need a guard on the API server-side for direct API callers such as
    41  	// python-libjuju. Always default to the master branch.
    42  	if args.BranchName == "" {
    43  		args.BranchName = model.GenerationMaster
    44  	}
    45  	settings, err := app.CharmConfig(args.BranchName)
    46  	if err != nil {
    47  		return params.ApplicationGetResults{}, err
    48  	}
    49  
    50  	ch, _, err := app.Charm()
    51  	if err != nil {
    52  		return params.ApplicationGetResults{}, err
    53  	}
    54  	configInfo := describe(settings, ch.Config())
    55  	appConfig, err := app.ApplicationConfig()
    56  	if err != nil {
    57  		return params.ApplicationGetResults{}, err
    58  	}
    59  
    60  	providerSchema, providerDefaults, err := applicationConfigSchema(api.modelType)
    61  	if err != nil {
    62  		return params.ApplicationGetResults{}, err
    63  	}
    64  	appConfigInfo := describeAppConfig(appConfig, providerSchema, caas.ConfigDefaults(providerDefaults))
    65  	var cons constraints.Value
    66  	if app.IsPrincipal() {
    67  		cons, err = app.Constraints()
    68  		if err != nil {
    69  			return params.ApplicationGetResults{}, err
    70  		}
    71  	}
    72  	endpoints, err := app.EndpointBindings()
    73  	if err != nil {
    74  		return params.ApplicationGetResults{}, err
    75  	}
    76  
    77  	allSpaceInfosLookup, err := api.backend.AllSpaceInfos()
    78  	if err != nil {
    79  		return params.ApplicationGetResults{}, apiservererrors.ServerError(err)
    80  	}
    81  
    82  	bindingMap, err := endpoints.MapWithSpaceNames(allSpaceInfosLookup)
    83  	if err != nil {
    84  		return params.ApplicationGetResults{}, err
    85  	}
    86  
    87  	var appChannel string
    88  
    89  	// If the applications charm origin is from charm-hub, then build the real
    90  	// channel and send that back.
    91  	origin := app.CharmOrigin()
    92  	if corecharm.CharmHub.Matches(origin.Source) && origin.Channel != nil {
    93  		ch := charm.MakePermissiveChannel(origin.Channel.Track, origin.Channel.Risk, origin.Channel.Branch)
    94  		appChannel = ch.String()
    95  	}
    96  
    97  	base, err := corebase.ParseBase(origin.Platform.OS, origin.Platform.Channel)
    98  	if err != nil {
    99  		return params.ApplicationGetResults{}, err
   100  	}
   101  	return params.ApplicationGetResults{
   102  		Application:       args.ApplicationName,
   103  		Charm:             ch.Meta().Name,
   104  		CharmConfig:       configInfo,
   105  		ApplicationConfig: appConfigInfo,
   106  		Constraints:       cons,
   107  		Base: params.Base{
   108  			Name:    base.OS,
   109  			Channel: base.Channel.String(),
   110  		},
   111  		Channel:          appChannel,
   112  		EndpointBindings: bindingMap,
   113  	}, nil
   114  }
   115  
   116  func describeAppConfig(
   117  	appConfig config.ConfigAttributes,
   118  	schema environschema.Fields,
   119  	defaults schema.Defaults,
   120  ) map[string]interface{} {
   121  	results := make(map[string]interface{})
   122  	for name, field := range schema {
   123  		defaultValue := defaults[name]
   124  		info := map[string]interface{}{
   125  			"description": field.Description,
   126  			"type":        field.Type,
   127  			"source":      "unset",
   128  		}
   129  		set := false
   130  		if value := appConfig[name]; value != nil && defaultValue != value {
   131  			set = true
   132  			info["value"] = value
   133  			info["source"] = "user"
   134  		}
   135  		if defaultValue != nil {
   136  			info["default"] = defaultValue
   137  			if !set {
   138  				info["value"] = defaultValue
   139  				info["source"] = "default"
   140  			}
   141  		}
   142  		results[name] = info
   143  	}
   144  	return results
   145  }
   146  
   147  func describe(settings charm.Settings, config *charm.Config) map[string]interface{} {
   148  	results := make(map[string]interface{})
   149  	for name, option := range config.Options {
   150  		info := map[string]interface{}{
   151  			"description": option.Description,
   152  			"type":        option.Type,
   153  			"source":      "unset",
   154  		}
   155  		set := false
   156  		if value := settings[name]; value != nil && option.Default != value {
   157  			set = true
   158  			info["value"] = value
   159  			info["source"] = "user"
   160  		}
   161  		if option.Default != nil {
   162  			info["default"] = option.Default
   163  			if !set {
   164  				info["value"] = option.Default
   165  				info["source"] = "default"
   166  			}
   167  		}
   168  		results[name] = info
   169  	}
   170  	return results
   171  }