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