github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/api/uniter/uniter.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package uniter
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  	"gopkg.in/juju/charm.v6-unstable"
    11  	"gopkg.in/juju/names.v2"
    12  
    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"
    19  )
    20  
    21  const uniterFacade = "Uniter"
    22  
    23  // State provides access to the Uniter API facade.
    24  type State struct {
    25  	*common.ModelWatcher
    26  	*common.APIAddresser
    27  	*StorageAccessor
    28  
    29  	LeadershipSettings *LeadershipSettingsAccessor
    30  	facade             base.FacadeCaller
    31  	// unitTag contains the authenticated unit's tag.
    32  	unitTag names.UnitTag
    33  }
    34  
    35  // newStateForVersion creates a new client-side Uniter facade for the
    36  // given version.
    37  func newStateForVersion(
    38  	caller base.APICaller,
    39  	authTag names.UnitTag,
    40  	version int,
    41  ) *State {
    42  	facadeCaller := base.NewFacadeCallerForVersion(
    43  		caller,
    44  		uniterFacade,
    45  		version,
    46  	)
    47  	state := &State{
    48  		ModelWatcher:    common.NewModelWatcher(facadeCaller),
    49  		APIAddresser:    common.NewAPIAddresser(facadeCaller),
    50  		StorageAccessor: NewStorageAccessor(facadeCaller),
    51  		facade:          facadeCaller,
    52  		unitTag:         authTag,
    53  	}
    54  
    55  	newWatcher := func(result params.NotifyWatchResult) watcher.NotifyWatcher {
    56  		return apiwatcher.NewNotifyWatcher(caller, result)
    57  	}
    58  	state.LeadershipSettings = NewLeadershipSettingsAccessor(
    59  		facadeCaller.FacadeCall,
    60  		newWatcher,
    61  		ErrIfNotVersionFn(2, state.BestAPIVersion()),
    62  	)
    63  	return state
    64  }
    65  
    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)
    69  	}
    70  }
    71  
    72  // newStateV4 creates a new client-side Uniter facade, version 4.
    73  var newStateV4 = newStateForVersionFn(4)
    74  
    75  // NewState creates a new client-side Uniter facade.
    76  // Defined like this to allow patching during tests.
    77  var NewState = newStateV4
    78  
    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()
    83  }
    84  
    85  // Facade returns the current facade.
    86  func (st *State) Facade() base.FacadeCaller {
    87  	return st.facade
    88  }
    89  
    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)
    93  }
    94  
    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()},
   102  		},
   103  	}
   104  	err := st.facade.FacadeCall("Relation", args, &result)
   105  	if err != nil {
   106  		return nothing, err
   107  	}
   108  	if len(result.Results) != 1 {
   109  		return nothing, fmt.Errorf("expected 1 result, got %d", len(result.Results))
   110  	}
   111  	if err := result.Results[0].Error; err != nil {
   112  		return nothing, err
   113  	}
   114  	return result.Results[0], nil
   115  }
   116  
   117  // getOneAction retrieves a single Action from the controller.
   118  func (st *State) getOneAction(tag *names.ActionTag) (params.ActionResult, error) {
   119  	nothing := params.ActionResult{}
   120  
   121  	args := params.Entities{
   122  		Entities: []params.Entity{
   123  			{Tag: tag.String()},
   124  		},
   125  	}
   126  
   127  	var results params.ActionResults
   128  	err := st.facade.FacadeCall("Actions", args, &results)
   129  	if err != nil {
   130  		return nothing, err
   131  	}
   132  
   133  	if len(results.Results) > 1 {
   134  		return nothing, fmt.Errorf("expected only 1 action query result, got %d", len(results.Results))
   135  	}
   136  
   137  	// handle server errors
   138  	result := results.Results[0]
   139  	if err := result.Error; err != nil {
   140  		return nothing, err
   141  	}
   142  
   143  	return result, nil
   144  }
   145  
   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)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	return &Unit{
   153  		tag:  tag,
   154  		life: life,
   155  		st:   st,
   156  	}, nil
   157  }
   158  
   159  // Application returns an application state by tag.
   160  func (st *State) Application(tag names.ApplicationTag) (*Application, error) {
   161  	life, err := st.life(tag)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  	return &Application{
   166  		tag:  tag,
   167  		life: life,
   168  		st:   st,
   169  	}, nil
   170  }
   171  
   172  // ProviderType returns a provider type used by the current juju model.
   173  //
   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)
   179  	if err != nil {
   180  		return "", err
   181  	}
   182  	if err := result.Error; err != nil {
   183  		return "", err
   184  	}
   185  	return result.Result, nil
   186  }
   187  
   188  // Charm returns the charm with the given URL.
   189  func (st *State) Charm(curl *charm.URL) (*Charm, error) {
   190  	if curl == nil {
   191  		return nil, fmt.Errorf("charm url cannot be nil")
   192  	}
   193  	return &Charm{
   194  		st:   st,
   195  		curl: curl,
   196  	}, nil
   197  }
   198  
   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)
   202  	if err != nil {
   203  		return nil, err
   204  	}
   205  	return &Relation{
   206  		id:   result.Id,
   207  		tag:  relationTag,
   208  		life: result.Life,
   209  		st:   st,
   210  	}, nil
   211  }
   212  
   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)
   216  	if err != nil {
   217  		return nil, err
   218  	}
   219  	return &Action{
   220  		name:   result.Action.Name,
   221  		params: result.Action.Parameters,
   222  	}, nil
   223  }
   224  
   225  // ActionBegin marks an action as running.
   226  func (st *State) ActionBegin(tag names.ActionTag) error {
   227  	var outcome params.ErrorResults
   228  
   229  	args := params.Entities{
   230  		Entities: []params.Entity{
   231  			{Tag: tag.String()},
   232  		},
   233  	}
   234  
   235  	err := st.facade.FacadeCall("BeginActions", args, &outcome)
   236  	if err != nil {
   237  		return err
   238  	}
   239  	if len(outcome.Results) != 1 {
   240  		return fmt.Errorf("expected 1 result, got %d", len(outcome.Results))
   241  	}
   242  	result := outcome.Results[0]
   243  	if result.Error != nil {
   244  		return result.Error
   245  	}
   246  	return nil
   247  }
   248  
   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
   252  
   253  	args := params.ActionExecutionResults{
   254  		Results: []params.ActionExecutionResult{
   255  			{
   256  				ActionTag: tag.String(),
   257  				Status:    status,
   258  				Results:   results,
   259  				Message:   message,
   260  			},
   261  		},
   262  	}
   263  
   264  	err := st.facade.FacadeCall("FinishActions", args, &outcome)
   265  	if err != nil {
   266  		return err
   267  	}
   268  	if len(outcome.Results) != 1 {
   269  		return fmt.Errorf("expected 1 result, got %d", len(outcome.Results))
   270  	}
   271  	result := outcome.Results[0]
   272  	if result.Error != nil {
   273  		return result.Error
   274  	}
   275  	return nil
   276  }
   277  
   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},
   283  	}
   284  	err := st.facade.FacadeCall("RelationById", args, &results)
   285  	if err != nil {
   286  		return nil, err
   287  	}
   288  	if len(results.Results) != 1 {
   289  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   290  	}
   291  	result := results.Results[0]
   292  	if err := result.Error; err != nil {
   293  		return nil, err
   294  	}
   295  	relationTag := names.NewRelationTag(result.Key)
   296  	return &Relation{
   297  		id:   result.Id,
   298  		tag:  relationTag,
   299  		life: result.Life,
   300  		st:   st,
   301  	}, nil
   302  }
   303  
   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)
   308  	if err != nil {
   309  		return nil, err
   310  	}
   311  	if err := result.Error; err != nil {
   312  		return nil, err
   313  	}
   314  	return &Model{
   315  		name: result.Name,
   316  		uuid: result.UUID,
   317  	}, nil
   318  }
   319  
   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+)")
   327  	}
   328  	var results params.MachinePortsResults
   329  	args := params.Entities{
   330  		Entities: []params.Entity{{Tag: machineTag.String()}},
   331  	}
   332  	err := st.facade.FacadeCall("AllMachinePorts", args, &results)
   333  	if err != nil {
   334  		return nil, err
   335  	}
   336  	if len(results.Results) != 1 {
   337  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   338  	}
   339  	result := results.Results[0]
   340  	if result.Error != nil {
   341  		return nil, result.Error
   342  	}
   343  	portsMap := make(map[network.PortRange]params.RelationUnit)
   344  	for _, ports := range result.Ports {
   345  		portRange := ports.PortRange.NetworkPortRange()
   346  		portsMap[portRange] = params.RelationUnit{
   347  			Unit:     ports.UnitTag,
   348  			Relation: ports.RelationTag,
   349  		}
   350  	}
   351  	return portsMap, nil
   352  }
   353  
   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(),
   365  		}},
   366  	}
   367  	err := st.facade.FacadeCall("WatchRelationUnits", args, &results)
   368  	if err != nil {
   369  		return nil, err
   370  	}
   371  	if len(results.Results) != 1 {
   372  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   373  	}
   374  	result := results.Results[0]
   375  	if result.Error != nil {
   376  		return nil, result.Error
   377  	}
   378  	w := apiwatcher.NewRelationUnitsWatcher(st.facade.RawAPICaller(), result)
   379  	return w, nil
   380  }
   381  
   382  // ErrIfNotVersionFn returns a function which can be used to check for
   383  // the minimum supported version, and, if appropriate, generate an
   384  // error.
   385  func ErrIfNotVersionFn(minVersion int, bestAPIVersion int) func(string) error {
   386  	return func(fnName string) error {
   387  		if minVersion <= bestAPIVersion {
   388  			return nil
   389  		}
   390  		return errors.NotImplementedf("%s(...) requires v%d+", fnName, minVersion)
   391  	}
   392  }