1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
4
// Package uniter implements the API interface used by the uniter worker.
11
"github.com/juju/errors"
12
"github.com/juju/loggo"
13
"gopkg.in/juju/charm.v6-unstable"
14
"gopkg.in/juju/names.v2"
16
"github.com/juju/juju/apiserver/common"
17
"github.com/juju/juju/apiserver/facade"
18
leadershipapiserver "github.com/juju/juju/apiserver/leadership"
19
"github.com/juju/juju/apiserver/meterstatus"
20
"github.com/juju/juju/apiserver/params"
21
"github.com/juju/juju/core/leadership"
22
"github.com/juju/juju/network"
23
"github.com/juju/juju/state"
24
"github.com/juju/juju/state/multiwatcher"
25
"github.com/juju/juju/state/watcher"
28
var logger = loggo.GetLogger("juju.apiserver.uniter")
31
common.RegisterStandardFacade("Uniter", 4, NewUniterAPIV4)
34
// UniterAPIV3 implements the API version 3, used by the uniter worker.
35
type UniterAPIV3 struct {
39
*common.AgentEntityWatcher
42
*common.RebootRequester
43
*leadershipapiserver.LeadershipSettingsAccessor
44
meterstatus.MeterStatus
47
auth facade.Authorizer
48
resources facade.Resources
49
accessUnit common.GetAuthFunc
50
accessService common.GetAuthFunc
52
accessMachine common.GetAuthFunc
56
// NewUniterAPIV4 creates a new instance of the Uniter API, version 3.
57
func NewUniterAPIV4(st *state.State, resources facade.Resources, authorizer facade.Authorizer) (*UniterAPIV3, error) {
58
if !authorizer.AuthUnitAgent() {
59
return nil, common.ErrPerm
63
switch tag := authorizer.GetAuthTag().(type) {
65
unit, err = st.Unit(tag.Id())
67
return nil, errors.Trace(err)
70
return nil, errors.Errorf("expected names.UnitTag, got %T", tag)
72
accessUnit := func() (common.AuthFunc, error) {
73
return authorizer.AuthOwner, nil
75
accessService := func() (common.AuthFunc, error) {
76
switch tag := authorizer.GetAuthTag().(type) {
78
entity, err := st.Unit(tag.Id())
80
return nil, errors.Trace(err)
82
applicationName := entity.ApplicationName()
83
applicationTag := names.NewApplicationTag(applicationName)
84
return func(tag names.Tag) bool {
85
return tag == applicationTag
88
return nil, errors.Errorf("expected names.UnitTag, got %T", tag)
91
accessMachine := func() (common.AuthFunc, error) {
92
switch tag := authorizer.GetAuthTag().(type) {
94
entity, err := st.Unit(tag.Id())
96
return nil, errors.Trace(err)
98
machineId, err := entity.AssignedMachineId()
100
return nil, errors.Trace(err)
102
machineTag := names.NewMachineTag(machineId)
103
return func(tag names.Tag) bool {
104
return tag == machineTag
107
return nil, errors.Errorf("expected names.UnitTag, got %T", tag)
110
storageAPI, err := newStorageAPI(getStorageState(st), resources, accessUnit)
114
msAPI, err := meterstatus.NewMeterStatusAPI(st, resources, authorizer)
116
return nil, errors.Annotate(err, "could not create meter status API handler")
118
accessUnitOrService := common.AuthEither(accessUnit, accessService)
120
LifeGetter: common.NewLifeGetter(st, accessUnitOrService),
121
DeadEnsurer: common.NewDeadEnsurer(st, accessUnit),
122
AgentEntityWatcher: common.NewAgentEntityWatcher(st, resources, accessUnitOrService),
123
APIAddresser: common.NewAPIAddresser(st, resources),
124
ModelWatcher: common.NewModelWatcher(st, resources, authorizer),
125
RebootRequester: common.NewRebootRequester(st, accessMachine),
126
LeadershipSettingsAccessor: leadershipSettingsAccessorFactory(st, resources, authorizer),
128
// TODO(fwereade): so *every* unit should be allowed to get/set its
129
// own status *and* its service's? This is not a pleasing arrangement.
130
StatusAPI: NewStatusAPI(st, accessUnitOrService),
134
resources: resources,
135
accessUnit: accessUnit,
136
accessService: accessService,
137
accessMachine: accessMachine,
139
StorageAPI: *storageAPI,
143
// AllMachinePorts returns all opened port ranges for each given
144
// machine (on all networks).
145
func (u *UniterAPIV3) AllMachinePorts(args params.Entities) (params.MachinePortsResults, error) {
146
result := params.MachinePortsResults{
147
Results: make([]params.MachinePortsResult, len(args.Entities)),
149
canAccess, err := u.accessMachine()
151
return params.MachinePortsResults{}, err
153
for i, entity := range args.Entities {
154
result.Results[i] = u.getOneMachinePorts(canAccess, entity.Tag)
159
// AssignedMachine returns the machine tag for each given unit tag, or
160
// an error satisfying params.IsCodeNotAssigned when a unit has no
162
func (u *UniterAPIV3) AssignedMachine(args params.Entities) (params.StringResults, error) {
163
result := params.StringResults{
164
Results: make([]params.StringResult, len(args.Entities)),
166
canAccess, err := u.accessUnit()
168
return params.StringResults{}, err
170
for i, entity := range args.Entities {
171
tag, err := names.ParseUnitTag(entity.Tag)
173
result.Results[i].Error = common.ServerError(common.ErrPerm)
177
result.Results[i].Error = common.ServerError(common.ErrPerm)
180
unit, err := u.getUnit(tag)
182
result.Results[i].Error = common.ServerError(err)
185
machineId, err := unit.AssignedMachineId()
187
result.Results[i].Error = common.ServerError(err)
189
result.Results[i].Result = names.NewMachineTag(machineId).String()
195
func (u *UniterAPIV3) getMachine(tag names.MachineTag) (*state.Machine, error) {
196
return u.st.Machine(tag.Id())
199
func (u *UniterAPIV3) getOneMachinePorts(canAccess common.AuthFunc, machineTag string) params.MachinePortsResult {
200
tag, err := names.ParseMachineTag(machineTag)
202
return params.MachinePortsResult{Error: common.ServerError(common.ErrPerm)}
205
return params.MachinePortsResult{Error: common.ServerError(common.ErrPerm)}
207
machine, err := u.getMachine(tag)
209
return params.MachinePortsResult{Error: common.ServerError(err)}
211
allPorts, err := machine.AllPorts()
213
return params.MachinePortsResult{Error: common.ServerError(err)}
215
var resultPorts []params.MachinePortRange
216
for _, ports := range allPorts {
217
// AllPortRanges gives a map, but apis require a stable order
218
// for results, so sort the port ranges.
219
portRangesToUnits := ports.AllPortRanges()
220
portRanges := make([]network.PortRange, 0, len(portRangesToUnits))
221
for portRange := range portRangesToUnits {
222
portRanges = append(portRanges, portRange)
224
network.SortPortRanges(portRanges)
225
for _, portRange := range portRanges {
226
unitName := portRangesToUnits[portRange]
227
resultPorts = append(resultPorts, params.MachinePortRange{
228
UnitTag: names.NewUnitTag(unitName).String(),
229
PortRange: params.FromNetworkPortRange(portRange),
233
return params.MachinePortsResult{
238
// PublicAddress returns the public address for each given unit, if set.
239
func (u *UniterAPIV3) PublicAddress(args params.Entities) (params.StringResults, error) {
240
result := params.StringResults{
241
Results: make([]params.StringResult, len(args.Entities)),
243
canAccess, err := u.accessUnit()
245
return params.StringResults{}, err
247
for i, entity := range args.Entities {
248
tag, err := names.ParseUnitTag(entity.Tag)
250
result.Results[i].Error = common.ServerError(common.ErrPerm)
256
unit, err = u.getUnit(tag)
258
var address network.Address
259
address, err = unit.PublicAddress()
261
result.Results[i].Result = address.Value
262
} else if network.IsNoAddressError(err) {
263
err = common.NoAddressSetError(tag, "public")
267
result.Results[i].Error = common.ServerError(err)
272
// PrivateAddress returns the private address for each given unit, if set.
273
func (u *UniterAPIV3) PrivateAddress(args params.Entities) (params.StringResults, error) {
274
result := params.StringResults{
275
Results: make([]params.StringResult, len(args.Entities)),
277
canAccess, err := u.accessUnit()
279
return params.StringResults{}, err
281
for i, entity := range args.Entities {
282
tag, err := names.ParseUnitTag(entity.Tag)
284
result.Results[i].Error = common.ServerError(common.ErrPerm)
290
unit, err = u.getUnit(tag)
292
var address network.Address
293
address, err = unit.PrivateAddress()
295
result.Results[i].Result = address.Value
296
} else if network.IsNoAddressError(err) {
297
err = common.NoAddressSetError(tag, "private")
301
result.Results[i].Error = common.ServerError(err)
306
// TODO(ericsnow) Factor out the common code amongst the many methods here.
308
var getZone = func(st *state.State, tag names.Tag) (string, error) {
309
unit, err := st.Unit(tag.Id())
311
return "", errors.Trace(err)
313
zone, err := unit.AvailabilityZone()
314
return zone, errors.Trace(err)
317
// AvailabilityZone returns the availability zone for each given unit, if applicable.
318
func (u *UniterAPIV3) AvailabilityZone(args params.Entities) (params.StringResults, error) {
319
var results params.StringResults
321
canAccess, err := u.accessUnit()
323
return results, errors.Trace(err)
327
results = params.StringResults{
328
Results: make([]params.StringResult, len(args.Entities)),
331
// Collect the zones. No zone will be collected for any entity where
332
// the tag is invalid or not authorized. Instead the corresponding
333
// result will be updated with the error.
334
for i, entity := range args.Entities {
335
tag, err := names.ParseUnitTag(entity.Tag)
337
results.Results[i].Error = common.ServerError(common.ErrPerm)
343
zone, err = getZone(u.st, tag)
345
results.Results[i].Result = zone
348
results.Results[i].Error = common.ServerError(err)
354
// Resolved returns the current resolved setting for each given unit.
355
func (u *UniterAPIV3) Resolved(args params.Entities) (params.ResolvedModeResults, error) {
356
result := params.ResolvedModeResults{
357
Results: make([]params.ResolvedModeResult, len(args.Entities)),
359
canAccess, err := u.accessUnit()
361
return params.ResolvedModeResults{}, err
363
for i, entity := range args.Entities {
364
tag, err := names.ParseUnitTag(entity.Tag)
366
result.Results[i].Error = common.ServerError(common.ErrPerm)
372
unit, err = u.getUnit(tag)
374
result.Results[i].Mode = params.ResolvedMode(unit.Resolved())
377
result.Results[i].Error = common.ServerError(err)
382
// ClearResolved removes any resolved setting from each given unit.
383
func (u *UniterAPIV3) ClearResolved(args params.Entities) (params.ErrorResults, error) {
384
result := params.ErrorResults{
385
Results: make([]params.ErrorResult, len(args.Entities)),
387
canAccess, err := u.accessUnit()
389
return params.ErrorResults{}, err
391
for i, entity := range args.Entities {
392
tag, err := names.ParseUnitTag(entity.Tag)
394
result.Results[i].Error = common.ServerError(common.ErrPerm)
400
unit, err = u.getUnit(tag)
402
err = unit.ClearResolved()
405
result.Results[i].Error = common.ServerError(err)
410
// GetPrincipal returns the result of calling PrincipalName() and
411
// converting it to a tag, on each given unit.
412
func (u *UniterAPIV3) GetPrincipal(args params.Entities) (params.StringBoolResults, error) {
413
result := params.StringBoolResults{
414
Results: make([]params.StringBoolResult, len(args.Entities)),
416
canAccess, err := u.accessUnit()
418
return params.StringBoolResults{}, err
420
for i, entity := range args.Entities {
421
tag, err := names.ParseUnitTag(entity.Tag)
423
result.Results[i].Error = common.ServerError(common.ErrPerm)
429
unit, err = u.getUnit(tag)
431
principal, ok := unit.PrincipalName()
433
result.Results[i].Result = names.NewUnitTag(principal).String()
435
result.Results[i].Ok = ok
438
result.Results[i].Error = common.ServerError(err)
443
// Destroy advances all given Alive units' lifecycles as far as
444
// possible. See state/Unit.Destroy().
445
func (u *UniterAPIV3) Destroy(args params.Entities) (params.ErrorResults, error) {
446
result := params.ErrorResults{
447
Results: make([]params.ErrorResult, len(args.Entities)),
449
canAccess, err := u.accessUnit()
451
return params.ErrorResults{}, err
453
for i, entity := range args.Entities {
454
tag, err := names.ParseUnitTag(entity.Tag)
456
result.Results[i].Error = common.ServerError(common.ErrPerm)
462
unit, err = u.getUnit(tag)
467
result.Results[i].Error = common.ServerError(err)
472
// DestroyAllSubordinates destroys all subordinates of each given unit.
473
func (u *UniterAPIV3) DestroyAllSubordinates(args params.Entities) (params.ErrorResults, error) {
474
result := params.ErrorResults{
475
Results: make([]params.ErrorResult, len(args.Entities)),
477
canAccess, err := u.accessUnit()
479
return params.ErrorResults{}, err
481
for i, entity := range args.Entities {
482
tag, err := names.ParseUnitTag(entity.Tag)
484
result.Results[i].Error = common.ServerError(common.ErrPerm)
490
unit, err = u.getUnit(tag)
492
err = u.destroySubordinates(unit)
495
result.Results[i].Error = common.ServerError(err)
500
// HasSubordinates returns the whether each given unit has any subordinates.
501
func (u *UniterAPIV3) HasSubordinates(args params.Entities) (params.BoolResults, error) {
502
result := params.BoolResults{
503
Results: make([]params.BoolResult, len(args.Entities)),
505
canAccess, err := u.accessUnit()
507
return params.BoolResults{}, err
509
for i, entity := range args.Entities {
510
tag, err := names.ParseUnitTag(entity.Tag)
512
result.Results[i].Error = common.ServerError(common.ErrPerm)
518
unit, err = u.getUnit(tag)
520
subordinates := unit.SubordinateNames()
521
result.Results[i].Result = len(subordinates) > 0
524
result.Results[i].Error = common.ServerError(err)
529
// CharmModifiedVersion returns the most CharmModifiedVersion for all given
530
// units or services.
531
func (u *UniterAPIV3) CharmModifiedVersion(args params.Entities) (params.IntResults, error) {
532
results := params.IntResults{
533
Results: make([]params.IntResult, len(args.Entities)),
536
accessUnitOrService := common.AuthEither(u.accessUnit, u.accessService)
537
canAccess, err := accessUnitOrService()
541
for i, entity := range args.Entities {
542
ver, err := u.charmModifiedVersion(entity.Tag, canAccess)
544
results.Results[i].Error = common.ServerError(err)
547
results.Results[i].Result = ver
552
func (u *UniterAPIV3) charmModifiedVersion(tagStr string, canAccess func(names.Tag) bool) (int, error) {
553
tag, err := names.ParseTag(tagStr)
555
return -1, common.ErrPerm
558
return -1, common.ErrPerm
560
unitOrService, err := u.st.FindEntity(tag)
564
var service *state.Application
565
switch entity := unitOrService.(type) {
566
case *state.Application:
569
service, err = entity.Application()
574
return -1, errors.BadRequestf("type %t does not have a CharmModifiedVersion", entity)
576
return service.CharmModifiedVersion(), nil
579
// CharmURL returns the charm URL for all given units or services.
580
func (u *UniterAPIV3) CharmURL(args params.Entities) (params.StringBoolResults, error) {
581
result := params.StringBoolResults{
582
Results: make([]params.StringBoolResult, len(args.Entities)),
584
accessUnitOrService := common.AuthEither(u.accessUnit, u.accessService)
585
canAccess, err := accessUnitOrService()
587
return params.StringBoolResults{}, err
589
for i, entity := range args.Entities {
590
tag, err := names.ParseTag(entity.Tag)
592
result.Results[i].Error = common.ServerError(common.ErrPerm)
597
var unitOrService state.Entity
598
unitOrService, err = u.st.FindEntity(tag)
600
charmURLer := unitOrService.(interface {
601
CharmURL() (*charm.URL, bool)
603
curl, ok := charmURLer.CharmURL()
605
result.Results[i].Result = curl.String()
606
result.Results[i].Ok = ok
610
result.Results[i].Error = common.ServerError(err)
615
// SetCharmURL sets the charm URL for each given unit. An error will
616
// be returned if a unit is dead, or the charm URL is not know.
617
func (u *UniterAPIV3) SetCharmURL(args params.EntitiesCharmURL) (params.ErrorResults, error) {
618
result := params.ErrorResults{
619
Results: make([]params.ErrorResult, len(args.Entities)),
621
canAccess, err := u.accessUnit()
623
return params.ErrorResults{}, err
625
for i, entity := range args.Entities {
626
tag, err := names.ParseUnitTag(entity.Tag)
628
result.Results[i].Error = common.ServerError(common.ErrPerm)
634
unit, err = u.getUnit(tag)
637
curl, err = charm.ParseURL(entity.CharmURL)
639
err = unit.SetCharmURL(curl)
643
result.Results[i].Error = common.ServerError(err)
648
// WorkloadVersion returns the workload version for all given units or services.
649
func (u *UniterAPIV3) WorkloadVersion(args params.Entities) (params.StringResults, error) {
650
result := params.StringResults{
651
Results: make([]params.StringResult, len(args.Entities)),
653
canAccess, err := u.accessUnit()
655
return params.StringResults{}, err
657
for i, entity := range args.Entities {
658
resultItem := &result.Results[i]
659
tag, err := names.ParseUnitTag(entity.Tag)
661
resultItem.Error = common.ServerError(err)
665
resultItem.Error = common.ServerError(common.ErrPerm)
668
unit, err := u.getUnit(tag)
670
resultItem.Error = common.ServerError(err)
673
version, err := unit.WorkloadVersion()
675
resultItem.Error = common.ServerError(err)
678
resultItem.Result = version
683
// SetWorkloadVersion sets the workload version for each given unit. An error will
684
// be returned if a unit is dead.
685
func (u *UniterAPIV3) SetWorkloadVersion(args params.EntityWorkloadVersions) (params.ErrorResults, error) {
686
result := params.ErrorResults{
687
Results: make([]params.ErrorResult, len(args.Entities)),
689
canAccess, err := u.accessUnit()
691
return params.ErrorResults{}, err
693
for i, entity := range args.Entities {
694
resultItem := &result.Results[i]
695
tag, err := names.ParseUnitTag(entity.Tag)
697
resultItem.Error = common.ServerError(err)
701
resultItem.Error = common.ServerError(common.ErrPerm)
704
unit, err := u.getUnit(tag)
706
resultItem.Error = common.ServerError(err)
709
err = unit.SetWorkloadVersion(entity.WorkloadVersion)
711
resultItem.Error = common.ServerError(err)
717
// OpenPorts sets the policy of the port range with protocol to be
718
// opened, for all given units.
719
func (u *UniterAPIV3) OpenPorts(args params.EntitiesPortRanges) (params.ErrorResults, error) {
720
result := params.ErrorResults{
721
Results: make([]params.ErrorResult, len(args.Entities)),
723
canAccess, err := u.accessUnit()
725
return params.ErrorResults{}, err
727
for i, entity := range args.Entities {
728
tag, err := names.ParseUnitTag(entity.Tag)
730
result.Results[i].Error = common.ServerError(common.ErrPerm)
736
unit, err = u.getUnit(tag)
738
err = unit.OpenPorts(entity.Protocol, entity.FromPort, entity.ToPort)
741
result.Results[i].Error = common.ServerError(err)
746
// ClosePorts sets the policy of the port range with protocol to be
747
// closed, for all given units.
748
func (u *UniterAPIV3) ClosePorts(args params.EntitiesPortRanges) (params.ErrorResults, error) {
749
result := params.ErrorResults{
750
Results: make([]params.ErrorResult, len(args.Entities)),
752
canAccess, err := u.accessUnit()
754
return params.ErrorResults{}, err
756
for i, entity := range args.Entities {
757
tag, err := names.ParseUnitTag(entity.Tag)
759
result.Results[i].Error = common.ServerError(common.ErrPerm)
765
unit, err = u.getUnit(tag)
767
err = unit.ClosePorts(entity.Protocol, entity.FromPort, entity.ToPort)
770
result.Results[i].Error = common.ServerError(err)
775
// WatchConfigSettings returns a NotifyWatcher for observing changes
776
// to each unit's service configuration settings. See also
777
// state/watcher.go:Unit.WatchConfigSettings().
778
func (u *UniterAPIV3) WatchConfigSettings(args params.Entities) (params.NotifyWatchResults, error) {
779
result := params.NotifyWatchResults{
780
Results: make([]params.NotifyWatchResult, len(args.Entities)),
782
canAccess, err := u.accessUnit()
784
return params.NotifyWatchResults{}, err
786
for i, entity := range args.Entities {
787
tag, err := names.ParseUnitTag(entity.Tag)
789
result.Results[i].Error = common.ServerError(common.ErrPerm)
795
watcherId, err = u.watchOneUnitConfigSettings(tag)
797
result.Results[i].NotifyWatcherId = watcherId
798
result.Results[i].Error = common.ServerError(err)
803
// WatchActionNotifications returns a StringsWatcher for observing
804
// incoming action calls to a unit. See also state/watcher.go
805
// Unit.WatchActionNotifications(). This method is called from
806
// api/uniter/uniter.go WatchActionNotifications().
807
func (u *UniterAPIV3) WatchActionNotifications(args params.Entities) (params.StringsWatchResults, error) {
808
tagToActionReceiver := common.TagToActionReceiverFn(u.st.FindEntity)
809
watchOne := common.WatchOneActionReceiverNotifications(tagToActionReceiver, u.resources.Register)
810
canAccess, err := u.accessUnit()
812
return params.StringsWatchResults{}, err
814
return common.WatchActionNotifications(args, canAccess, watchOne), nil
817
// ConfigSettings returns the complete set of service charm config
818
// settings available to each given unit.
819
func (u *UniterAPIV3) ConfigSettings(args params.Entities) (params.ConfigSettingsResults, error) {
820
result := params.ConfigSettingsResults{
821
Results: make([]params.ConfigSettingsResult, len(args.Entities)),
823
canAccess, err := u.accessUnit()
825
return params.ConfigSettingsResults{}, err
827
for i, entity := range args.Entities {
828
tag, err := names.ParseUnitTag(entity.Tag)
830
result.Results[i].Error = common.ServerError(common.ErrPerm)
836
unit, err = u.getUnit(tag)
838
var settings charm.Settings
839
settings, err = unit.ConfigSettings()
841
result.Results[i].Settings = params.ConfigSettings(settings)
845
result.Results[i].Error = common.ServerError(err)
850
// WatchApplicationRelations returns a StringsWatcher, for each given
851
// service, that notifies of changes to the lifecycles of relations
852
// involving that service.
853
func (u *UniterAPIV3) WatchApplicationRelations(args params.Entities) (params.StringsWatchResults, error) {
854
result := params.StringsWatchResults{
855
Results: make([]params.StringsWatchResult, len(args.Entities)),
857
canAccess, err := u.accessService()
859
return params.StringsWatchResults{}, err
861
for i, entity := range args.Entities {
862
tag, err := names.ParseApplicationTag(entity.Tag)
864
result.Results[i].Error = common.ServerError(common.ErrPerm)
869
result.Results[i], err = u.watchOneServiceRelations(tag)
871
result.Results[i].Error = common.ServerError(err)
876
// CharmArchiveSha256 returns the SHA256 digest of the charm archive
877
// (bundle) data for each charm url in the given parameters.
878
func (u *UniterAPIV3) CharmArchiveSha256(args params.CharmURLs) (params.StringResults, error) {
879
result := params.StringResults{
880
Results: make([]params.StringResult, len(args.URLs)),
882
for i, arg := range args.URLs {
883
curl, err := charm.ParseURL(arg.URL)
888
sch, err = u.st.Charm(curl)
889
if errors.IsNotFound(err) {
893
result.Results[i].Result = sch.BundleSha256()
896
result.Results[i].Error = common.ServerError(err)
901
// Relation returns information about all given relation/unit pairs,
902
// including their id, key and the local endpoint.
903
func (u *UniterAPIV3) Relation(args params.RelationUnits) (params.RelationResults, error) {
904
result := params.RelationResults{
905
Results: make([]params.RelationResult, len(args.RelationUnits)),
907
canAccess, err := u.accessUnit()
909
return params.RelationResults{}, err
911
for i, rel := range args.RelationUnits {
912
relParams, err := u.getOneRelation(canAccess, rel.Relation, rel.Unit)
914
result.Results[i] = relParams
916
result.Results[i].Error = common.ServerError(err)
921
// Actions returns the Actions by Tags passed and ensures that the Unit asking
922
// for them is the same Unit that has the Actions.
923
func (u *UniterAPIV3) Actions(args params.Entities) (params.ActionResults, error) {
924
canAccess, err := u.accessUnit()
926
return params.ActionResults{}, err
929
actionFn := common.AuthAndActionFromTagFn(canAccess, u.st.ActionByTag)
930
return common.Actions(args, actionFn), nil
933
// BeginActions marks the actions represented by the passed in Tags as running.
934
func (u *UniterAPIV3) BeginActions(args params.Entities) (params.ErrorResults, error) {
935
canAccess, err := u.accessUnit()
937
return params.ErrorResults{}, err
940
actionFn := common.AuthAndActionFromTagFn(canAccess, u.st.ActionByTag)
941
return common.BeginActions(args, actionFn), nil
944
// FinishActions saves the result of a completed Action
945
func (u *UniterAPIV3) FinishActions(args params.ActionExecutionResults) (params.ErrorResults, error) {
946
canAccess, err := u.accessUnit()
948
return params.ErrorResults{}, err
951
actionFn := common.AuthAndActionFromTagFn(canAccess, u.st.ActionByTag)
952
return common.FinishActions(args, actionFn), nil
955
// RelationById returns information about all given relations,
956
// specified by their ids, including their key and the local
958
func (u *UniterAPIV3) RelationById(args params.RelationIds) (params.RelationResults, error) {
959
result := params.RelationResults{
960
Results: make([]params.RelationResult, len(args.RelationIds)),
962
for i, relId := range args.RelationIds {
963
relParams, err := u.getOneRelationById(relId)
965
result.Results[i] = relParams
967
result.Results[i].Error = common.ServerError(err)
972
// JoinedRelations returns the tags of all relations for which each supplied unit
973
// has entered scope. It should be called RelationsInScope, but it's not convenient
974
// to make that change until we have versioned APIs.
975
func (u *UniterAPIV3) JoinedRelations(args params.Entities) (params.StringsResults, error) {
976
result := params.StringsResults{
977
Results: make([]params.StringsResult, len(args.Entities)),
979
if len(args.Entities) == 0 {
982
canRead, err := u.accessUnit()
984
return params.StringsResults{}, err
986
for i, entity := range args.Entities {
987
tag, err := names.ParseUnitTag(entity.Tag)
989
result.Results[i].Error = common.ServerError(common.ErrPerm)
995
unit, err = u.getUnit(tag)
997
result.Results[i].Result, err = relationsInScopeTags(unit)
1000
result.Results[i].Error = common.ServerError(err)
1005
// CurrentModel returns the name and UUID for the current juju model.
1006
func (u *UniterAPIV3) CurrentModel() (params.ModelResult, error) {
1007
result := params.ModelResult{}
1008
env, err := u.st.Model()
1010
result.Name = env.Name()
1011
result.UUID = env.UUID()
1016
// ProviderType returns the provider type used by the current juju
1019
// TODO(dimitern): Refactor the uniter to call this instead of calling
1020
// ModelConfig() just to get the provider type. Once we have machine
1021
// addresses, this might be completely unnecessary though.
1022
func (u *UniterAPIV3) ProviderType() (params.StringResult, error) {
1023
result := params.StringResult{}
1024
cfg, err := u.st.ModelConfig()
1026
result.Result = cfg.Type()
1031
// EnterScope ensures each unit has entered its scope in the relation,
1032
// for all of the given relation/unit pairs. See also
1033
// state.RelationUnit.EnterScope().
1034
func (u *UniterAPIV3) EnterScope(args params.RelationUnits) (params.ErrorResults, error) {
1035
result := params.ErrorResults{
1036
Results: make([]params.ErrorResult, len(args.RelationUnits)),
1038
canAccess, err := u.accessUnit()
1040
return params.ErrorResults{}, err
1042
for i, arg := range args.RelationUnits {
1043
tag, err := names.ParseUnitTag(arg.Unit)
1045
result.Results[i].Error = common.ServerError(common.ErrPerm)
1048
relUnit, err := u.getRelationUnit(canAccess, arg.Relation, tag)
1050
// Construct the settings, passing the unit's
1051
// private address (we already know it).
1052
privateAddress, _ := relUnit.PrivateAddress()
1053
settings := map[string]interface{}{
1054
"private-address": privateAddress.Value,
1056
err = relUnit.EnterScope(settings)
1058
result.Results[i].Error = common.ServerError(err)
1063
// LeaveScope signals each unit has left its scope in the relation,
1064
// for all of the given relation/unit pairs. See also
1065
// state.RelationUnit.LeaveScope().
1066
func (u *UniterAPIV3) LeaveScope(args params.RelationUnits) (params.ErrorResults, error) {
1067
result := params.ErrorResults{
1068
Results: make([]params.ErrorResult, len(args.RelationUnits)),
1070
canAccess, err := u.accessUnit()
1072
return params.ErrorResults{}, err
1074
for i, arg := range args.RelationUnits {
1075
unit, err := names.ParseUnitTag(arg.Unit)
1077
result.Results[i].Error = common.ServerError(common.ErrPerm)
1080
relUnit, err := u.getRelationUnit(canAccess, arg.Relation, unit)
1082
err = relUnit.LeaveScope()
1084
result.Results[i].Error = common.ServerError(err)
1089
// ReadSettings returns the local settings of each given set of
1091
func (u *UniterAPIV3) ReadSettings(args params.RelationUnits) (params.SettingsResults, error) {
1092
result := params.SettingsResults{
1093
Results: make([]params.SettingsResult, len(args.RelationUnits)),
1095
canAccess, err := u.accessUnit()
1097
return params.SettingsResults{}, err
1099
for i, arg := range args.RelationUnits {
1100
unit, err := names.ParseUnitTag(arg.Unit)
1102
result.Results[i].Error = common.ServerError(common.ErrPerm)
1105
relUnit, err := u.getRelationUnit(canAccess, arg.Relation, unit)
1107
var settings *state.Settings
1108
settings, err = relUnit.Settings()
1110
result.Results[i].Settings, err = convertRelationSettings(settings.Map())
1113
result.Results[i].Error = common.ServerError(err)
1118
// ReadRemoteSettings returns the remote settings of each given set of
1119
// relation/local unit/remote unit.
1120
func (u *UniterAPIV3) ReadRemoteSettings(args params.RelationUnitPairs) (params.SettingsResults, error) {
1121
result := params.SettingsResults{
1122
Results: make([]params.SettingsResult, len(args.RelationUnitPairs)),
1124
canAccess, err := u.accessUnit()
1126
return params.SettingsResults{}, err
1128
for i, arg := range args.RelationUnitPairs {
1129
unit, err := names.ParseUnitTag(arg.LocalUnit)
1131
result.Results[i].Error = common.ServerError(common.ErrPerm)
1134
relUnit, err := u.getRelationUnit(canAccess, arg.Relation, unit)
1136
// TODO(dfc) rework this logic
1138
remoteUnit, err = u.checkRemoteUnit(relUnit, arg.RemoteUnit)
1140
var settings map[string]interface{}
1141
settings, err = relUnit.ReadSettings(remoteUnit)
1143
result.Results[i].Settings, err = convertRelationSettings(settings)
1147
result.Results[i].Error = common.ServerError(err)
1152
// UpdateSettings persists all changes made to the local settings of
1153
// all given pairs of relation and unit. Keys with empty values are
1154
// considered a signal to delete these values.
1155
func (u *UniterAPIV3) UpdateSettings(args params.RelationUnitsSettings) (params.ErrorResults, error) {
1156
result := params.ErrorResults{
1157
Results: make([]params.ErrorResult, len(args.RelationUnits)),
1159
canAccess, err := u.accessUnit()
1161
return params.ErrorResults{}, err
1163
for i, arg := range args.RelationUnits {
1164
unit, err := names.ParseUnitTag(arg.Unit)
1166
result.Results[i].Error = common.ServerError(common.ErrPerm)
1169
relUnit, err := u.getRelationUnit(canAccess, arg.Relation, unit)
1171
var settings *state.Settings
1172
settings, err = relUnit.Settings()
1174
for k, v := range arg.Settings {
1181
_, err = settings.Write()
1184
result.Results[i].Error = common.ServerError(err)
1189
// WatchRelationUnits returns a RelationUnitsWatcher for observing
1190
// changes to every unit in the supplied relation that is visible to
1191
// the supplied unit. See also state/watcher.go:RelationUnit.Watch().
1192
func (u *UniterAPIV3) WatchRelationUnits(args params.RelationUnits) (params.RelationUnitsWatchResults, error) {
1193
result := params.RelationUnitsWatchResults{
1194
Results: make([]params.RelationUnitsWatchResult, len(args.RelationUnits)),
1196
canAccess, err := u.accessUnit()
1198
return params.RelationUnitsWatchResults{}, err
1200
for i, arg := range args.RelationUnits {
1201
unit, err := names.ParseUnitTag(arg.Unit)
1203
result.Results[i].Error = common.ServerError(common.ErrPerm)
1206
relUnit, err := u.getRelationUnit(canAccess, arg.Relation, unit)
1208
result.Results[i], err = u.watchOneRelationUnit(relUnit)
1210
result.Results[i].Error = common.ServerError(err)
1215
// WatchUnitAddresses returns a NotifyWatcher for observing changes
1216
// to each unit's addresses.
1217
func (u *UniterAPIV3) WatchUnitAddresses(args params.Entities) (params.NotifyWatchResults, error) {
1218
result := params.NotifyWatchResults{
1219
Results: make([]params.NotifyWatchResult, len(args.Entities)),
1221
canAccess, err := u.accessUnit()
1223
return params.NotifyWatchResults{}, err
1225
for i, entity := range args.Entities {
1226
unit, err := names.ParseUnitTag(entity.Tag)
1228
result.Results[i].Error = common.ServerError(common.ErrPerm)
1231
err = common.ErrPerm
1233
if canAccess(unit) {
1234
watcherId, err = u.watchOneUnitAddresses(unit)
1236
result.Results[i].NotifyWatcherId = watcherId
1237
result.Results[i].Error = common.ServerError(err)
1242
func (u *UniterAPIV3) getUnit(tag names.UnitTag) (*state.Unit, error) {
1243
return u.st.Unit(tag.Id())
1246
func (u *UniterAPIV3) getService(tag names.ApplicationTag) (*state.Application, error) {
1247
return u.st.Application(tag.Id())
1250
func (u *UniterAPIV3) getRelationUnit(canAccess common.AuthFunc, relTag string, unitTag names.UnitTag) (*state.RelationUnit, error) {
1251
rel, unit, err := u.getRelationAndUnit(canAccess, relTag, unitTag)
1255
return rel.Unit(unit)
1258
func (u *UniterAPIV3) getOneRelationById(relId int) (params.RelationResult, error) {
1259
nothing := params.RelationResult{}
1260
rel, err := u.st.Relation(relId)
1261
if errors.IsNotFound(err) {
1262
return nothing, common.ErrPerm
1263
} else if err != nil {
1266
tag := u.auth.GetAuthTag()
1271
panic("authenticated entity is not a unit")
1273
unit, err := u.st.FindEntity(tag)
1277
// Use the currently authenticated unit to get the endpoint.
1278
result, err := u.prepareRelationResult(rel, unit.(*state.Unit))
1280
// An error from prepareRelationResult means the authenticated
1281
// unit's service is not part of the requested
1282
// relation. That's why it's appropriate to return ErrPerm
1284
return nothing, common.ErrPerm
1289
func (u *UniterAPIV3) getRelationAndUnit(canAccess common.AuthFunc, relTag string, unitTag names.UnitTag) (*state.Relation, *state.Unit, error) {
1290
tag, err := names.ParseRelationTag(relTag)
1292
return nil, nil, common.ErrPerm
1294
rel, err := u.st.KeyRelation(tag.Id())
1295
if errors.IsNotFound(err) {
1296
return nil, nil, common.ErrPerm
1297
} else if err != nil {
1298
return nil, nil, err
1300
if !canAccess(unitTag) {
1301
return nil, nil, common.ErrPerm
1303
unit, err := u.getUnit(unitTag)
1304
return rel, unit, err
1307
func (u *UniterAPIV3) prepareRelationResult(rel *state.Relation, unit *state.Unit) (params.RelationResult, error) {
1308
nothing := params.RelationResult{}
1309
ep, err := rel.Endpoint(unit.ApplicationName())
1311
// An error here means the unit's service is not part of the
1315
return params.RelationResult{
1318
Life: params.Life(rel.Life().String()),
1319
Endpoint: multiwatcher.Endpoint{
1320
ApplicationName: ep.ApplicationName,
1321
Relation: multiwatcher.NewCharmRelation(ep.Relation),
1326
func (u *UniterAPIV3) getOneRelation(canAccess common.AuthFunc, relTag, unitTag string) (params.RelationResult, error) {
1327
nothing := params.RelationResult{}
1328
tag, err := names.ParseUnitTag(unitTag)
1330
return nothing, common.ErrPerm
1332
rel, unit, err := u.getRelationAndUnit(canAccess, relTag, tag)
1336
return u.prepareRelationResult(rel, unit)
1339
func (u *UniterAPIV3) destroySubordinates(principal *state.Unit) error {
1340
subordinates := principal.SubordinateNames()
1341
for _, subName := range subordinates {
1342
unit, err := u.getUnit(names.NewUnitTag(subName))
1346
if err = unit.Destroy(); err != nil {
1353
func (u *UniterAPIV3) watchOneServiceRelations(tag names.ApplicationTag) (params.StringsWatchResult, error) {
1354
nothing := params.StringsWatchResult{}
1355
service, err := u.getService(tag)
1359
watch := service.WatchRelations()
1360
// Consume the initial event and forward it to the result.
1361
if changes, ok := <-watch.Changes(); ok {
1362
return params.StringsWatchResult{
1363
StringsWatcherId: u.resources.Register(watch),
1367
return nothing, watcher.EnsureErr(watch)
1370
func (u *UniterAPIV3) watchOneUnitConfigSettings(tag names.UnitTag) (string, error) {
1371
unit, err := u.getUnit(tag)
1375
watch, err := unit.WatchConfigSettings()
1379
// Consume the initial event. Technically, API
1380
// calls to Watch 'transmit' the initial event
1381
// in the Watch response. But NotifyWatchers
1382
// have no state to transmit.
1383
if _, ok := <-watch.Changes(); ok {
1384
return u.resources.Register(watch), nil
1386
return "", watcher.EnsureErr(watch)
1389
func (u *UniterAPIV3) watchOneUnitAddresses(tag names.UnitTag) (string, error) {
1390
unit, err := u.getUnit(tag)
1394
machineId, err := unit.AssignedMachineId()
1398
machine, err := u.st.Machine(machineId)
1402
watch := machine.WatchAddresses()
1403
// Consume the initial event. Technically, API
1404
// calls to Watch 'transmit' the initial event
1405
// in the Watch response. But NotifyWatchers
1406
// have no state to transmit.
1407
if _, ok := <-watch.Changes(); ok {
1408
return u.resources.Register(watch), nil
1410
return "", watcher.EnsureErr(watch)
1413
func (u *UniterAPIV3) watchOneRelationUnit(relUnit *state.RelationUnit) (params.RelationUnitsWatchResult, error) {
1414
watch := relUnit.Watch()
1415
// Consume the initial event and forward it to the result.
1416
if changes, ok := <-watch.Changes(); ok {
1417
return params.RelationUnitsWatchResult{
1418
RelationUnitsWatcherId: u.resources.Register(watch),
1422
return params.RelationUnitsWatchResult{}, watcher.EnsureErr(watch)
1425
func (u *UniterAPIV3) checkRemoteUnit(relUnit *state.RelationUnit, remoteUnitTag string) (string, error) {
1426
// Make sure the unit is indeed remote.
1427
if remoteUnitTag == u.auth.GetAuthTag().String() {
1428
return "", common.ErrPerm
1430
// Check remoteUnit is indeed related. Note that we don't want to actually get
1431
// the *Unit, because it might have been removed; but its relation settings will
1432
// persist until the relation itself has been removed (and must remain accessible
1433
// because the local unit's view of reality may be time-shifted).
1434
tag, err := names.ParseUnitTag(remoteUnitTag)
1436
return "", common.ErrPerm
1438
remoteUnitName := tag.Id()
1439
remoteServiceName, err := names.UnitApplication(remoteUnitName)
1441
return "", common.ErrPerm
1443
rel := relUnit.Relation()
1444
_, err = rel.RelatedEndpoints(remoteServiceName)
1446
return "", common.ErrPerm
1448
return remoteUnitName, nil
1451
func convertRelationSettings(settings map[string]interface{}) (params.Settings, error) {
1452
result := make(params.Settings)
1453
for k, v := range settings {
1454
// All relation settings should be strings.
1455
sval, ok := v.(string)
1457
return nil, fmt.Errorf("unexpected relation setting %q: expected string, got %T", k, v)
1464
func relationsInScopeTags(unit *state.Unit) ([]string, error) {
1465
relations, err := unit.RelationsInScope()
1469
tags := make([]string, len(relations))
1470
for i, relation := range relations {
1471
tags[i] = relation.Tag().String()
1476
func leadershipSettingsAccessorFactory(
1478
resources facade.Resources,
1479
auth facade.Authorizer,
1480
) *leadershipapiserver.LeadershipSettingsAccessor {
1481
registerWatcher := func(serviceId string) (string, error) {
1482
service, err := st.Application(serviceId)
1486
w := service.WatchLeaderSettings()
1487
if _, ok := <-w.Changes(); ok {
1488
return resources.Register(w), nil
1490
return "", watcher.EnsureErr(w)
1492
getSettings := func(serviceId string) (map[string]string, error) {
1493
service, err := st.Application(serviceId)
1497
return service.LeaderSettings()
1499
writeSettings := func(token leadership.Token, serviceId string, settings map[string]string) error {
1500
service, err := st.Application(serviceId)
1504
return service.UpdateLeaderSettings(token, settings)
1506
return leadershipapiserver.NewLeadershipSettingsAccessor(
1510
st.LeadershipChecker().LeadershipCheck,
1515
// AddMetricBatches adds the metrics for the specified unit.
1516
func (u *UniterAPIV3) AddMetricBatches(args params.MetricBatchParams) (params.ErrorResults, error) {
1517
result := params.ErrorResults{
1518
Results: make([]params.ErrorResult, len(args.Batches)),
1520
canAccess, err := u.accessUnit()
1522
logger.Warningf("failed to check unit access: %v", err)
1523
return params.ErrorResults{}, common.ErrPerm
1525
for i, batch := range args.Batches {
1526
tag, err := names.ParseUnitTag(batch.Tag)
1528
result.Results[i].Error = common.ServerError(err)
1531
if !canAccess(tag) {
1532
result.Results[i].Error = common.ServerError(common.ErrPerm)
1535
metrics := make([]state.Metric, len(batch.Batch.Metrics))
1536
for j, metric := range batch.Batch.Metrics {
1537
metrics[j] = state.Metric{
1539
Value: metric.Value,
1543
_, err = u.st.AddMetrics(state.BatchParam{
1544
UUID: batch.Batch.UUID,
1545
Created: batch.Batch.Created,
1546
CharmURL: batch.Batch.CharmURL,
1550
result.Results[i].Error = common.ServerError(err)
1555
// NetworkConfig returns information about all given relation/unit pairs,
1556
// including their id, key and the local endpoint.
1557
func (u *UniterAPIV3) NetworkConfig(args params.UnitsNetworkConfig) (params.UnitNetworkConfigResults, error) {
1558
result := params.UnitNetworkConfigResults{
1559
Results: make([]params.UnitNetworkConfigResult, len(args.Args)),
1562
canAccess, err := u.accessUnit()
1564
return params.UnitNetworkConfigResults{}, err
1567
for i, arg := range args.Args {
1568
netConfig, err := u.getOneNetworkConfig(canAccess, arg.UnitTag, arg.BindingName)
1570
result.Results[i].Config = netConfig
1572
result.Results[i].Error = common.ServerError(err)
1578
func (u *UniterAPIV3) getOneNetworkConfig(canAccess common.AuthFunc, unitTagArg, bindingName string) ([]params.NetworkConfig, error) {
1579
unitTag, err := names.ParseUnitTag(unitTagArg)
1581
return nil, errors.Trace(err)
1584
if bindingName == "" {
1585
return nil, errors.Errorf("binding name cannot be empty")
1588
if !canAccess(unitTag) {
1589
return nil, common.ErrPerm
1592
unit, err := u.getUnit(unitTag)
1594
return nil, errors.Trace(err)
1597
service, err := unit.Application()
1599
return nil, errors.Trace(err)
1602
bindings, err := service.EndpointBindings()
1604
return nil, errors.Trace(err)
1606
boundSpace, known := bindings[bindingName]
1608
return nil, errors.Errorf("binding name %q not defined by the unit's charm", bindingName)
1611
machineID, err := unit.AssignedMachineId()
1613
return nil, errors.Trace(err)
1616
machine, err := u.st.Machine(machineID)
1618
return nil, errors.Trace(err)
1621
var results []params.NetworkConfig
1622
if boundSpace == "" {
1624
"endpoint %q not explicitly bound to a space, using preferred private address for machine %q",
1625
bindingName, machineID,
1628
privateAddress, err := machine.PrivateAddress()
1630
return nil, errors.Annotatef(err, "getting machine %q preferred private address", machineID)
1633
results = append(results, params.NetworkConfig{
1634
Address: privateAddress.Value,
1638
logger.Debugf("endpoint %q is explicitly bound to space %q", bindingName, boundSpace)
1641
// TODO(dimitern): Use NetworkInterfaces() instead later, this is just for
1642
// the PoC to enable minimal network-get implementation returning just the
1645
// LKK Card: https://canonical.leankit.com/Boards/View/101652562/119258804
1646
addresses, err := machine.AllAddresses()
1648
return nil, errors.Annotate(err, "cannot get devices addresses")
1651
"geting network config for machine %q with addresses %+v, hosting unit %q of application %q, with bindings %+v",
1652
machineID, addresses, unit.Name(), service.Name(), bindings,
1655
for _, addr := range addresses {
1656
subnet, err := addr.Subnet()
1657
if errors.IsNotFound(err) {
1658
logger.Debugf("skipping %s: not linked to a known subnet (%v)", addr, err)
1660
} else if err != nil {
1661
return nil, errors.Annotatef(err, "cannot get subnet for address %q", addr)
1664
if space := subnet.SpaceName(); space != boundSpace {
1665
logger.Debugf("skipping %s: want bound to space %q, got space %q", addr, boundSpace, space)
1668
logger.Debugf("endpoint %q bound to space %q has address %q", bindingName, boundSpace, addr)
1670
// TODO(dimitern): Fill in the rest later (see linked LKK card above).
1671
results = append(results, params.NetworkConfig{
1672
Address: addr.Value(),