16
16
"github.com/juju/juju/apiserver/common"
17
17
"github.com/juju/juju/apiserver/facade"
18
18
"github.com/juju/juju/apiserver/params"
19
"github.com/juju/juju/core/description"
20
19
"github.com/juju/juju/instance"
21
20
jjj "github.com/juju/juju/juju"
21
"github.com/juju/juju/permission"
22
22
"github.com/juju/juju/state"
23
23
statestorage "github.com/juju/juju/state/storage"
33
common.RegisterStandardFacade("Application", 1, NewAPI)
33
common.RegisterStandardFacade("Application", 1, newAPI)
36
// Application defines the methods on the application API end point.
37
type Application interface {
38
SetMetricCredentials(args params.ApplicationMetricCredentials) (params.ErrorResults, error)
35
// Facade version 2 adds support for the ConfigSettings
36
// and StorageConstraints fields in SetCharm.
37
common.RegisterStandardFacade("Application", 2, newAPI)
41
40
// API implements the application interface and is the concrete
42
41
// implementation of the api end point.
44
check *common.BlockChecker
46
44
authorizer facade.Authorizer
47
// TODO(axw) stateCharm only exists because I ran out
48
// of time unwinding all of the tendrils of state. We
49
// should pass a charm.Charm and charm.URL back into
50
// state wherever we pass in a state.Charm currently.
51
stateCharm func(Charm) *state.Charm
56
resources facade.Resources,
57
authorizer facade.Authorizer,
59
backend := NewStateBackend(st)
60
blockChecker := common.NewBlockChecker(st)
61
stateCharm := CharmToStateCharm
49
70
// NewAPI returns a new application API facade.
52
resources facade.Resources,
53
73
authorizer facade.Authorizer,
74
blockChecker BlockChecker,
75
stateCharm func(Charm) *state.Charm,
56
77
if !authorizer.AuthClient() {
57
78
return nil, common.ErrPerm
62
82
authorizer: authorizer,
63
check: common.NewBlockChecker(st),
84
stateCharm: stateCharm,
67
88
func (api *API) checkCanRead() error {
68
canRead, err := api.authorizer.HasPermission(description.ReadAccess, api.state.ModelTag())
89
canRead, err := api.authorizer.HasPermission(permission.ReadAccess, api.backend.ModelTag())
70
91
return errors.Trace(err)
133
154
// deployApplication fetches the charm from the charm store and deploys it.
134
155
// The logic has been factored out into a common function which is called by
135
156
// both the legacy API on the client facade, as well as the new application facade.
136
func deployApplication(st *state.State, args params.ApplicationDeploy) error {
157
func deployApplication(
159
stateCharm func(Charm) *state.Charm,
160
args params.ApplicationDeploy,
137
162
curl, err := charm.ParseURL(args.CharmUrl)
139
164
return errors.Trace(err)
147
172
if p.Scope != instance.MachineScope {
150
_, err = st.Machine(p.Directive)
175
_, err = backend.Machine(p.Directive)
152
177
return errors.Annotatef(err, `cannot deploy "%v" to machine %v`, args.ApplicationName, p.Directive)
156
181
// Try to find the charm URL in state first.
157
ch, err := st.Charm(curl)
182
ch, err := backend.Charm(curl)
159
184
return errors.Trace(err)
168
193
settings, err = ch.Config().ParseSettingsYAML([]byte(args.ConfigYAML), args.ApplicationName)
169
194
} else if len(args.Config) > 0 {
170
195
// Parse config in a compatible way (see function comment).
171
settings, err = parseSettingsCompatible(ch, args.Config)
196
settings, err = parseSettingsCompatible(ch.Config(), args.Config)
174
199
return errors.Trace(err)
177
202
channel := csparams.Channel(args.Channel)
179
_, err = jjj.DeployApplication(st,
204
_, err = jjj.DeployApplication(backend,
180
205
jjj.DeployApplicationParams{
181
206
ApplicationName: args.ApplicationName,
182
207
Series: args.Series,
208
Charm: stateCharm(ch),
184
209
Channel: channel,
185
210
NumUnits: args.NumUnits,
186
211
ConfigSettings: settings,
196
221
// ApplicationSetSettingsStrings updates the settings for the given application,
197
222
// taking the configuration from a map of strings.
198
func ApplicationSetSettingsStrings(application *state.Application, settings map[string]string) error {
223
func ApplicationSetSettingsStrings(application Application, settings map[string]string) error {
199
224
ch, _, err := application.Charm()
201
226
return errors.Trace(err)
203
228
// Parse config in a compatible way (see function comment).
204
changes, err := parseSettingsCompatible(ch, settings)
229
changes, err := parseSettingsCompatible(ch.Config(), settings)
206
231
return errors.Trace(err)
214
239
// string caused it to reset to the default value. We now allow
215
240
// empty strings as actual values, but we want to preserve the API
217
func parseSettingsCompatible(ch *state.Charm, settings map[string]string) (charm.Settings, error) {
242
func parseSettingsCompatible(charmConfig *charm.Config, settings map[string]string) (charm.Settings, error) {
218
243
setSettings := map[string]string{}
219
244
unsetSettings := charm.Settings{}
220
245
// Split settings into those which set and those which unset a value.
226
251
setSettings[name] = value
228
253
// Validate the settings.
229
changes, err := ch.Config().ParseSettingsStrings(setSettings)
254
changes, err := charmConfig.ParseSettingsStrings(setSettings)
231
256
return nil, errors.Trace(err)
233
258
// Validate the unsettings and merge them into the changes.
234
unsetSettings, err = ch.Config().ValidateSettings(unsetSettings)
259
unsetSettings, err = charmConfig.ValidateSettings(unsetSettings)
236
261
return nil, errors.Trace(err)
261
286
if args.CharmUrl != "" {
262
287
// For now we do not support changing the channel through Update().
263
288
// TODO(ericsnow) Support it?
264
channel := svc.Channel()
265
if err = api.applicationSetCharm(svc, args.CharmUrl, channel, args.ForceSeries, args.ForceCharmUrl, nil); err != nil {
289
channel := app.Channel()
290
if err = api.applicationSetCharm(
291
args.ApplicationName,
295
nil, // charm settings (strings map)
296
"", // charm settings (YAML)
300
nil, // storage constraints
266
302
return errors.Trace(err)
269
305
// Set the minimum number of units for the given application.
270
306
if args.MinUnits != nil {
271
if err = svc.SetMinUnits(*args.MinUnits); err != nil {
307
if err = app.SetMinUnits(*args.MinUnits); err != nil {
272
308
return errors.Trace(err)
275
311
// Set up application's settings.
276
312
if args.SettingsYAML != "" {
277
if err = applicationSetSettingsYAML(svc, args.SettingsYAML); err != nil {
313
if err = applicationSetSettingsYAML(args.ApplicationName, app, args.SettingsYAML); err != nil {
278
314
return errors.Annotate(err, "setting configuration from YAML")
280
316
} else if len(args.SettingsStrings) > 0 {
281
if err = ApplicationSetSettingsStrings(svc, args.SettingsStrings); err != nil {
317
if err = ApplicationSetSettingsStrings(app, args.SettingsStrings); err != nil {
282
318
return errors.Trace(err)
285
321
// Update application's constraints.
286
322
if args.Constraints != nil {
287
return svc.SetConstraints(*args.Constraints)
323
return app.SetConstraints(*args.Constraints)
300
336
return errors.Trace(err)
303
application, err := api.state.Application(args.ApplicationName)
339
application, err := api.backend.Application(args.ApplicationName)
305
341
return errors.Trace(err)
307
343
channel := csparams.Channel(args.Channel)
308
return api.applicationSetCharm(application, args.CharmUrl, channel, args.ForceSeries, args.ForceUnits, args.ResourceIDs)
344
return api.applicationSetCharm(
345
args.ApplicationName,
350
args.ConfigSettingsYAML,
354
args.StorageConstraints,
311
358
// applicationSetCharm sets the charm for the given for the application.
312
func (api *API) applicationSetCharm(application *state.Application, url string, channel csparams.Channel, forceSeries, forceUnits bool, resourceIDs map[string]string) error {
359
func (api *API) applicationSetCharm(
361
application Application,
363
channel csparams.Channel,
364
configSettingsStrings map[string]string,
365
configSettingsYAML string,
368
resourceIDs map[string]string,
369
storageConstraints map[string]params.StorageConstraints,
313
371
curl, err := charm.ParseURL(url)
315
373
return errors.Trace(err)
317
sch, err := api.state.Charm(curl)
375
sch, err := api.backend.Charm(curl)
319
377
return errors.Trace(err)
379
var settings charm.Settings
380
if configSettingsYAML != "" {
381
settings, err = sch.Config().ParseSettingsYAML([]byte(configSettingsYAML), appName)
382
} else if len(configSettingsStrings) > 0 {
383
settings, err = parseSettingsCompatible(sch.Config(), configSettingsStrings)
386
return errors.Annotate(err, "parsing config settings")
388
var stateStorageConstraints map[string]state.StorageConstraints
389
if len(storageConstraints) > 0 {
390
stateStorageConstraints = make(map[string]state.StorageConstraints)
391
for name, cons := range storageConstraints {
392
stateCons := state.StorageConstraints{Pool: cons.Pool}
393
if cons.Size != nil {
394
stateCons.Size = *cons.Size
396
if cons.Count != nil {
397
stateCons.Count = *cons.Count
399
stateStorageConstraints[name] = stateCons
321
402
cfg := state.SetCharmConfig{
324
ForceSeries: forceSeries,
325
ForceUnits: forceUnits,
326
ResourceIDs: resourceIDs,
403
Charm: api.stateCharm(sch),
405
ConfigSettings: settings,
406
ForceSeries: forceSeries,
407
ForceUnits: forceUnits,
408
ResourceIDs: resourceIDs,
409
StorageConstraints: stateStorageConstraints,
328
411
return application.SetCharm(cfg)
359
442
// applicationSetSettingsYAML updates the settings for the given application,
360
443
// taking the configuration from a YAML string.
361
func applicationSetSettingsYAML(application *state.Application, settings string) error {
444
func applicationSetSettingsYAML(appName string, application Application, settings string) error {
362
445
b := []byte(settings)
363
446
var all map[string]interface{}
364
447
if err := goyaml.Unmarshal(b, &all); err != nil {
365
448
return errors.Annotate(err, "parsing settings data")
367
450
// The file is already in the right format.
368
if _, ok := all[application.Name()]; !ok {
451
if _, ok := all[appName]; !ok {
369
452
changes, err := settingsFromGetYaml(all)
371
454
return errors.Annotate(err, "processing YAML generated by get")
409
492
if err := api.check.ChangeAllowed(); err != nil {
410
493
return errors.Trace(err)
412
svc, err := api.state.Application(p.ApplicationName)
495
app, err := api.backend.Application(p.ApplicationName)
416
ch, _, err := svc.Charm()
499
ch, _, err := app.Charm()
493
576
if err := api.check.ChangeAllowed(); err != nil {
494
577
return errors.Trace(err)
496
svc, err := api.state.Application(args.ApplicationName)
579
app, err := api.backend.Application(args.ApplicationName)
500
return svc.ClearExposed()
583
return app.ClearExposed()
503
586
// addApplicationUnits adds a given number of units to an application.
504
func addApplicationUnits(st *state.State, args params.AddApplicationUnits) ([]*state.Unit, error) {
505
application, err := st.Application(args.ApplicationName)
587
func addApplicationUnits(backend Backend, args params.AddApplicationUnits) ([]*state.Unit, error) {
588
application, err := backend.Application(args.ApplicationName)
507
590
return nil, errors.Trace(err)
509
592
if args.NumUnits < 1 {
510
593
return nil, errors.New("must add at least one unit")
512
return jjj.AddUnits(st, application, args.NumUnits, args.Placement)
595
return jjj.AddUnits(backend, application, args.ApplicationName, args.NumUnits, args.Placement)
515
598
// AddUnits adds a given number of units to an application.
520
603
if err := api.check.ChangeAllowed(); err != nil {
521
604
return params.AddApplicationUnitsResults{}, errors.Trace(err)
523
units, err := addApplicationUnits(api.state, args)
606
units, err := addApplicationUnits(api.backend, args)
525
608
return params.AddApplicationUnitsResults{}, errors.Trace(err)
580
663
if err := api.checkCanRead(); err != nil {
581
664
return params.GetConstraintsResults{}, errors.Trace(err)
583
svc, err := api.state.Application(args.ApplicationName)
666
app, err := api.backend.Application(args.ApplicationName)
585
668
return params.GetConstraintsResults{}, errors.Trace(err)
587
cons, err := svc.Constraints()
670
cons, err := app.Constraints()
588
671
return params.GetConstraintsResults{cons}, errors.Trace(err)
611
694
if err := api.check.ChangeAllowed(); err != nil {
612
695
return params.AddRelationResults{}, errors.Trace(err)
614
inEps, err := api.state.InferEndpoints(args.Endpoints...)
697
inEps, err := api.backend.InferEndpoints(args.Endpoints...)
616
699
return params.AddRelationResults{}, errors.Trace(err)
618
rel, err := api.state.AddRelation(inEps...)
701
rel, err := api.backend.AddRelation(inEps...)
620
703
return params.AddRelationResults{}, errors.Trace(err)