github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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  	"github.com/juju/names"
    11  	"gopkg.in/juju/charm.v6-unstable"
    12  
    13  	"github.com/juju/juju/api/base"
    14  	"github.com/juju/juju/api/common"
    15  	"github.com/juju/juju/api/watcher"
    16  	"github.com/juju/juju/apiserver/params"
    17  	"github.com/juju/juju/network"
    18  )
    19  
    20  const uniterFacade = "Uniter"
    21  
    22  // State provides access to the Uniter API facade.
    23  type State struct {
    24  	*common.EnvironWatcher
    25  	*common.APIAddresser
    26  	*StorageAccessor
    27  
    28  	LeadershipSettings *LeadershipSettingsAccessor
    29  	facade             base.FacadeCaller
    30  	// unitTag contains the authenticated unit's tag.
    31  	unitTag names.UnitTag
    32  }
    33  
    34  // newStateForVersion creates a new client-side Uniter facade for the
    35  // given version.
    36  func newStateForVersion(
    37  	caller base.APICaller,
    38  	authTag names.UnitTag,
    39  	version int,
    40  ) *State {
    41  	facadeCaller := base.NewFacadeCallerForVersion(
    42  		caller,
    43  		uniterFacade,
    44  		version,
    45  	)
    46  	state := &State{
    47  		EnvironWatcher:  common.NewEnvironWatcher(facadeCaller),
    48  		APIAddresser:    common.NewAPIAddresser(facadeCaller),
    49  		StorageAccessor: NewStorageAccessor(facadeCaller),
    50  		facade:          facadeCaller,
    51  		unitTag:         authTag,
    52  	}
    53  
    54  	if version >= 2 {
    55  		newWatcher := func(result params.NotifyWatchResult) watcher.NotifyWatcher {
    56  			return watcher.NewNotifyWatcher(caller, result)
    57  		}
    58  		state.LeadershipSettings = NewLeadershipSettingsAccessor(
    59  			facadeCaller.FacadeCall,
    60  			newWatcher,
    61  			ErrIfNotVersionFn(2, state.BestAPIVersion()),
    62  		)
    63  	}
    64  
    65  	return state
    66  }
    67  
    68  func newStateForVersionFn(version int) func(base.APICaller, names.UnitTag) *State {
    69  	return func(caller base.APICaller, authTag names.UnitTag) *State {
    70  		return newStateForVersion(caller, authTag, version)
    71  	}
    72  }
    73  
    74  // newStateV0 creates a new client-side Uniter facade, version 0.
    75  var newStateV0 = newStateForVersionFn(0)
    76  
    77  // newStateV1 creates a new client-side Uniter facade, version 1.
    78  var newStateV1 = newStateForVersionFn(1)
    79  
    80  // newStateV2 creates a new client-side Uniter facade, version 2.
    81  var newStateV2 = newStateForVersionFn(2)
    82  
    83  // NewState creates a new client-side Uniter facade.
    84  // Defined like this to allow patching during tests.
    85  var NewState = newStateV2
    86  
    87  // BestAPIVersion returns the API version that we were able to
    88  // determine is supported by both the client and the API Server.
    89  func (st *State) BestAPIVersion() int {
    90  	return st.facade.BestAPIVersion()
    91  }
    92  
    93  // life requests the lifecycle of the given entity from the server.
    94  func (st *State) life(tag names.Tag) (params.Life, error) {
    95  	return common.Life(st.facade, tag)
    96  }
    97  
    98  // relation requests relation information from the server.
    99  func (st *State) relation(relationTag, unitTag names.Tag) (params.RelationResult, error) {
   100  	nothing := params.RelationResult{}
   101  	var result params.RelationResults
   102  	args := params.RelationUnits{
   103  		RelationUnits: []params.RelationUnit{
   104  			{Relation: relationTag.String(), Unit: unitTag.String()},
   105  		},
   106  	}
   107  	err := st.facade.FacadeCall("Relation", args, &result)
   108  	if err != nil {
   109  		return nothing, err
   110  	}
   111  	if len(result.Results) != 1 {
   112  		return nothing, fmt.Errorf("expected 1 result, got %d", len(result.Results))
   113  	}
   114  	if err := result.Results[0].Error; err != nil {
   115  		return nothing, err
   116  	}
   117  	return result.Results[0], nil
   118  }
   119  
   120  // getOneAction retrieves a single Action from the state server.
   121  func (st *State) getOneAction(tag *names.ActionTag) (params.ActionsQueryResult, error) {
   122  	nothing := params.ActionsQueryResult{}
   123  
   124  	args := params.Entities{
   125  		Entities: []params.Entity{
   126  			{Tag: tag.String()},
   127  		},
   128  	}
   129  
   130  	var results params.ActionsQueryResults
   131  	err := st.facade.FacadeCall("Actions", args, &results)
   132  	if err != nil {
   133  		return nothing, err
   134  	}
   135  
   136  	if len(results.Results) > 1 {
   137  		return nothing, fmt.Errorf("expected only 1 action query result, got %d", len(results.Results))
   138  	}
   139  
   140  	// handle server errors
   141  	result := results.Results[0]
   142  	if err := result.Error; err != nil {
   143  		return nothing, err
   144  	}
   145  
   146  	return result, nil
   147  }
   148  
   149  // Unit provides access to methods of a state.Unit through the facade.
   150  func (st *State) Unit(tag names.UnitTag) (*Unit, error) {
   151  	life, err := st.life(tag)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  	return &Unit{
   156  		tag:  tag,
   157  		life: life,
   158  		st:   st,
   159  	}, nil
   160  }
   161  
   162  // Service returns a service state by tag.
   163  func (st *State) Service(tag names.ServiceTag) (*Service, error) {
   164  	life, err := st.life(tag)
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  	return &Service{
   169  		tag:  tag,
   170  		life: life,
   171  		st:   st,
   172  	}, nil
   173  }
   174  
   175  // ProviderType returns a provider type used by the current juju
   176  // environment.
   177  //
   178  // TODO(dimitern): We might be able to drop this, once we have machine
   179  // addresses implemented fully. See also LP bug 1221798.
   180  func (st *State) ProviderType() (string, error) {
   181  	var result params.StringResult
   182  	err := st.facade.FacadeCall("ProviderType", nil, &result)
   183  	if err != nil {
   184  		return "", err
   185  	}
   186  	if err := result.Error; err != nil {
   187  		return "", err
   188  	}
   189  	return result.Result, nil
   190  }
   191  
   192  // Charm returns the charm with the given URL.
   193  func (st *State) Charm(curl *charm.URL) (*Charm, error) {
   194  	if curl == nil {
   195  		return nil, fmt.Errorf("charm url cannot be nil")
   196  	}
   197  	return &Charm{
   198  		st:   st,
   199  		curl: curl,
   200  	}, nil
   201  }
   202  
   203  // Relation returns the existing relation with the given tag.
   204  func (st *State) Relation(relationTag names.RelationTag) (*Relation, error) {
   205  	result, err := st.relation(relationTag, st.unitTag)
   206  	if err != nil {
   207  		return nil, err
   208  	}
   209  	return &Relation{
   210  		id:   result.Id,
   211  		tag:  relationTag,
   212  		life: result.Life,
   213  		st:   st,
   214  	}, nil
   215  }
   216  
   217  // Action returns the Action with the given tag.
   218  func (st *State) Action(tag names.ActionTag) (*Action, error) {
   219  	result, err := st.getOneAction(&tag)
   220  	if err != nil {
   221  		return nil, err
   222  	}
   223  	return &Action{
   224  		name:   result.Action.Action.Name,
   225  		params: result.Action.Action.Parameters,
   226  	}, nil
   227  }
   228  
   229  // ActionBegin marks an action as running.
   230  func (st *State) ActionBegin(tag names.ActionTag) error {
   231  	var outcome params.ErrorResults
   232  
   233  	args := params.Entities{
   234  		Entities: []params.Entity{
   235  			{Tag: tag.String()},
   236  		},
   237  	}
   238  
   239  	err := st.facade.FacadeCall("BeginActions", args, &outcome)
   240  	if err != nil {
   241  		return err
   242  	}
   243  	if len(outcome.Results) != 1 {
   244  		return fmt.Errorf("expected 1 result, got %d", len(outcome.Results))
   245  	}
   246  	result := outcome.Results[0]
   247  	if result.Error != nil {
   248  		return result.Error
   249  	}
   250  	return nil
   251  }
   252  
   253  // ActionFinish captures the structured output of an action.
   254  func (st *State) ActionFinish(tag names.ActionTag, status string, results map[string]interface{}, message string) error {
   255  	var outcome params.ErrorResults
   256  
   257  	args := params.ActionExecutionResults{
   258  		Results: []params.ActionExecutionResult{
   259  			{
   260  				ActionTag: tag.String(),
   261  				Status:    status,
   262  				Results:   results,
   263  				Message:   message,
   264  			},
   265  		},
   266  	}
   267  
   268  	err := st.facade.FacadeCall("FinishActions", args, &outcome)
   269  	if err != nil {
   270  		return err
   271  	}
   272  	if len(outcome.Results) != 1 {
   273  		return fmt.Errorf("expected 1 result, got %d", len(outcome.Results))
   274  	}
   275  	result := outcome.Results[0]
   276  	if result.Error != nil {
   277  		return result.Error
   278  	}
   279  	return nil
   280  }
   281  
   282  // RelationById returns the existing relation with the given id.
   283  func (st *State) RelationById(id int) (*Relation, error) {
   284  	var results params.RelationResults
   285  	args := params.RelationIds{
   286  		RelationIds: []int{id},
   287  	}
   288  	err := st.facade.FacadeCall("RelationById", args, &results)
   289  	if err != nil {
   290  		return nil, err
   291  	}
   292  	if len(results.Results) != 1 {
   293  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   294  	}
   295  	result := results.Results[0]
   296  	if err := result.Error; err != nil {
   297  		return nil, err
   298  	}
   299  	relationTag := names.NewRelationTag(result.Key)
   300  	return &Relation{
   301  		id:   result.Id,
   302  		tag:  relationTag,
   303  		life: result.Life,
   304  		st:   st,
   305  	}, nil
   306  }
   307  
   308  // Environment returns the environment entity.
   309  func (st *State) Environment() (*Environment, error) {
   310  	var result params.EnvironmentResult
   311  	err := st.facade.FacadeCall("CurrentEnvironment", nil, &result)
   312  	if params.IsCodeNotImplemented(err) {
   313  		// Fall back to using the 1.16 API.
   314  		return st.environment1dot16()
   315  	}
   316  	if err != nil {
   317  		return nil, err
   318  	}
   319  	if err := result.Error; err != nil {
   320  		return nil, err
   321  	}
   322  	return &Environment{
   323  		name: result.Name,
   324  		uuid: result.UUID,
   325  	}, nil
   326  }
   327  
   328  // AllMachinePorts returns all port ranges currently open on the given
   329  // machine, mapped to the tags of the unit that opened them and the
   330  // relation that applies.
   331  func (st *State) AllMachinePorts(machineTag names.MachineTag) (map[network.PortRange]params.RelationUnit, error) {
   332  	if st.BestAPIVersion() < 1 {
   333  		// AllMachinePorts() was introduced in UniterAPIV1.
   334  		return nil, errors.NotImplementedf("AllMachinePorts() (need V1+)")
   335  	}
   336  	var results params.MachinePortsResults
   337  	args := params.Entities{
   338  		Entities: []params.Entity{{Tag: machineTag.String()}},
   339  	}
   340  	err := st.facade.FacadeCall("AllMachinePorts", args, &results)
   341  	if err != nil {
   342  		return nil, err
   343  	}
   344  	if len(results.Results) != 1 {
   345  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   346  	}
   347  	result := results.Results[0]
   348  	if result.Error != nil {
   349  		return nil, result.Error
   350  	}
   351  	portsMap := make(map[network.PortRange]params.RelationUnit)
   352  	for _, ports := range result.Ports {
   353  		portRange := ports.PortRange.NetworkPortRange()
   354  		portsMap[portRange] = params.RelationUnit{
   355  			Unit:     ports.UnitTag,
   356  			Relation: ports.RelationTag,
   357  		}
   358  	}
   359  	return portsMap, nil
   360  }
   361  
   362  // WatchRelationUnits returns a watcher that notifies of changes to the
   363  // counterpart units in the relation for the given unit.
   364  func (st *State) WatchRelationUnits(
   365  	relationTag names.RelationTag,
   366  	unitTag names.UnitTag,
   367  ) (watcher.RelationUnitsWatcher, error) {
   368  	var results params.RelationUnitsWatchResults
   369  	args := params.RelationUnits{
   370  		RelationUnits: []params.RelationUnit{{
   371  			Relation: relationTag.String(),
   372  			Unit:     unitTag.String(),
   373  		}},
   374  	}
   375  	err := st.facade.FacadeCall("WatchRelationUnits", args, &results)
   376  	if err != nil {
   377  		return nil, err
   378  	}
   379  	if len(results.Results) != 1 {
   380  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   381  	}
   382  	result := results.Results[0]
   383  	if result.Error != nil {
   384  		return nil, result.Error
   385  	}
   386  	w := watcher.NewRelationUnitsWatcher(st.facade.RawAPICaller(), result)
   387  	return w, nil
   388  }
   389  
   390  // environment1dot16 requests just the UUID of the current environment, when
   391  // using an older API server that does not support CurrentEnvironment API call.
   392  func (st *State) environment1dot16() (*Environment, error) {
   393  	var result params.StringResult
   394  	err := st.facade.FacadeCall("CurrentEnvironUUID", nil, &result)
   395  	if err != nil {
   396  		return nil, err
   397  	}
   398  	if err := result.Error; err != nil {
   399  		return nil, err
   400  	}
   401  	return &Environment{
   402  		uuid: result.Result,
   403  	}, nil
   404  }
   405  
   406  // ErrIfNotVersionFn returns a function which can be used to check for
   407  // the minimum supported version, and, if appropriate, generate an
   408  // error.
   409  func ErrIfNotVersionFn(minVersion int, bestApiVersion int) func(string) error {
   410  	return func(fnName string) error {
   411  		if minVersion <= bestApiVersion {
   412  			return nil
   413  		}
   414  		return errors.NotImplementedf("%s(...) requires v%d+", fnName, minVersion)
   415  	}
   416  }