github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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"
    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/core/application"
    18  	"github.com/juju/juju/core/model"
    19  	corenetwork "github.com/juju/juju/core/network"
    20  	"github.com/juju/juju/core/relation"
    21  	"github.com/juju/juju/core/watcher"
    22  )
    23  
    24  const uniterFacade = "Uniter"
    25  
    26  // State provides access to the Uniter API facade.
    27  type State struct {
    28  	*common.ModelWatcher
    29  	*common.APIAddresser
    30  	*common.UpgradeSeriesAPI
    31  	*LXDProfileAPI
    32  	*StorageAccessor
    33  
    34  	LeadershipSettings *LeadershipSettingsAccessor
    35  	facade             base.FacadeCaller
    36  	// unitTag contains the authenticated unit's tag.
    37  	unitTag names.UnitTag
    38  }
    39  
    40  // newStateForVersion creates a new client-side Uniter facade for the
    41  // given version.
    42  func newStateForVersion(
    43  	caller base.APICaller,
    44  	authTag names.UnitTag,
    45  	version int,
    46  ) *State {
    47  	facadeCaller := base.NewFacadeCallerForVersion(
    48  		caller,
    49  		uniterFacade,
    50  		version,
    51  	)
    52  	state := &State{
    53  		ModelWatcher:     common.NewModelWatcher(facadeCaller),
    54  		APIAddresser:     common.NewAPIAddresser(facadeCaller),
    55  		UpgradeSeriesAPI: common.NewUpgradeSeriesAPI(facadeCaller, authTag),
    56  		LXDProfileAPI:    NewLXDProfileAPI(facadeCaller, authTag),
    57  		StorageAccessor:  NewStorageAccessor(facadeCaller),
    58  		facade:           facadeCaller,
    59  		unitTag:          authTag,
    60  	}
    61  
    62  	newWatcher := func(result params.NotifyWatchResult) watcher.NotifyWatcher {
    63  		return apiwatcher.NewNotifyWatcher(caller, result)
    64  	}
    65  	state.LeadershipSettings = NewLeadershipSettingsAccessor(
    66  		facadeCaller.FacadeCall,
    67  		newWatcher,
    68  		ErrIfNotVersionFn(2, state.BestAPIVersion()),
    69  	)
    70  	return state
    71  }
    72  
    73  func newStateForVersionFn(version int) func(base.APICaller, names.UnitTag) *State {
    74  	return func(caller base.APICaller, authTag names.UnitTag) *State {
    75  		return newStateForVersion(caller, authTag, version)
    76  	}
    77  }
    78  
    79  // newStateV9 creates a new client-side Uniter facade, version 9
    80  var newStateV9 = newStateForVersionFn(9)
    81  
    82  // NewState creates a new client-side Uniter facade.
    83  // Defined like this to allow patching during tests.
    84  var NewState = newStateV9
    85  
    86  // BestAPIVersion returns the API version that we were able to
    87  // determine is supported by both the client and the API Server.
    88  func (st *State) BestAPIVersion() int {
    89  	return st.facade.BestAPIVersion()
    90  }
    91  
    92  // Facade returns the current facade.
    93  func (st *State) Facade() base.FacadeCaller {
    94  	return st.facade
    95  }
    96  
    97  // life requests the lifecycle of the given entity from the server.
    98  func (st *State) life(tag names.Tag) (params.Life, error) {
    99  	return common.OneLife(st.facade, tag)
   100  }
   101  
   102  // relation requests relation information from the server.
   103  func (st *State) relation(relationTag, unitTag names.Tag) (params.RelationResult, error) {
   104  	nothing := params.RelationResult{}
   105  	var result params.RelationResults
   106  	args := params.RelationUnits{
   107  		RelationUnits: []params.RelationUnit{
   108  			{Relation: relationTag.String(), Unit: unitTag.String()},
   109  		},
   110  	}
   111  	err := st.facade.FacadeCall("Relation", args, &result)
   112  	if err != nil {
   113  		return nothing, err
   114  	}
   115  	if len(result.Results) != 1 {
   116  		return nothing, fmt.Errorf("expected 1 result, got %d", len(result.Results))
   117  	}
   118  	if err := result.Results[0].Error; err != nil {
   119  		return nothing, err
   120  	}
   121  	return result.Results[0], nil
   122  }
   123  
   124  func (st *State) setRelationStatus(id int, status relation.Status) error {
   125  	args := params.RelationStatusArgs{
   126  		Args: []params.RelationStatusArg{{
   127  			UnitTag:    st.unitTag.String(),
   128  			RelationId: id,
   129  			Status:     params.RelationStatusValue(status),
   130  		}},
   131  	}
   132  	var results params.ErrorResults
   133  	if err := st.facade.FacadeCall("SetRelationStatus", args, &results); err != nil {
   134  		return errors.Trace(err)
   135  	}
   136  	return results.OneError()
   137  }
   138  
   139  // getOneAction retrieves a single Action from the controller.
   140  func (st *State) getOneAction(tag *names.ActionTag) (params.ActionResult, error) {
   141  	nothing := params.ActionResult{}
   142  
   143  	args := params.Entities{
   144  		Entities: []params.Entity{
   145  			{Tag: tag.String()},
   146  		},
   147  	}
   148  
   149  	var results params.ActionResults
   150  	err := st.facade.FacadeCall("Actions", args, &results)
   151  	if err != nil {
   152  		return nothing, err
   153  	}
   154  
   155  	if len(results.Results) > 1 {
   156  		return nothing, fmt.Errorf("expected only 1 action query result, got %d", len(results.Results))
   157  	}
   158  
   159  	// handle server errors
   160  	result := results.Results[0]
   161  	if err := result.Error; err != nil {
   162  		return nothing, err
   163  	}
   164  
   165  	return result, nil
   166  }
   167  
   168  // Unit provides access to methods of a state.Unit through the facade.
   169  func (st *State) Unit(tag names.UnitTag) (*Unit, error) {
   170  	unit := &Unit{
   171  		tag: tag,
   172  		st:  st,
   173  	}
   174  	err := unit.Refresh()
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  	return unit, nil
   179  }
   180  
   181  // Application returns an application state by tag.
   182  func (st *State) Application(tag names.ApplicationTag) (*Application, error) {
   183  	life, err := st.life(tag)
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  	return &Application{
   188  		tag:  tag,
   189  		life: life,
   190  		st:   st,
   191  	}, nil
   192  }
   193  
   194  // ProviderType returns a provider type used by the current juju model.
   195  //
   196  // TODO(dimitern): We might be able to drop this, once we have machine
   197  // addresses implemented fully. See also LP bug 1221798.
   198  func (st *State) ProviderType() (string, error) {
   199  	var result params.StringResult
   200  	err := st.facade.FacadeCall("ProviderType", nil, &result)
   201  	if err != nil {
   202  		return "", err
   203  	}
   204  	if err := result.Error; err != nil {
   205  		return "", err
   206  	}
   207  	return result.Result, nil
   208  }
   209  
   210  // Charm returns the charm with the given URL.
   211  func (st *State) Charm(curl *charm.URL) (*Charm, error) {
   212  	if curl == nil {
   213  		return nil, fmt.Errorf("charm url cannot be nil")
   214  	}
   215  	return &Charm{
   216  		st:   st,
   217  		curl: curl,
   218  	}, nil
   219  }
   220  
   221  // Relation returns the existing relation with the given tag.
   222  func (st *State) Relation(relationTag names.RelationTag) (*Relation, error) {
   223  	result, err := st.relation(relationTag, st.unitTag)
   224  	if err != nil {
   225  		return nil, err
   226  	}
   227  	return &Relation{
   228  		id:        result.Id,
   229  		tag:       relationTag,
   230  		life:      result.Life,
   231  		suspended: result.Suspended,
   232  		st:        st,
   233  		otherApp:  result.OtherApplication,
   234  	}, nil
   235  }
   236  
   237  // Action returns the Action with the given tag.
   238  func (st *State) Action(tag names.ActionTag) (*Action, error) {
   239  	result, err := st.getOneAction(&tag)
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  	return &Action{
   244  		name:   result.Action.Name,
   245  		params: result.Action.Parameters,
   246  	}, nil
   247  }
   248  
   249  // ActionBegin marks an action as running.
   250  func (st *State) ActionBegin(tag names.ActionTag) error {
   251  	var outcome params.ErrorResults
   252  
   253  	args := params.Entities{
   254  		Entities: []params.Entity{
   255  			{Tag: tag.String()},
   256  		},
   257  	}
   258  
   259  	err := st.facade.FacadeCall("BeginActions", args, &outcome)
   260  	if err != nil {
   261  		return err
   262  	}
   263  	if len(outcome.Results) != 1 {
   264  		return fmt.Errorf("expected 1 result, got %d", len(outcome.Results))
   265  	}
   266  	result := outcome.Results[0]
   267  	if result.Error != nil {
   268  		return result.Error
   269  	}
   270  	return nil
   271  }
   272  
   273  // ActionFinish captures the structured output of an action.
   274  func (st *State) ActionFinish(tag names.ActionTag, status string, results map[string]interface{}, message string) error {
   275  	var outcome params.ErrorResults
   276  
   277  	args := params.ActionExecutionResults{
   278  		Results: []params.ActionExecutionResult{
   279  			{
   280  				ActionTag: tag.String(),
   281  				Status:    status,
   282  				Results:   results,
   283  				Message:   message,
   284  			},
   285  		},
   286  	}
   287  
   288  	err := st.facade.FacadeCall("FinishActions", args, &outcome)
   289  	if err != nil {
   290  		return err
   291  	}
   292  	if len(outcome.Results) != 1 {
   293  		return fmt.Errorf("expected 1 result, got %d", len(outcome.Results))
   294  	}
   295  	result := outcome.Results[0]
   296  	if result.Error != nil {
   297  		return result.Error
   298  	}
   299  	return nil
   300  }
   301  
   302  // RelationById returns the existing relation with the given id.
   303  func (st *State) RelationById(id int) (*Relation, error) {
   304  	var results params.RelationResults
   305  	args := params.RelationIds{
   306  		RelationIds: []int{id},
   307  	}
   308  	err := st.facade.FacadeCall("RelationById", args, &results)
   309  	if err != nil {
   310  		return nil, err
   311  	}
   312  	if len(results.Results) != 1 {
   313  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   314  	}
   315  	result := results.Results[0]
   316  	if err := result.Error; err != nil {
   317  		return nil, err
   318  	}
   319  	relationTag := names.NewRelationTag(result.Key)
   320  	return &Relation{
   321  		id:        result.Id,
   322  		tag:       relationTag,
   323  		life:      result.Life,
   324  		suspended: result.Suspended,
   325  		st:        st,
   326  		otherApp:  result.OtherApplication,
   327  	}, nil
   328  }
   329  
   330  // Model returns the model entity.
   331  func (st *State) Model() (*model.Model, error) {
   332  	var result params.ModelResult
   333  	err := st.facade.FacadeCall("CurrentModel", nil, &result)
   334  	if err != nil {
   335  		return nil, err
   336  	}
   337  	if err := result.Error; err != nil {
   338  		return nil, err
   339  	}
   340  	modelType := model.ModelType(result.Type)
   341  	if modelType == "" {
   342  		modelType = model.IAAS
   343  	}
   344  	return &model.Model{
   345  		Name:      result.Name,
   346  		UUID:      result.UUID,
   347  		ModelType: modelType,
   348  	}, nil
   349  }
   350  
   351  // AllMachinePorts returns all port ranges currently open on the given
   352  // machine, mapped to the tags of the unit that opened them and the
   353  // relation that applies.
   354  func (st *State) AllMachinePorts(machineTag names.MachineTag) (map[corenetwork.PortRange]params.RelationUnit, error) {
   355  	if st.BestAPIVersion() < 1 {
   356  		// AllMachinePorts() was introduced in UniterAPIV1.
   357  		return nil, errors.NotImplementedf("AllMachinePorts() (need V1+)")
   358  	}
   359  	var results params.MachinePortsResults
   360  	args := params.Entities{
   361  		Entities: []params.Entity{{Tag: machineTag.String()}},
   362  	}
   363  	err := st.facade.FacadeCall("AllMachinePorts", args, &results)
   364  	if err != nil {
   365  		return nil, err
   366  	}
   367  	if len(results.Results) != 1 {
   368  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   369  	}
   370  	result := results.Results[0]
   371  	if result.Error != nil {
   372  		return nil, result.Error
   373  	}
   374  	portsMap := make(map[corenetwork.PortRange]params.RelationUnit)
   375  	for _, ports := range result.Ports {
   376  		portRange := ports.PortRange.NetworkPortRange()
   377  		portsMap[portRange] = params.RelationUnit{
   378  			Unit:     ports.UnitTag,
   379  			Relation: ports.RelationTag,
   380  		}
   381  	}
   382  	return portsMap, nil
   383  }
   384  
   385  // WatchRelationUnits returns a watcher that notifies of changes to the
   386  // counterpart units in the relation for the given unit.
   387  func (st *State) WatchRelationUnits(
   388  	relationTag names.RelationTag,
   389  	unitTag names.UnitTag,
   390  ) (watcher.RelationUnitsWatcher, error) {
   391  	var results params.RelationUnitsWatchResults
   392  	args := params.RelationUnits{
   393  		RelationUnits: []params.RelationUnit{{
   394  			Relation: relationTag.String(),
   395  			Unit:     unitTag.String(),
   396  		}},
   397  	}
   398  	err := st.facade.FacadeCall("WatchRelationUnits", args, &results)
   399  	if err != nil {
   400  		return nil, err
   401  	}
   402  	if len(results.Results) != 1 {
   403  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   404  	}
   405  	result := results.Results[0]
   406  	if result.Error != nil {
   407  		return nil, result.Error
   408  	}
   409  	w := apiwatcher.NewRelationUnitsWatcher(st.facade.RawAPICaller(), result)
   410  	return w, nil
   411  }
   412  
   413  // ErrIfNotVersionFn returns a function which can be used to check for
   414  // the minimum supported version, and, if appropriate, generate an
   415  // error.
   416  func ErrIfNotVersionFn(minVersion int, bestAPIVersion int) func(string) error {
   417  	return func(fnName string) error {
   418  		if minVersion <= bestAPIVersion {
   419  			return nil
   420  		}
   421  		return errors.NotImplementedf("%s(...) requires v%d+", fnName, minVersion)
   422  	}
   423  }
   424  
   425  // SLALevel returns the SLA level set on the model.
   426  func (st *State) SLALevel() (string, error) {
   427  	if st.BestAPIVersion() < 5 {
   428  		return "unsupported", nil
   429  	}
   430  	var result params.StringResult
   431  	err := st.facade.FacadeCall("SLALevel", nil, &result)
   432  	if err != nil {
   433  		return "", errors.Trace(err)
   434  	}
   435  	if err := result.Error; err != nil {
   436  		return "", errors.Trace(err)
   437  	}
   438  	return result.Result, nil
   439  }
   440  
   441  // GoalState returns a GoalState struct with the charm's
   442  // peers and related units information.
   443  func (st *State) GoalState() (application.GoalState, error) {
   444  	var result params.GoalStateResults
   445  
   446  	gs := application.GoalState{}
   447  
   448  	args := params.Entities{
   449  		Entities: []params.Entity{
   450  			{Tag: st.unitTag.String()},
   451  		},
   452  	}
   453  
   454  	err := st.facade.FacadeCall("GoalStates", args, &result)
   455  	if err != nil {
   456  		return gs, err
   457  	}
   458  	if len(result.Results) != 1 {
   459  		return gs, errors.Errorf("expected 1 result, got %d", len(result.Results))
   460  	}
   461  	if err := result.Results[0].Error; err != nil {
   462  		return gs, err
   463  	}
   464  	gs = goalStateFromParams(result.Results[0].Result)
   465  	return gs, nil
   466  }
   467  
   468  func goalStateFromParams(paramsGoalState *params.GoalState) application.GoalState {
   469  	goalState := application.GoalState{}
   470  
   471  	copyUnits := func(units params.UnitsGoalState) application.UnitsGoalState {
   472  		copiedUnits := application.UnitsGoalState{}
   473  		for name, gs := range units {
   474  			copiedUnits[name] = application.GoalStateStatus{
   475  				Status: gs.Status,
   476  				Since:  gs.Since,
   477  			}
   478  		}
   479  		return copiedUnits
   480  	}
   481  
   482  	goalState.Units = copyUnits(paramsGoalState.Units)
   483  
   484  	if paramsGoalState.Relations != nil {
   485  		goalState.Relations = make(map[string]application.UnitsGoalState)
   486  		for relation, relationUnits := range paramsGoalState.Relations {
   487  			goalState.Relations[relation] = copyUnits(relationUnits)
   488  		}
   489  	}
   490  
   491  	return goalState
   492  }
   493  
   494  // SetPodSpec sets the pod spec of the specified application.
   495  func (st *State) SetPodSpec(appName string, spec string) error {
   496  	if !names.IsValidApplication(appName) {
   497  		return errors.NotValidf("application name %q", appName)
   498  	}
   499  	tag := names.NewApplicationTag(appName)
   500  	var result params.ErrorResults
   501  	args := params.SetPodSpecParams{
   502  		Specs: []params.EntityString{{
   503  			Tag:   tag.String(),
   504  			Value: spec,
   505  		}},
   506  	}
   507  	if err := st.facade.FacadeCall("SetPodSpec", args, &result); err != nil {
   508  		return errors.Trace(err)
   509  	}
   510  	return result.OneError()
   511  }
   512  
   513  // CloudSpec returns the cloud spec for the model that calling unit or
   514  // application resides in.
   515  // If the application has not been authorised to access its cloud spec,
   516  // then an authorisation error will be returned.
   517  func (st *State) CloudSpec() (*params.CloudSpec, error) {
   518  	var result params.CloudSpecResult
   519  
   520  	err := st.facade.FacadeCall("CloudSpec", nil, &result)
   521  	if err != nil {
   522  		return nil, err
   523  	}
   524  	if err := result.Error; err != nil {
   525  		return nil, err
   526  	}
   527  	return result.Result, nil
   528  }