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 }