~juju-qa/ubuntu/xenial/juju/2.0-rc2

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/apiserver/application/application.go

  • Committer: Nicholas Skaggs
  • Date: 2016-09-30 14:39:30 UTC
  • mfrom: (1.8.1)
  • Revision ID: nicholas.skaggs@canonical.com-20160930143930-vwwhrefh6ftckccy
import upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
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"
24
24
)
30
30
)
31
31
 
32
32
func init() {
33
 
        common.RegisterStandardFacade("Application", 1, NewAPI)
34
 
}
 
33
        common.RegisterStandardFacade("Application", 1, newAPI)
35
34
 
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)
39
38
}
40
39
 
41
40
// API implements the application interface and is the concrete
42
41
// implementation of the api end point.
43
42
type API struct {
44
 
        check      *common.BlockChecker
45
 
        state      *state.State
 
43
        backend    Backend
46
44
        authorizer facade.Authorizer
 
45
        check      BlockChecker
 
46
 
 
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
 
52
}
 
53
 
 
54
func newAPI(
 
55
        st *state.State,
 
56
        resources facade.Resources,
 
57
        authorizer facade.Authorizer,
 
58
) (*API, error) {
 
59
        backend := NewStateBackend(st)
 
60
        blockChecker := common.NewBlockChecker(st)
 
61
        stateCharm := CharmToStateCharm
 
62
        return NewAPI(
 
63
                backend,
 
64
                authorizer,
 
65
                blockChecker,
 
66
                stateCharm,
 
67
        )
47
68
}
48
69
 
49
70
// NewAPI returns a new application API facade.
50
71
func NewAPI(
51
 
        st *state.State,
52
 
        resources facade.Resources,
 
72
        backend Backend,
53
73
        authorizer facade.Authorizer,
 
74
        blockChecker BlockChecker,
 
75
        stateCharm func(Charm) *state.Charm,
54
76
) (*API, error) {
55
 
 
56
77
        if !authorizer.AuthClient() {
57
78
                return nil, common.ErrPerm
58
79
        }
59
 
 
60
80
        return &API{
61
 
                state:      st,
 
81
                backend:    backend,
62
82
                authorizer: authorizer,
63
 
                check:      common.NewBlockChecker(st),
 
83
                check:      blockChecker,
 
84
                stateCharm: stateCharm,
64
85
        }, nil
65
86
}
66
87
 
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())
69
90
        if err != nil {
70
91
                return errors.Trace(err)
71
92
        }
76
97
}
77
98
 
78
99
func (api *API) checkCanWrite() error {
79
 
        canWrite, err := api.authorizer.HasPermission(description.WriteAccess, api.state.ModelTag())
 
100
        canWrite, err := api.authorizer.HasPermission(permission.WriteAccess, api.backend.ModelTag())
80
101
        if err != nil {
81
102
                return errors.Trace(err)
82
103
        }
98
119
                return result, nil
99
120
        }
100
121
        for i, a := range args.Creds {
101
 
                application, err := api.state.Application(a.ApplicationName)
 
122
                application, err := api.backend.Application(a.ApplicationName)
102
123
                if err != nil {
103
124
                        result.Results[i].Error = common.ServerError(err)
104
125
                        continue
124
145
                return result, errors.Trace(err)
125
146
        }
126
147
        for i, arg := range args.Applications {
127
 
                err := deployApplication(api.state, arg)
 
148
                err := deployApplication(api.backend, api.stateCharm, arg)
128
149
                result.Results[i].Error = common.ServerError(err)
129
150
        }
130
151
        return result, nil
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(
 
158
        backend Backend,
 
159
        stateCharm func(Charm) *state.Charm,
 
160
        args params.ApplicationDeploy,
 
161
) error {
137
162
        curl, err := charm.ParseURL(args.CharmUrl)
138
163
        if err != nil {
139
164
                return errors.Trace(err)
147
172
                if p.Scope != instance.MachineScope {
148
173
                        continue
149
174
                }
150
 
                _, err = st.Machine(p.Directive)
 
175
                _, err = backend.Machine(p.Directive)
151
176
                if err != nil {
152
177
                        return errors.Annotatef(err, `cannot deploy "%v" to machine %v`, args.ApplicationName, p.Directive)
153
178
                }
154
179
        }
155
180
 
156
181
        // Try to find the charm URL in state first.
157
 
        ch, err := st.Charm(curl)
 
182
        ch, err := backend.Charm(curl)
158
183
        if err != nil {
159
184
                return errors.Trace(err)
160
185
        }
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)
172
197
        }
173
198
        if err != nil {
174
199
                return errors.Trace(err)
176
201
 
177
202
        channel := csparams.Channel(args.Channel)
178
203
 
179
 
        _, err = jjj.DeployApplication(st,
 
204
        _, err = jjj.DeployApplication(backend,
180
205
                jjj.DeployApplicationParams{
181
206
                        ApplicationName:  args.ApplicationName,
182
207
                        Series:           args.Series,
183
 
                        Charm:            ch,
 
208
                        Charm:            stateCharm(ch),
184
209
                        Channel:          channel,
185
210
                        NumUnits:         args.NumUnits,
186
211
                        ConfigSettings:   settings,
195
220
 
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()
200
225
        if err != nil {
201
226
                return errors.Trace(err)
202
227
        }
203
228
        // Parse config in a compatible way (see function comment).
204
 
        changes, err := parseSettingsCompatible(ch, settings)
 
229
        changes, err := parseSettingsCompatible(ch.Config(), settings)
205
230
        if err != nil {
206
231
                return errors.Trace(err)
207
232
        }
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
216
241
// behavior.
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
227
252
        }
228
253
        // Validate the settings.
229
 
        changes, err := ch.Config().ParseSettingsStrings(setSettings)
 
254
        changes, err := charmConfig.ParseSettingsStrings(setSettings)
230
255
        if err != nil {
231
256
                return nil, errors.Trace(err)
232
257
        }
233
258
        // Validate the unsettings and merge them into the changes.
234
 
        unsetSettings, err = ch.Config().ValidateSettings(unsetSettings)
 
259
        unsetSettings, err = charmConfig.ValidateSettings(unsetSettings)
235
260
        if err != nil {
236
261
                return nil, errors.Trace(err)
237
262
        }
253
278
                        return errors.Trace(err)
254
279
                }
255
280
        }
256
 
        svc, err := api.state.Application(args.ApplicationName)
 
281
        app, err := api.backend.Application(args.ApplicationName)
257
282
        if err != nil {
258
283
                return errors.Trace(err)
259
284
        }
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,
 
292
                        app,
 
293
                        args.CharmUrl,
 
294
                        channel,
 
295
                        nil, // charm settings (strings map)
 
296
                        "",  // charm settings (YAML)
 
297
                        args.ForceSeries,
 
298
                        args.ForceCharmUrl,
 
299
                        nil, // resource IDs
 
300
                        nil, // storage constraints
 
301
                ); err != nil {
266
302
                        return errors.Trace(err)
267
303
                }
268
304
        }
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)
273
309
                }
274
310
        }
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")
279
315
                }
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)
283
319
                }
284
320
        }
285
321
        // Update application's constraints.
286
322
        if args.Constraints != nil {
287
 
                return svc.SetConstraints(*args.Constraints)
 
323
                return app.SetConstraints(*args.Constraints)
288
324
        }
289
325
        return nil
290
326
}
300
336
                        return errors.Trace(err)
301
337
                }
302
338
        }
303
 
        application, err := api.state.Application(args.ApplicationName)
 
339
        application, err := api.backend.Application(args.ApplicationName)
304
340
        if err != nil {
305
341
                return errors.Trace(err)
306
342
        }
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,
 
346
                application,
 
347
                args.CharmUrl,
 
348
                channel,
 
349
                args.ConfigSettings,
 
350
                args.ConfigSettingsYAML,
 
351
                args.ForceSeries,
 
352
                args.ForceUnits,
 
353
                args.ResourceIDs,
 
354
                args.StorageConstraints,
 
355
        )
309
356
}
310
357
 
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(
 
360
        appName string,
 
361
        application Application,
 
362
        url string,
 
363
        channel csparams.Channel,
 
364
        configSettingsStrings map[string]string,
 
365
        configSettingsYAML string,
 
366
        forceSeries,
 
367
        forceUnits bool,
 
368
        resourceIDs map[string]string,
 
369
        storageConstraints map[string]params.StorageConstraints,
 
370
) error {
313
371
        curl, err := charm.ParseURL(url)
314
372
        if err != nil {
315
373
                return errors.Trace(err)
316
374
        }
317
 
        sch, err := api.state.Charm(curl)
 
375
        sch, err := api.backend.Charm(curl)
318
376
        if err != nil {
319
377
                return errors.Trace(err)
320
378
        }
 
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)
 
384
        }
 
385
        if err != nil {
 
386
                return errors.Annotate(err, "parsing config settings")
 
387
        }
 
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
 
395
                        }
 
396
                        if cons.Count != nil {
 
397
                                stateCons.Count = *cons.Count
 
398
                        }
 
399
                        stateStorageConstraints[name] = stateCons
 
400
                }
 
401
        }
321
402
        cfg := state.SetCharmConfig{
322
 
                Charm:       sch,
323
 
                Channel:     channel,
324
 
                ForceSeries: forceSeries,
325
 
                ForceUnits:  forceUnits,
326
 
                ResourceIDs: resourceIDs,
 
403
                Charm:              api.stateCharm(sch),
 
404
                Channel:            channel,
 
405
                ConfigSettings:     settings,
 
406
                ForceSeries:        forceSeries,
 
407
                ForceUnits:         forceUnits,
 
408
                ResourceIDs:        resourceIDs,
 
409
                StorageConstraints: stateStorageConstraints,
327
410
        }
328
411
        return application.SetCharm(cfg)
329
412
}
358
441
 
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")
366
449
        }
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)
370
453
                if err != nil {
371
454
                        return errors.Annotate(err, "processing YAML generated by get")
378
461
                return errors.Annotate(err, "obtaining charm for this application")
379
462
        }
380
463
 
381
 
        changes, err := ch.Config().ParseSettingsYAML(b, application.Name())
 
464
        changes, err := ch.Config().ParseSettingsYAML(b, appName)
382
465
        if err != nil {
383
466
                return errors.Annotate(err, "creating config from YAML")
384
467
        }
391
474
        if err := api.checkCanWrite(); err != nil {
392
475
                return params.StringResult{}, errors.Trace(err)
393
476
        }
394
 
        application, err := api.state.Application(args.ApplicationName)
 
477
        application, err := api.backend.Application(args.ApplicationName)
395
478
        if err != nil {
396
479
                return params.StringResult{}, errors.Trace(err)
397
480
        }
409
492
        if err := api.check.ChangeAllowed(); err != nil {
410
493
                return errors.Trace(err)
411
494
        }
412
 
        svc, err := api.state.Application(p.ApplicationName)
 
495
        app, err := api.backend.Application(p.ApplicationName)
413
496
        if err != nil {
414
497
                return err
415
498
        }
416
 
        ch, _, err := svc.Charm()
 
499
        ch, _, err := app.Charm()
417
500
        if err != nil {
418
501
                return err
419
502
        }
423
506
                return err
424
507
        }
425
508
 
426
 
        return svc.UpdateConfigSettings(changes)
 
509
        return app.UpdateConfigSettings(changes)
427
510
 
428
511
}
429
512
 
435
518
        if err := api.check.ChangeAllowed(); err != nil {
436
519
                return errors.Trace(err)
437
520
        }
438
 
        svc, err := api.state.Application(p.ApplicationName)
 
521
        app, err := api.backend.Application(p.ApplicationName)
439
522
        if err != nil {
440
523
                return err
441
524
        }
443
526
        for _, option := range p.Options {
444
527
                settings[option] = nil
445
528
        }
446
 
        return svc.UpdateConfigSettings(settings)
 
529
        return app.UpdateConfigSettings(settings)
447
530
}
448
531
 
449
532
// CharmRelations implements the server side of Application.CharmRelations.
453
536
                return results, errors.Trace(err)
454
537
        }
455
538
 
456
 
        application, err := api.state.Application(p.ApplicationName)
 
539
        application, err := api.backend.Application(p.ApplicationName)
457
540
        if err != nil {
458
541
                return results, errors.Trace(err)
459
542
        }
477
560
        if err := api.check.ChangeAllowed(); err != nil {
478
561
                return errors.Trace(err)
479
562
        }
480
 
        svc, err := api.state.Application(args.ApplicationName)
 
563
        app, err := api.backend.Application(args.ApplicationName)
481
564
        if err != nil {
482
565
                return err
483
566
        }
484
 
        return svc.SetExposed()
 
567
        return app.SetExposed()
485
568
}
486
569
 
487
570
// Unexpose changes the juju-managed firewall to unexpose any ports that
493
576
        if err := api.check.ChangeAllowed(); err != nil {
494
577
                return errors.Trace(err)
495
578
        }
496
 
        svc, err := api.state.Application(args.ApplicationName)
 
579
        app, err := api.backend.Application(args.ApplicationName)
497
580
        if err != nil {
498
581
                return err
499
582
        }
500
 
        return svc.ClearExposed()
 
583
        return app.ClearExposed()
501
584
}
502
585
 
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)
506
589
        if err != nil {
507
590
                return nil, errors.Trace(err)
508
591
        }
509
592
        if args.NumUnits < 1 {
510
593
                return nil, errors.New("must add at least one unit")
511
594
        }
512
 
        return jjj.AddUnits(st, application, args.NumUnits, args.Placement)
 
595
        return jjj.AddUnits(backend, application, args.ApplicationName, args.NumUnits, args.Placement)
513
596
}
514
597
 
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)
522
605
        }
523
 
        units, err := addApplicationUnits(api.state, args)
 
606
        units, err := addApplicationUnits(api.backend, args)
524
607
        if err != nil {
525
608
                return params.AddApplicationUnitsResults{}, errors.Trace(err)
526
609
        }
541
624
        }
542
625
        var errs []string
543
626
        for _, name := range args.UnitNames {
544
 
                unit, err := api.state.Unit(name)
 
627
                unit, err := api.backend.Unit(name)
545
628
                switch {
546
629
                case errors.IsNotFound(err):
547
630
                        err = errors.Errorf("unit %q does not exist", name)
568
651
        if err := api.check.RemoveAllowed(); err != nil {
569
652
                return errors.Trace(err)
570
653
        }
571
 
        svc, err := api.state.Application(args.ApplicationName)
 
654
        app, err := api.backend.Application(args.ApplicationName)
572
655
        if err != nil {
573
656
                return err
574
657
        }
575
 
        return svc.Destroy()
 
658
        return app.Destroy()
576
659
}
577
660
 
578
661
// GetConstraints returns the constraints for a given application.
580
663
        if err := api.checkCanRead(); err != nil {
581
664
                return params.GetConstraintsResults{}, errors.Trace(err)
582
665
        }
583
 
        svc, err := api.state.Application(args.ApplicationName)
 
666
        app, err := api.backend.Application(args.ApplicationName)
584
667
        if err != nil {
585
668
                return params.GetConstraintsResults{}, errors.Trace(err)
586
669
        }
587
 
        cons, err := svc.Constraints()
 
670
        cons, err := app.Constraints()
588
671
        return params.GetConstraintsResults{cons}, errors.Trace(err)
589
672
}
590
673
 
596
679
        if err := api.check.ChangeAllowed(); err != nil {
597
680
                return errors.Trace(err)
598
681
        }
599
 
        svc, err := api.state.Application(args.ApplicationName)
 
682
        app, err := api.backend.Application(args.ApplicationName)
600
683
        if err != nil {
601
684
                return err
602
685
        }
603
 
        return svc.SetConstraints(args.Constraints)
 
686
        return app.SetConstraints(args.Constraints)
604
687
}
605
688
 
606
689
// AddRelation adds a relation between the specified endpoints and returns the relation info.
611
694
        if err := api.check.ChangeAllowed(); err != nil {
612
695
                return params.AddRelationResults{}, errors.Trace(err)
613
696
        }
614
 
        inEps, err := api.state.InferEndpoints(args.Endpoints...)
 
697
        inEps, err := api.backend.InferEndpoints(args.Endpoints...)
615
698
        if err != nil {
616
699
                return params.AddRelationResults{}, errors.Trace(err)
617
700
        }
618
 
        rel, err := api.state.AddRelation(inEps...)
 
701
        rel, err := api.backend.AddRelation(inEps...)
619
702
        if err != nil {
620
703
                return params.AddRelationResults{}, errors.Trace(err)
621
704
        }
645
728
        if err := api.check.RemoveAllowed(); err != nil {
646
729
                return errors.Trace(err)
647
730
        }
648
 
        eps, err := api.state.InferEndpoints(args.Endpoints...)
 
731
        eps, err := api.backend.InferEndpoints(args.Endpoints...)
649
732
        if err != nil {
650
733
                return err
651
734
        }
652
 
        rel, err := api.state.EndpointsRelation(eps...)
 
735
        rel, err := api.backend.EndpointsRelation(eps...)
653
736
        if err != nil {
654
737
                return err
655
738
        }