1
// Copyright 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
9
"github.com/juju/errors"
10
"github.com/juju/names"
11
"gopkg.in/juju/charm.v6-unstable"
13
"github.com/juju/juju/api/base"
14
"github.com/juju/juju/api/common"
15
apiwatcher "github.com/juju/juju/api/watcher"
16
"github.com/juju/juju/apiserver/params"
17
"github.com/juju/juju/network"
18
"github.com/juju/juju/watcher"
21
const uniterFacade = "Uniter"
23
// State provides access to the Uniter API facade.
29
LeadershipSettings *LeadershipSettingsAccessor
30
facade base.FacadeCaller
31
// unitTag contains the authenticated unit's tag.
35
// newStateForVersion creates a new client-side Uniter facade for the
37
func newStateForVersion(
38
caller base.APICaller,
39
authTag names.UnitTag,
42
facadeCaller := base.NewFacadeCallerForVersion(
48
ModelWatcher: common.NewModelWatcher(facadeCaller),
49
APIAddresser: common.NewAPIAddresser(facadeCaller),
50
StorageAccessor: NewStorageAccessor(facadeCaller),
55
newWatcher := func(result params.NotifyWatchResult) watcher.NotifyWatcher {
56
return apiwatcher.NewNotifyWatcher(caller, result)
58
state.LeadershipSettings = NewLeadershipSettingsAccessor(
59
facadeCaller.FacadeCall,
61
ErrIfNotVersionFn(2, state.BestAPIVersion()),
66
func newStateForVersionFn(version int) func(base.APICaller, names.UnitTag) *State {
67
return func(caller base.APICaller, authTag names.UnitTag) *State {
68
return newStateForVersion(caller, authTag, version)
72
// newStateV3 creates a new client-side Uniter facade, version 3.
73
var newStateV3 = newStateForVersionFn(3)
75
// NewState creates a new client-side Uniter facade.
76
// Defined like this to allow patching during tests.
77
var NewState = newStateV3
79
// BestAPIVersion returns the API version that we were able to
80
// determine is supported by both the client and the API Server.
81
func (st *State) BestAPIVersion() int {
82
return st.facade.BestAPIVersion()
85
// Facade returns the current facade.
86
func (st *State) Facade() base.FacadeCaller {
90
// life requests the lifecycle of the given entity from the server.
91
func (st *State) life(tag names.Tag) (params.Life, error) {
92
return common.Life(st.facade, tag)
95
// relation requests relation information from the server.
96
func (st *State) relation(relationTag, unitTag names.Tag) (params.RelationResult, error) {
97
nothing := params.RelationResult{}
98
var result params.RelationResults
99
args := params.RelationUnits{
100
RelationUnits: []params.RelationUnit{
101
{Relation: relationTag.String(), Unit: unitTag.String()},
104
err := st.facade.FacadeCall("Relation", args, &result)
108
if len(result.Results) != 1 {
109
return nothing, fmt.Errorf("expected 1 result, got %d", len(result.Results))
111
if err := result.Results[0].Error; err != nil {
114
return result.Results[0], nil
117
// getOneAction retrieves a single Action from the controller.
118
func (st *State) getOneAction(tag *names.ActionTag) (params.ActionsQueryResult, error) {
119
nothing := params.ActionsQueryResult{}
121
args := params.Entities{
122
Entities: []params.Entity{
127
var results params.ActionsQueryResults
128
err := st.facade.FacadeCall("Actions", args, &results)
133
if len(results.Results) > 1 {
134
return nothing, fmt.Errorf("expected only 1 action query result, got %d", len(results.Results))
137
// handle server errors
138
result := results.Results[0]
139
if err := result.Error; err != nil {
146
// Unit provides access to methods of a state.Unit through the facade.
147
func (st *State) Unit(tag names.UnitTag) (*Unit, error) {
148
life, err := st.life(tag)
159
// Service returns a service state by tag.
160
func (st *State) Service(tag names.ServiceTag) (*Service, error) {
161
life, err := st.life(tag)
172
// ProviderType returns a provider type used by the current juju model.
174
// TODO(dimitern): We might be able to drop this, once we have machine
175
// addresses implemented fully. See also LP bug 1221798.
176
func (st *State) ProviderType() (string, error) {
177
var result params.StringResult
178
err := st.facade.FacadeCall("ProviderType", nil, &result)
182
if err := result.Error; err != nil {
185
return result.Result, nil
188
// Charm returns the charm with the given URL.
189
func (st *State) Charm(curl *charm.URL) (*Charm, error) {
191
return nil, fmt.Errorf("charm url cannot be nil")
199
// Relation returns the existing relation with the given tag.
200
func (st *State) Relation(relationTag names.RelationTag) (*Relation, error) {
201
result, err := st.relation(relationTag, st.unitTag)
213
// Action returns the Action with the given tag.
214
func (st *State) Action(tag names.ActionTag) (*Action, error) {
215
result, err := st.getOneAction(&tag)
220
name: result.Action.Action.Name,
221
params: result.Action.Action.Parameters,
225
// ActionBegin marks an action as running.
226
func (st *State) ActionBegin(tag names.ActionTag) error {
227
var outcome params.ErrorResults
229
args := params.Entities{
230
Entities: []params.Entity{
235
err := st.facade.FacadeCall("BeginActions", args, &outcome)
239
if len(outcome.Results) != 1 {
240
return fmt.Errorf("expected 1 result, got %d", len(outcome.Results))
242
result := outcome.Results[0]
243
if result.Error != nil {
249
// ActionFinish captures the structured output of an action.
250
func (st *State) ActionFinish(tag names.ActionTag, status string, results map[string]interface{}, message string) error {
251
var outcome params.ErrorResults
253
args := params.ActionExecutionResults{
254
Results: []params.ActionExecutionResult{
256
ActionTag: tag.String(),
264
err := st.facade.FacadeCall("FinishActions", args, &outcome)
268
if len(outcome.Results) != 1 {
269
return fmt.Errorf("expected 1 result, got %d", len(outcome.Results))
271
result := outcome.Results[0]
272
if result.Error != nil {
278
// RelationById returns the existing relation with the given id.
279
func (st *State) RelationById(id int) (*Relation, error) {
280
var results params.RelationResults
281
args := params.RelationIds{
282
RelationIds: []int{id},
284
err := st.facade.FacadeCall("RelationById", args, &results)
288
if len(results.Results) != 1 {
289
return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
291
result := results.Results[0]
292
if err := result.Error; err != nil {
295
relationTag := names.NewRelationTag(result.Key)
304
// Model returns the model entity.
305
func (st *State) Model() (*Model, error) {
306
var result params.ModelResult
307
err := st.facade.FacadeCall("CurrentModel", nil, &result)
311
if err := result.Error; err != nil {
320
// AllMachinePorts returns all port ranges currently open on the given
321
// machine, mapped to the tags of the unit that opened them and the
322
// relation that applies.
323
func (st *State) AllMachinePorts(machineTag names.MachineTag) (map[network.PortRange]params.RelationUnit, error) {
324
if st.BestAPIVersion() < 1 {
325
// AllMachinePorts() was introduced in UniterAPIV1.
326
return nil, errors.NotImplementedf("AllMachinePorts() (need V1+)")
328
var results params.MachinePortsResults
329
args := params.Entities{
330
Entities: []params.Entity{{Tag: machineTag.String()}},
332
err := st.facade.FacadeCall("AllMachinePorts", args, &results)
336
if len(results.Results) != 1 {
337
return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
339
result := results.Results[0]
340
if result.Error != nil {
341
return nil, result.Error
343
portsMap := make(map[network.PortRange]params.RelationUnit)
344
for _, ports := range result.Ports {
345
portRange := ports.PortRange.NetworkPortRange()
346
portsMap[portRange] = params.RelationUnit{
348
Relation: ports.RelationTag,
354
// WatchRelationUnits returns a watcher that notifies of changes to the
355
// counterpart units in the relation for the given unit.
356
func (st *State) WatchRelationUnits(
357
relationTag names.RelationTag,
358
unitTag names.UnitTag,
359
) (watcher.RelationUnitsWatcher, error) {
360
var results params.RelationUnitsWatchResults
361
args := params.RelationUnits{
362
RelationUnits: []params.RelationUnit{{
363
Relation: relationTag.String(),
364
Unit: unitTag.String(),
367
err := st.facade.FacadeCall("WatchRelationUnits", args, &results)
371
if len(results.Results) != 1 {
372
return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
374
result := results.Results[0]
375
if result.Error != nil {
376
return nil, result.Error
378
w := apiwatcher.NewRelationUnitsWatcher(st.facade.RawAPICaller(), result)
382
// ErrIfNotVersionFn returns a function which can be used to check for
383
// the minimum supported version, and, if appropriate, generate an
385
func ErrIfNotVersionFn(minVersion int, bestApiVersion int) func(string) error {
386
return func(fnName string) error {
387
if minVersion <= bestApiVersion {
390
return errors.NotImplementedf("%s(...) requires v%d+", fnName, minVersion)