github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/uniter/unit.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  	"github.com/juju/errors"
     8  	"gopkg.in/juju/charm.v6"
     9  	"gopkg.in/juju/names.v2"
    10  
    11  	"github.com/juju/juju/api/common"
    12  	apiwatcher "github.com/juju/juju/api/watcher"
    13  	"github.com/juju/juju/apiserver/params"
    14  	"github.com/juju/juju/core/model"
    15  	"github.com/juju/juju/core/status"
    16  	"github.com/juju/juju/core/watcher"
    17  )
    18  
    19  // Unit represents a juju unit as seen by a uniter worker.
    20  type Unit struct {
    21  	st           *State
    22  	tag          names.UnitTag
    23  	life         params.Life
    24  	resolvedMode params.ResolvedMode
    25  }
    26  
    27  // Tag returns the unit's tag.
    28  func (u *Unit) Tag() names.UnitTag {
    29  	return u.tag
    30  }
    31  
    32  // Name returns the name of the unit.
    33  func (u *Unit) Name() string {
    34  	return u.tag.Id()
    35  }
    36  
    37  // String returns the unit as a string.
    38  func (u *Unit) String() string {
    39  	return u.Name()
    40  }
    41  
    42  // Life returns the unit's lifecycle value.
    43  func (u *Unit) Life() params.Life {
    44  	return u.life
    45  }
    46  
    47  // Resolved returns the unit's resolved mode value.
    48  func (u *Unit) Resolved() params.ResolvedMode {
    49  	return u.resolvedMode
    50  }
    51  
    52  // Refresh updates the cached local copy of the unit's data.
    53  func (u *Unit) Refresh() error {
    54  	var results params.UnitRefreshResults
    55  	args := params.Entities{
    56  		Entities: []params.Entity{
    57  			{Tag: u.tag.String()},
    58  		},
    59  	}
    60  	err := u.st.facade.FacadeCall("Refresh", args, &results)
    61  	if err != nil {
    62  		return errors.Trace(err)
    63  	}
    64  	if len(results.Results) != 1 {
    65  		return errors.Errorf("expected 1 result, got %d", len(results.Results))
    66  	}
    67  	result := results.Results[0]
    68  	if result.Error != nil {
    69  		return errors.Trace(result.Error)
    70  	}
    71  
    72  	u.life = result.Life
    73  	u.resolvedMode = result.Resolved
    74  	return nil
    75  }
    76  
    77  // SetUnitStatus sets the status of the unit.
    78  func (u *Unit) SetUnitStatus(unitStatus status.Status, info string, data map[string]interface{}) error {
    79  	if u.st.facade.BestAPIVersion() < 2 {
    80  		return errors.NotImplementedf("SetUnitStatus")
    81  	}
    82  	var result params.ErrorResults
    83  	args := params.SetStatus{
    84  		Entities: []params.EntityStatusArgs{
    85  			{Tag: u.tag.String(), Status: unitStatus.String(), Info: info, Data: data},
    86  		},
    87  	}
    88  	err := u.st.facade.FacadeCall("SetUnitStatus", args, &result)
    89  	if err != nil {
    90  		return errors.Trace(err)
    91  	}
    92  	return result.OneError()
    93  }
    94  
    95  // UnitStatus gets the status details of the unit.
    96  func (u *Unit) UnitStatus() (params.StatusResult, error) {
    97  	var results params.StatusResults
    98  	args := params.Entities{
    99  		Entities: []params.Entity{
   100  			{Tag: u.tag.String()},
   101  		},
   102  	}
   103  	err := u.st.facade.FacadeCall("UnitStatus", args, &results)
   104  	if err != nil {
   105  		return params.StatusResult{}, errors.Trace(err)
   106  	}
   107  	if len(results.Results) != 1 {
   108  		return params.StatusResult{}, errors.Errorf("expected 1 result, got %d", len(results.Results))
   109  	}
   110  	result := results.Results[0]
   111  	if result.Error != nil {
   112  		return params.StatusResult{}, result.Error
   113  	}
   114  	return result, nil
   115  }
   116  
   117  // SetAgentStatus sets the status of the unit agent.
   118  func (u *Unit) SetAgentStatus(agentStatus status.Status, info string, data map[string]interface{}) error {
   119  	var result params.ErrorResults
   120  	args := params.SetStatus{
   121  		Entities: []params.EntityStatusArgs{
   122  			{Tag: u.tag.String(), Status: agentStatus.String(), Info: info, Data: data},
   123  		},
   124  	}
   125  	setStatusFacadeCall := "SetAgentStatus"
   126  	if u.st.facade.BestAPIVersion() < 2 {
   127  		setStatusFacadeCall = "SetStatus"
   128  	}
   129  	err := u.st.facade.FacadeCall(setStatusFacadeCall, args, &result)
   130  	if err != nil {
   131  		return err
   132  	}
   133  	return result.OneError()
   134  }
   135  
   136  // AddMetrics adds the metrics for the unit.
   137  func (u *Unit) AddMetrics(metrics []params.Metric) error {
   138  	var result params.ErrorResults
   139  	args := params.MetricsParams{
   140  		Metrics: []params.MetricsParam{{
   141  			Tag:     u.tag.String(),
   142  			Metrics: metrics,
   143  		}},
   144  	}
   145  	err := u.st.facade.FacadeCall("AddMetrics", args, &result)
   146  	if err != nil {
   147  		return errors.Annotate(err, "unable to add metric")
   148  	}
   149  	return result.OneError()
   150  }
   151  
   152  // AddMetricsBatches makes an api call to the uniter requesting it to store metrics batches in state.
   153  func (u *Unit) AddMetricBatches(batches []params.MetricBatch) (map[string]error, error) {
   154  	p := params.MetricBatchParams{
   155  		Batches: make([]params.MetricBatchParam, len(batches)),
   156  	}
   157  
   158  	batchResults := make(map[string]error, len(batches))
   159  
   160  	for i, batch := range batches {
   161  		p.Batches[i].Tag = u.tag.String()
   162  		p.Batches[i].Batch = batch
   163  
   164  		batchResults[batch.UUID] = nil
   165  	}
   166  	results := new(params.ErrorResults)
   167  	err := u.st.facade.FacadeCall("AddMetricBatches", p, results)
   168  	if err != nil {
   169  		return nil, errors.Annotate(err, "failed to send metric batches")
   170  	}
   171  	for i, result := range results.Results {
   172  		batchResults[batches[i].UUID] = result.Error
   173  	}
   174  	return batchResults, nil
   175  }
   176  
   177  // EnsureDead sets the unit lifecycle to Dead if it is Alive or
   178  // Dying. It does nothing otherwise.
   179  func (u *Unit) EnsureDead() error {
   180  	var result params.ErrorResults
   181  	args := params.Entities{
   182  		Entities: []params.Entity{{Tag: u.tag.String()}},
   183  	}
   184  	err := u.st.facade.FacadeCall("EnsureDead", args, &result)
   185  	if err != nil {
   186  		return err
   187  	}
   188  	return result.OneError()
   189  }
   190  
   191  // Watch returns a watcher for observing changes to the unit.
   192  func (u *Unit) Watch() (watcher.NotifyWatcher, error) {
   193  	return common.Watch(u.st.facade, "Watch", u.tag)
   194  }
   195  
   196  // WatchRelations returns a StringsWatcher that notifies of changes to
   197  // the lifecycles of relations involving u.
   198  func (u *Unit) WatchRelations() (watcher.StringsWatcher, error) {
   199  	var results params.StringsWatchResults
   200  	args := params.Entities{
   201  		Entities: []params.Entity{{Tag: u.tag.String()}},
   202  	}
   203  	err := u.st.facade.FacadeCall("WatchUnitRelations", args, &results)
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  	if len(results.Results) != 1 {
   208  		return nil, errors.Errorf("expected 1 result, got %d", len(results.Results))
   209  	}
   210  	result := results.Results[0]
   211  	if result.Error != nil {
   212  		return nil, result.Error
   213  	}
   214  	w := apiwatcher.NewStringsWatcher(u.st.facade.RawAPICaller(), result)
   215  	return w, nil
   216  }
   217  
   218  // Application returns the unit's application.
   219  func (u *Unit) Application() (*Application, error) {
   220  	application := &Application{
   221  		st:  u.st,
   222  		tag: u.ApplicationTag(),
   223  	}
   224  	// Call Refresh() immediately to get the up-to-date
   225  	// life and other needed locally cached fields.
   226  	err := application.Refresh()
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  	return application, nil
   231  }
   232  
   233  // ConfigSettings returns the complete set of application charm config settings
   234  // available to the unit. Unset values will be replaced with the default
   235  // value for the associated option, and may thus be nil when no default is
   236  // specified.
   237  func (u *Unit) ConfigSettings() (charm.Settings, error) {
   238  	var results params.ConfigSettingsResults
   239  	args := params.Entities{
   240  		Entities: []params.Entity{{Tag: u.tag.String()}},
   241  	}
   242  	err := u.st.facade.FacadeCall("ConfigSettings", args, &results)
   243  	if err != nil {
   244  		return nil, err
   245  	}
   246  	if len(results.Results) != 1 {
   247  		return nil, errors.Errorf("expected 1 result, got %d", len(results.Results))
   248  	}
   249  	result := results.Results[0]
   250  	if result.Error != nil {
   251  		return nil, result.Error
   252  	}
   253  	return charm.Settings(result.Settings), nil
   254  }
   255  
   256  // ApplicationName returns the application name.
   257  func (u *Unit) ApplicationName() string {
   258  	application, err := names.UnitApplication(u.Name())
   259  	if err != nil {
   260  		panic(err)
   261  	}
   262  	return application
   263  }
   264  
   265  // ApplicationTag returns the application tag.
   266  func (u *Unit) ApplicationTag() names.ApplicationTag {
   267  	return names.NewApplicationTag(u.ApplicationName())
   268  }
   269  
   270  // Destroy, when called on a Alive unit, advances its lifecycle as far as
   271  // possible; it otherwise has no effect. In most situations, the unit's
   272  // life is just set to Dying; but if a principal unit that is not assigned
   273  // to a provisioned machine is Destroyed, it will be removed from state
   274  // directly.
   275  func (u *Unit) Destroy() error {
   276  	var result params.ErrorResults
   277  	args := params.Entities{
   278  		Entities: []params.Entity{{Tag: u.tag.String()}},
   279  	}
   280  	err := u.st.facade.FacadeCall("Destroy", args, &result)
   281  	if err != nil {
   282  		return err
   283  	}
   284  	return result.OneError()
   285  }
   286  
   287  // DestroyAllSubordinates destroys all subordinates of the unit.
   288  func (u *Unit) DestroyAllSubordinates() error {
   289  	var result params.ErrorResults
   290  	args := params.Entities{
   291  		Entities: []params.Entity{{Tag: u.tag.String()}},
   292  	}
   293  	err := u.st.facade.FacadeCall("DestroyAllSubordinates", args, &result)
   294  	if err != nil {
   295  		return err
   296  	}
   297  	return result.OneError()
   298  }
   299  
   300  // AssignedMachine returns the unit's assigned machine tag or an error
   301  // satisfying params.IsCodeNotAssigned when the unit has no assigned
   302  // machine..
   303  func (u *Unit) AssignedMachine() (names.MachineTag, error) {
   304  	if u.st.BestAPIVersion() < 1 {
   305  		return names.MachineTag{}, errors.NotImplementedf("unit.AssignedMachine() (need V1+)")
   306  	}
   307  	var invalidTag names.MachineTag
   308  	var results params.StringResults
   309  	args := params.Entities{
   310  		Entities: []params.Entity{{Tag: u.tag.String()}},
   311  	}
   312  	err := u.st.facade.FacadeCall("AssignedMachine", args, &results)
   313  	if err != nil {
   314  		return invalidTag, err
   315  	}
   316  	if len(results.Results) != 1 {
   317  		return invalidTag, errors.Errorf("expected 1 result, got %d", len(results.Results))
   318  	}
   319  	result := results.Results[0]
   320  	if result.Error != nil {
   321  		return invalidTag, result.Error
   322  	}
   323  	return names.ParseMachineTag(result.Result)
   324  }
   325  
   326  // PrincipalName returns the principal unit name and true for subordinates.
   327  // For principal units the function returns "" and false.
   328  //
   329  // NOTE: This differs from state.Unit.PrincipalName() by returning an
   330  // error as well, because it needs to make an API call.
   331  func (u *Unit) PrincipalName() (string, bool, error) {
   332  	var results params.StringBoolResults
   333  	args := params.Entities{
   334  		Entities: []params.Entity{{Tag: u.tag.String()}},
   335  	}
   336  	err := u.st.facade.FacadeCall("GetPrincipal", args, &results)
   337  	if err != nil {
   338  		return "", false, err
   339  	}
   340  	if len(results.Results) != 1 {
   341  		return "", false, errors.Errorf("expected 1 result, got %d", len(results.Results))
   342  	}
   343  	result := results.Results[0]
   344  	if result.Error != nil {
   345  		return "", false, result.Error
   346  	}
   347  	var unitName string
   348  	if result.Ok {
   349  		unitTag, err := names.ParseUnitTag(result.Result)
   350  		if err != nil {
   351  			return "", false, err
   352  		}
   353  		unitName = unitTag.Id()
   354  	}
   355  	return unitName, result.Ok, nil
   356  }
   357  
   358  // HasSubordinates returns the tags of any subordinate units.
   359  func (u *Unit) HasSubordinates() (bool, error) {
   360  	var results params.BoolResults
   361  	args := params.Entities{
   362  		Entities: []params.Entity{{Tag: u.tag.String()}},
   363  	}
   364  	err := u.st.facade.FacadeCall("HasSubordinates", args, &results)
   365  	if err != nil {
   366  		return false, err
   367  	}
   368  	if len(results.Results) != 1 {
   369  		return false, errors.Errorf("expected 1 result, got %d", len(results.Results))
   370  	}
   371  	result := results.Results[0]
   372  	if result.Error != nil {
   373  		return false, result.Error
   374  	}
   375  	return result.Result, nil
   376  }
   377  
   378  // PublicAddress returns the public address of the unit and whether it
   379  // is valid.
   380  //
   381  // NOTE: This differs from state.Unit.PublicAddres() by returning
   382  // an error instead of a bool, because it needs to make an API call.
   383  //
   384  // TODO(dimitern): We might be able to drop this, once we have machine
   385  // addresses implemented fully. See also LP bug 1221798.
   386  func (u *Unit) PublicAddress() (string, error) {
   387  	var results params.StringResults
   388  	args := params.Entities{
   389  		Entities: []params.Entity{{Tag: u.tag.String()}},
   390  	}
   391  	err := u.st.facade.FacadeCall("PublicAddress", args, &results)
   392  	if err != nil {
   393  		return "", err
   394  	}
   395  	if len(results.Results) != 1 {
   396  		return "", errors.Errorf("expected 1 result, got %d", len(results.Results))
   397  	}
   398  	result := results.Results[0]
   399  	if result.Error != nil {
   400  		return "", result.Error
   401  	}
   402  	return result.Result, nil
   403  }
   404  
   405  // PrivateAddress returns the private address of the unit and whether
   406  // it is valid.
   407  //
   408  // NOTE: This differs from state.Unit.PrivateAddress() by returning
   409  // an error instead of a bool, because it needs to make an API call.
   410  //
   411  // TODO(dimitern): We might be able to drop this, once we have machine
   412  // addresses implemented fully. See also LP bug 1221798.
   413  func (u *Unit) PrivateAddress() (string, error) {
   414  	var results params.StringResults
   415  	args := params.Entities{
   416  		Entities: []params.Entity{{Tag: u.tag.String()}},
   417  	}
   418  	err := u.st.facade.FacadeCall("PrivateAddress", args, &results)
   419  	if err != nil {
   420  		return "", err
   421  	}
   422  	if len(results.Results) != 1 {
   423  		return "", errors.Errorf("expected 1 result, got %d", len(results.Results))
   424  	}
   425  	result := results.Results[0]
   426  	if result.Error != nil {
   427  		return "", result.Error
   428  	}
   429  	return result.Result, nil
   430  }
   431  
   432  // AvailabilityZone returns the availability zone of the unit.
   433  func (u *Unit) AvailabilityZone() (string, error) {
   434  	var results params.StringResults
   435  	args := params.Entities{
   436  		Entities: []params.Entity{{Tag: u.tag.String()}},
   437  	}
   438  	if err := u.st.facade.FacadeCall("AvailabilityZone", args, &results); err != nil {
   439  		return "", errors.Trace(err)
   440  	}
   441  	if len(results.Results) != 1 {
   442  		return "", errors.Errorf("expected 1 result, got %d", len(results.Results))
   443  	}
   444  	result := results.Results[0]
   445  	if result.Error != nil {
   446  		return "", errors.Trace(result.Error)
   447  	}
   448  	return result.Result, nil
   449  }
   450  
   451  // OpenPorts sets the policy of the port range with protocol to be
   452  // opened.
   453  func (u *Unit) OpenPorts(protocol string, fromPort, toPort int) error {
   454  	var result params.ErrorResults
   455  	args := params.EntitiesPortRanges{
   456  		Entities: []params.EntityPortRange{{
   457  			Tag:      u.tag.String(),
   458  			Protocol: protocol,
   459  			FromPort: fromPort,
   460  			ToPort:   toPort,
   461  		}},
   462  	}
   463  	err := u.st.facade.FacadeCall("OpenPorts", args, &result)
   464  	if err != nil {
   465  		return err
   466  	}
   467  	return result.OneError()
   468  }
   469  
   470  // ClosePorts sets the policy of the port range with protocol to be
   471  // closed.
   472  func (u *Unit) ClosePorts(protocol string, fromPort, toPort int) error {
   473  	var result params.ErrorResults
   474  	args := params.EntitiesPortRanges{
   475  		Entities: []params.EntityPortRange{{
   476  			Tag:      u.tag.String(),
   477  			Protocol: protocol,
   478  			FromPort: fromPort,
   479  			ToPort:   toPort,
   480  		}},
   481  	}
   482  	err := u.st.facade.FacadeCall("ClosePorts", args, &result)
   483  	if err != nil {
   484  		return err
   485  	}
   486  	return result.OneError()
   487  }
   488  
   489  var ErrNoCharmURLSet = errors.New("unit has no charm url set")
   490  
   491  // CharmURL returns the charm URL this unit is currently using.
   492  //
   493  // NOTE: This differs from state.Unit.CharmURL() by returning
   494  // an error instead of a bool, because it needs to make an API call.
   495  func (u *Unit) CharmURL() (*charm.URL, error) {
   496  	var results params.StringBoolResults
   497  	args := params.Entities{
   498  		Entities: []params.Entity{{Tag: u.tag.String()}},
   499  	}
   500  	err := u.st.facade.FacadeCall("CharmURL", args, &results)
   501  	if err != nil {
   502  		return nil, err
   503  	}
   504  	if len(results.Results) != 1 {
   505  		return nil, errors.Errorf("expected 1 result, got %d", len(results.Results))
   506  	}
   507  	result := results.Results[0]
   508  	if result.Error != nil {
   509  		return nil, result.Error
   510  	}
   511  	if result.Result != "" {
   512  		curl, err := charm.ParseURL(result.Result)
   513  		if err != nil {
   514  			return nil, err
   515  		}
   516  		return curl, nil
   517  	}
   518  	return nil, ErrNoCharmURLSet
   519  }
   520  
   521  // SetCharmURL marks the unit as currently using the supplied charm URL.
   522  // An error will be returned if the unit is dead, or the charm URL not known.
   523  func (u *Unit) SetCharmURL(curl *charm.URL) error {
   524  	if curl == nil {
   525  		return errors.Errorf("charm URL cannot be nil")
   526  	}
   527  	var result params.ErrorResults
   528  	args := params.EntitiesCharmURL{
   529  		Entities: []params.EntityCharmURL{
   530  			{Tag: u.tag.String(), CharmURL: curl.String()},
   531  		},
   532  	}
   533  	err := u.st.facade.FacadeCall("SetCharmURL", args, &result)
   534  	if err != nil {
   535  		return err
   536  	}
   537  	return result.OneError()
   538  }
   539  
   540  // ClearResolved removes any resolved setting on the unit.
   541  func (u *Unit) ClearResolved() error {
   542  	var result params.ErrorResults
   543  	args := params.Entities{
   544  		Entities: []params.Entity{{Tag: u.tag.String()}},
   545  	}
   546  	err := u.st.facade.FacadeCall("ClearResolved", args, &result)
   547  	if err != nil {
   548  		return err
   549  	}
   550  	return result.OneError()
   551  }
   552  
   553  // WatchConfigSettingsHash returns a watcher for observing changes to
   554  // the unit's charm configuration settings (with a hash of the
   555  // settings content so we can determine whether it has changed since
   556  // it was last seen by the uniter). The unit must have a charm URL set
   557  // before this method is called, and the returned watcher will be
   558  // valid only while the unit's charm URL is not changed.
   559  func (u *Unit) WatchConfigSettingsHash() (watcher.StringsWatcher, error) {
   560  	return getHashWatcher(u, "WatchConfigSettingsHash")
   561  }
   562  
   563  // WatchTrustConfigSettingsHash returns a watcher for observing changes to
   564  // the unit's application configuration settings (with a hash of the
   565  // settings content so we can determine whether it has changed since
   566  // it was last seen by the uniter).
   567  func (u *Unit) WatchTrustConfigSettingsHash() (watcher.StringsWatcher, error) {
   568  	return getHashWatcher(u, "WatchTrustConfigSettingsHash")
   569  }
   570  
   571  func getHashWatcher(u *Unit, methodName string) (watcher.StringsWatcher, error) {
   572  	var results params.StringsWatchResults
   573  	args := params.Entities{
   574  		Entities: []params.Entity{{Tag: u.tag.String()}},
   575  	}
   576  	err := u.st.facade.FacadeCall(methodName, args, &results)
   577  	if err != nil {
   578  		return nil, err
   579  	}
   580  	if len(results.Results) != 1 {
   581  		return nil, errors.Errorf("expected 1 result, got %d", len(results.Results))
   582  	}
   583  	result := results.Results[0]
   584  	if result.Error != nil {
   585  		return nil, result.Error
   586  	}
   587  	w := apiwatcher.NewStringsWatcher(u.st.facade.RawAPICaller(), result)
   588  	return w, nil
   589  }
   590  
   591  // WatchAddressesHash returns a watcher for observing changes to the
   592  // hash of the unit's addresses.
   593  // For IAAS models, the unit must be assigned to a machine before
   594  // this method is called, and the returned watcher will be valid
   595  // only while the unit's assigned machine is not changed.
   596  // For CAAS models, the watcher observes changes to the address
   597  // of the pod associated with the unit.
   598  func (u *Unit) WatchAddressesHash() (watcher.StringsWatcher, error) {
   599  	return getHashWatcher(u, "WatchUnitAddressesHash")
   600  }
   601  
   602  // WatchActionNotifications returns a StringsWatcher for observing the
   603  // ids of Actions added to the Unit. The initial event will contain the
   604  // ids of any Actions pending at the time the Watcher is made.
   605  func (u *Unit) WatchActionNotifications() (watcher.StringsWatcher, error) {
   606  	var results params.StringsWatchResults
   607  	args := params.Entities{
   608  		Entities: []params.Entity{{Tag: u.tag.String()}},
   609  	}
   610  	err := u.st.facade.FacadeCall("WatchActionNotifications", args, &results)
   611  	if err != nil {
   612  		return nil, err
   613  	}
   614  	if len(results.Results) != 1 {
   615  		return nil, errors.Errorf("expected 1 result, got %d", len(results.Results))
   616  	}
   617  	result := results.Results[0]
   618  	if result.Error != nil {
   619  		return nil, result.Error
   620  	}
   621  	w := apiwatcher.NewStringsWatcher(u.st.facade.RawAPICaller(), result)
   622  	return w, nil
   623  }
   624  
   625  // WatchUpgradeSeriesNotifications returns a NotifyWatcher for observing the
   626  // state of a series upgrade.
   627  func (u *Unit) WatchUpgradeSeriesNotifications() (watcher.NotifyWatcher, error) {
   628  	return u.st.WatchUpgradeSeriesNotifications()
   629  }
   630  
   631  // UpgradeSeriesStatus returns the upgrade series status of a unit from remote state
   632  func (u *Unit) UpgradeSeriesStatus() (model.UpgradeSeriesStatus, error) {
   633  	res, err := u.st.UpgradeSeriesUnitStatus()
   634  	if err != nil {
   635  		return "", errors.Trace(err)
   636  	}
   637  	if len(res) != 1 {
   638  		return "", errors.Errorf("expected 1 result, got %d", len(res))
   639  	}
   640  	return res[0], nil
   641  }
   642  
   643  // SetUpgradeSeriesStatus sets the upgrade series status of the unit in the remote state
   644  func (u *Unit) SetUpgradeSeriesStatus(status model.UpgradeSeriesStatus, reason string) error {
   645  	return u.st.SetUpgradeSeriesUnitStatus(status, reason)
   646  }
   647  
   648  // WatchLXDProfileUpgradeNotifications returns a StringsWatcher for observing the
   649  // state of a lxd profile upgrade
   650  func (u *Unit) WatchLXDProfileUpgradeNotifications() (watcher.StringsWatcher, error) {
   651  	return u.st.WatchLXDProfileUpgradeNotifications(u.ApplicationName())
   652  }
   653  
   654  // RemoveUpgradeCharmProfileData removes the upgrade charm profile data
   655  func (u *Unit) RemoveUpgradeCharmProfileData() error {
   656  	return u.st.RemoveUpgradeCharmProfileData()
   657  }
   658  
   659  // RequestReboot sets the reboot flag for its machine agent
   660  func (u *Unit) RequestReboot() error {
   661  	machineId, err := u.AssignedMachine()
   662  	if err != nil {
   663  		return err
   664  	}
   665  	var result params.ErrorResults
   666  	args := params.Entities{
   667  		Entities: []params.Entity{{Tag: machineId.String()}},
   668  	}
   669  	err = u.st.facade.FacadeCall("RequestReboot", args, &result)
   670  	if err != nil {
   671  		return err
   672  	}
   673  	return result.OneError()
   674  }
   675  
   676  // RelationStatus holds information about a relation's scope and status.
   677  type RelationStatus struct {
   678  	// Tag is the relation tag.
   679  	Tag names.RelationTag
   680  
   681  	// Suspended is true if the relation is suspended.
   682  	Suspended bool
   683  
   684  	// InScope is true if the relation unit is in scope.
   685  	InScope bool
   686  }
   687  
   688  // RelationsInScope returns the tags of the relations the unit has joined
   689  // and entered scope, or the relation is suspended.
   690  func (u *Unit) RelationsStatus() ([]RelationStatus, error) {
   691  	args := params.Entities{
   692  		Entities: []params.Entity{{Tag: u.tag.String()}},
   693  	}
   694  	var results params.RelationUnitStatusResults
   695  	err := u.st.facade.FacadeCall("RelationsStatus", args, &results)
   696  	if err != nil {
   697  		return nil, err
   698  	}
   699  	if len(results.Results) != 1 {
   700  		return nil, errors.Errorf("expected 1 result, got %d", len(results.Results))
   701  	}
   702  	result := results.Results[0]
   703  	if result.Error != nil {
   704  		return nil, result.Error
   705  	}
   706  	var statusResult []RelationStatus
   707  	for _, result := range result.RelationResults {
   708  		tag, err := names.ParseRelationTag(result.RelationTag)
   709  		if err != nil {
   710  			return nil, err
   711  		}
   712  		statusResult = append(statusResult, RelationStatus{
   713  			Tag:       tag,
   714  			InScope:   result.InScope,
   715  			Suspended: result.Suspended,
   716  		})
   717  	}
   718  	return statusResult, nil
   719  }
   720  
   721  // MeterStatus returns the meter status of the unit.
   722  func (u *Unit) MeterStatus() (statusCode, statusInfo string, rErr error) {
   723  	var results params.MeterStatusResults
   724  	args := params.Entities{
   725  		Entities: []params.Entity{{Tag: u.tag.String()}},
   726  	}
   727  	err := u.st.facade.FacadeCall("GetMeterStatus", args, &results)
   728  	if err != nil {
   729  		return "", "", errors.Trace(err)
   730  	}
   731  	if len(results.Results) != 1 {
   732  		return "", "", errors.Errorf("expected 1 result, got %d", len(results.Results))
   733  	}
   734  	result := results.Results[0]
   735  	if result.Error != nil {
   736  		return "", "", errors.Trace(result.Error)
   737  	}
   738  	return result.Code, result.Info, nil
   739  }
   740  
   741  // WatchMeterStatus returns a watcher for observing changes to the
   742  // unit's meter status.
   743  func (u *Unit) WatchMeterStatus() (watcher.NotifyWatcher, error) {
   744  	var results params.NotifyWatchResults
   745  	args := params.Entities{
   746  		Entities: []params.Entity{{Tag: u.tag.String()}},
   747  	}
   748  	err := u.st.facade.FacadeCall("WatchMeterStatus", args, &results)
   749  	if err != nil {
   750  		return nil, err
   751  	}
   752  	if len(results.Results) != 1 {
   753  		return nil, errors.Errorf("expected 1 result, got %d", len(results.Results))
   754  	}
   755  	result := results.Results[0]
   756  	if result.Error != nil {
   757  		return nil, result.Error
   758  	}
   759  	w := apiwatcher.NewNotifyWatcher(u.st.facade.RawAPICaller(), result)
   760  	return w, nil
   761  }
   762  
   763  // WatchStorage returns a watcher for observing changes to the
   764  // unit's storage attachments.
   765  func (u *Unit) WatchStorage() (watcher.StringsWatcher, error) {
   766  	return u.st.WatchUnitStorageAttachments(u.tag)
   767  }
   768  
   769  // AddStorage adds desired storage instances to a unit.
   770  func (u *Unit) AddStorage(constraints map[string][]params.StorageConstraints) error {
   771  	if u.st.facade.BestAPIVersion() < 2 {
   772  		return errors.NotImplementedf("AddStorage() (need V2+)")
   773  	}
   774  
   775  	all := make([]params.StorageAddParams, 0, len(constraints))
   776  	for storage, cons := range constraints {
   777  		for _, one := range cons {
   778  			all = append(all, params.StorageAddParams{
   779  				UnitTag:     u.Tag().String(),
   780  				StorageName: storage,
   781  				Constraints: one,
   782  			})
   783  		}
   784  	}
   785  
   786  	args := params.StoragesAddParams{Storages: all}
   787  	var results params.ErrorResults
   788  	err := u.st.facade.FacadeCall("AddUnitStorage", args, &results)
   789  	if err != nil {
   790  		return err
   791  	}
   792  
   793  	return results.Combine()
   794  }
   795  
   796  // NetworkInfo returns network interfaces/addresses for specified bindings.
   797  func (u *Unit) NetworkInfo(bindings []string, relationId *int) (map[string]params.NetworkInfoResult, error) {
   798  	var results params.NetworkInfoResults
   799  	args := params.NetworkInfoParams{
   800  		Unit:       u.tag.String(),
   801  		Bindings:   bindings,
   802  		RelationId: relationId,
   803  	}
   804  
   805  	err := u.st.facade.FacadeCall("NetworkInfo", args, &results)
   806  	if err != nil {
   807  		return nil, errors.Trace(err)
   808  	}
   809  
   810  	return results.Results, nil
   811  }