github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/state/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  	"errors"
     8  	"fmt"
     9  
    10  	"github.com/juju/names"
    11  
    12  	"github.com/juju/juju/charm"
    13  	"github.com/juju/juju/state/api/params"
    14  	"github.com/juju/juju/state/api/watcher"
    15  )
    16  
    17  // Unit represents a juju unit as seen by a uniter worker.
    18  type Unit struct {
    19  	st   *State
    20  	tag  string
    21  	life params.Life
    22  }
    23  
    24  // Tag returns the unit's tag.
    25  func (u *Unit) Tag() string {
    26  	return u.tag
    27  }
    28  
    29  // Name returns the name of the unit.
    30  func (u *Unit) Name() string {
    31  	_, unitName, err := names.ParseTag(u.tag, names.UnitTagKind)
    32  	if err != nil {
    33  		panic(fmt.Sprintf("%q is not a valid unit tag", u.tag))
    34  	}
    35  	return unitName
    36  }
    37  
    38  // String returns the unit as a string.
    39  func (u *Unit) String() string {
    40  	return u.Name()
    41  }
    42  
    43  // Life returns the unit's lifecycle value.
    44  func (u *Unit) Life() params.Life {
    45  	return u.life
    46  }
    47  
    48  // Refresh updates the cached local copy of the unit's data.
    49  func (u *Unit) Refresh() error {
    50  	life, err := u.st.life(u.tag)
    51  	if err != nil {
    52  		return err
    53  	}
    54  	u.life = life
    55  	return nil
    56  }
    57  
    58  // SetStatus sets the status of the unit.
    59  func (u *Unit) SetStatus(status params.Status, info string, data params.StatusData) error {
    60  	var result params.ErrorResults
    61  	args := params.SetStatus{
    62  		Entities: []params.EntityStatus{
    63  			{Tag: u.tag, Status: status, Info: info, Data: data},
    64  		},
    65  	}
    66  	err := u.st.call("SetStatus", args, &result)
    67  	if err != nil {
    68  		return err
    69  	}
    70  	return result.OneError()
    71  }
    72  
    73  // EnsureDead sets the unit lifecycle to Dead if it is Alive or
    74  // Dying. It does nothing otherwise.
    75  func (u *Unit) EnsureDead() error {
    76  	var result params.ErrorResults
    77  	args := params.Entities{
    78  		Entities: []params.Entity{{Tag: u.tag}},
    79  	}
    80  	err := u.st.call("EnsureDead", args, &result)
    81  	if err != nil {
    82  		return err
    83  	}
    84  	return result.OneError()
    85  }
    86  
    87  // Watch returns a watcher for observing changes to the unit.
    88  func (u *Unit) Watch() (watcher.NotifyWatcher, error) {
    89  	var results params.NotifyWatchResults
    90  	args := params.Entities{
    91  		Entities: []params.Entity{{Tag: u.tag}},
    92  	}
    93  	err := u.st.call("Watch", args, &results)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	if len(results.Results) != 1 {
    98  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
    99  	}
   100  	result := results.Results[0]
   101  	if result.Error != nil {
   102  		return nil, result.Error
   103  	}
   104  	w := watcher.NewNotifyWatcher(u.st.caller, result)
   105  	return w, nil
   106  }
   107  
   108  // Service returns the service.
   109  func (u *Unit) Service() (*Service, error) {
   110  	serviceTag := names.ServiceTag(u.ServiceName())
   111  	service := &Service{
   112  		st:  u.st,
   113  		tag: serviceTag,
   114  	}
   115  	// Call Refresh() immediately to get the up-to-date
   116  	// life and other needed locally cached fields.
   117  	err := service.Refresh()
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  	return service, nil
   122  }
   123  
   124  // ConfigSettings returns the complete set of service charm config settings
   125  // available to the unit. Unset values will be replaced with the default
   126  // value for the associated option, and may thus be nil when no default is
   127  // specified.
   128  func (u *Unit) ConfigSettings() (charm.Settings, error) {
   129  	var results params.ConfigSettingsResults
   130  	args := params.Entities{
   131  		Entities: []params.Entity{{Tag: u.tag}},
   132  	}
   133  	err := u.st.call("ConfigSettings", args, &results)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	if len(results.Results) != 1 {
   138  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   139  	}
   140  	result := results.Results[0]
   141  	if result.Error != nil {
   142  		return nil, result.Error
   143  	}
   144  	return charm.Settings(result.Settings), nil
   145  }
   146  
   147  // ServiceName returns the service name.
   148  func (u *Unit) ServiceName() string {
   149  	return names.UnitService(u.Name())
   150  }
   151  
   152  // ServiceTag returns the service tag.
   153  func (u *Unit) ServiceTag() string {
   154  	return names.ServiceTag(u.ServiceName())
   155  }
   156  
   157  // Destroy, when called on a Alive unit, advances its lifecycle as far as
   158  // possible; it otherwise has no effect. In most situations, the unit's
   159  // life is just set to Dying; but if a principal unit that is not assigned
   160  // to a provisioned machine is Destroyed, it will be removed from state
   161  // directly.
   162  func (u *Unit) Destroy() error {
   163  	var result params.ErrorResults
   164  	args := params.Entities{
   165  		Entities: []params.Entity{{Tag: u.tag}},
   166  	}
   167  	err := u.st.call("Destroy", args, &result)
   168  	if err != nil {
   169  		return err
   170  	}
   171  	return result.OneError()
   172  }
   173  
   174  // DestroyAllSubordinates destroys all subordinates of the unit.
   175  func (u *Unit) DestroyAllSubordinates() error {
   176  	var result params.ErrorResults
   177  	args := params.Entities{
   178  		Entities: []params.Entity{{Tag: u.tag}},
   179  	}
   180  	err := u.st.call("DestroyAllSubordinates", args, &result)
   181  	if err != nil {
   182  		return err
   183  	}
   184  	return result.OneError()
   185  }
   186  
   187  // Resolved returns the resolved mode for the unit.
   188  //
   189  // NOTE: This differs from state.Unit.Resolved() by returning an
   190  // error as well, because it needs to make an API call
   191  func (u *Unit) Resolved() (params.ResolvedMode, error) {
   192  	var results params.ResolvedModeResults
   193  	args := params.Entities{
   194  		Entities: []params.Entity{{Tag: u.tag}},
   195  	}
   196  	err := u.st.call("Resolved", args, &results)
   197  	if err != nil {
   198  		return "", err
   199  	}
   200  	if len(results.Results) != 1 {
   201  		return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
   202  	}
   203  	result := results.Results[0]
   204  	if result.Error != nil {
   205  		return "", result.Error
   206  	}
   207  	return result.Mode, nil
   208  }
   209  
   210  // IsPrincipal returns whether the unit is deployed in its own container,
   211  // and can therefore have subordinate services deployed alongside it.
   212  //
   213  // NOTE: This differs from state.Unit.IsPrincipal() by returning an
   214  // error as well, because it needs to make an API call.
   215  func (u *Unit) IsPrincipal() (bool, error) {
   216  	var results params.StringBoolResults
   217  	args := params.Entities{
   218  		Entities: []params.Entity{{Tag: u.tag}},
   219  	}
   220  	err := u.st.call("GetPrincipal", args, &results)
   221  	if err != nil {
   222  		return false, err
   223  	}
   224  	if len(results.Results) != 1 {
   225  		return false, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   226  	}
   227  	result := results.Results[0]
   228  	if result.Error != nil {
   229  		return false, result.Error
   230  	}
   231  	// GetPrincipal returns false when the unit is subordinate.
   232  	return !result.Ok, nil
   233  }
   234  
   235  // HasSubordinates returns the tags of any subordinate units.
   236  func (u *Unit) HasSubordinates() (bool, error) {
   237  	var results params.BoolResults
   238  	args := params.Entities{
   239  		Entities: []params.Entity{{Tag: u.tag}},
   240  	}
   241  	err := u.st.call("HasSubordinates", args, &results)
   242  	if err != nil {
   243  		return false, err
   244  	}
   245  	if len(results.Results) != 1 {
   246  		return false, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   247  	}
   248  	result := results.Results[0]
   249  	if result.Error != nil {
   250  		return false, result.Error
   251  	}
   252  	return result.Result, nil
   253  }
   254  
   255  // PublicAddress returns the public address of the unit and whether it
   256  // is valid.
   257  //
   258  // NOTE: This differs from state.Unit.PublicAddres() by returning
   259  // an error instead of a bool, because it needs to make an API call.
   260  //
   261  // TODO(dimitern): We might be able to drop this, once we have machine
   262  // addresses implemented fully. See also LP bug 1221798.
   263  func (u *Unit) PublicAddress() (string, error) {
   264  	var results params.StringResults
   265  	args := params.Entities{
   266  		Entities: []params.Entity{{Tag: u.tag}},
   267  	}
   268  	err := u.st.call("PublicAddress", args, &results)
   269  	if err != nil {
   270  		return "", err
   271  	}
   272  	if len(results.Results) != 1 {
   273  		return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
   274  	}
   275  	result := results.Results[0]
   276  	if result.Error != nil {
   277  		return "", result.Error
   278  	}
   279  	return result.Result, nil
   280  }
   281  
   282  // PrivateAddress returns the private address of the unit and whether
   283  // it is valid.
   284  //
   285  // NOTE: This differs from state.Unit.PrivateAddress() by returning
   286  // an error instead of a bool, because it needs to make an API call.
   287  //
   288  // TODO(dimitern): We might be able to drop this, once we have machine
   289  // addresses implemented fully. See also LP bug 1221798.
   290  func (u *Unit) PrivateAddress() (string, error) {
   291  	var results params.StringResults
   292  	args := params.Entities{
   293  		Entities: []params.Entity{{Tag: u.tag}},
   294  	}
   295  	err := u.st.call("PrivateAddress", args, &results)
   296  	if err != nil {
   297  		return "", err
   298  	}
   299  	if len(results.Results) != 1 {
   300  		return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
   301  	}
   302  	result := results.Results[0]
   303  	if result.Error != nil {
   304  		return "", result.Error
   305  	}
   306  	return result.Result, nil
   307  }
   308  
   309  // OpenPort sets the policy of the port with protocol and number to be
   310  // opened.
   311  //
   312  // TODO: We should really be opening and closing ports on machines,
   313  // rather than units.
   314  func (u *Unit) OpenPort(protocol string, number int) error {
   315  	var result params.ErrorResults
   316  	args := params.EntitiesPorts{
   317  		Entities: []params.EntityPort{
   318  			{Tag: u.tag, Protocol: protocol, Port: number},
   319  		},
   320  	}
   321  	err := u.st.call("OpenPort", args, &result)
   322  	if err != nil {
   323  		return err
   324  	}
   325  	return result.OneError()
   326  }
   327  
   328  // ClosePort sets the policy of the port with protocol and number to
   329  // be closed.
   330  //
   331  // TODO: We should really be opening and closing ports on machines,
   332  // rather than units.
   333  func (u *Unit) ClosePort(protocol string, number int) error {
   334  	var result params.ErrorResults
   335  	args := params.EntitiesPorts{
   336  		Entities: []params.EntityPort{
   337  			{Tag: u.tag, Protocol: protocol, Port: number},
   338  		},
   339  	}
   340  	err := u.st.call("ClosePort", args, &result)
   341  	if err != nil {
   342  		return err
   343  	}
   344  	return result.OneError()
   345  }
   346  
   347  var ErrNoCharmURLSet = errors.New("unit has no charm url set")
   348  
   349  // CharmURL returns the charm URL this unit is currently using.
   350  //
   351  // NOTE: This differs from state.Unit.CharmURL() by returning
   352  // an error instead of a bool, because it needs to make an API call.
   353  func (u *Unit) CharmURL() (*charm.URL, error) {
   354  	var results params.StringBoolResults
   355  	args := params.Entities{
   356  		Entities: []params.Entity{{Tag: u.tag}},
   357  	}
   358  	err := u.st.call("CharmURL", args, &results)
   359  	if err != nil {
   360  		return nil, err
   361  	}
   362  	if len(results.Results) != 1 {
   363  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   364  	}
   365  	result := results.Results[0]
   366  	if result.Error != nil {
   367  		return nil, result.Error
   368  	}
   369  	if result.Result != "" {
   370  		curl, err := charm.ParseURL(result.Result)
   371  		if err != nil {
   372  			return nil, err
   373  		}
   374  		return curl, nil
   375  	}
   376  	return nil, ErrNoCharmURLSet
   377  }
   378  
   379  // SetCharmURL marks the unit as currently using the supplied charm URL.
   380  // An error will be returned if the unit is dead, or the charm URL not known.
   381  func (u *Unit) SetCharmURL(curl *charm.URL) error {
   382  	if curl == nil {
   383  		return fmt.Errorf("charm URL cannot be nil")
   384  	}
   385  	var result params.ErrorResults
   386  	args := params.EntitiesCharmURL{
   387  		Entities: []params.EntityCharmURL{
   388  			{Tag: u.tag, CharmURL: curl.String()},
   389  		},
   390  	}
   391  	err := u.st.call("SetCharmURL", args, &result)
   392  	if err != nil {
   393  		return err
   394  	}
   395  	return result.OneError()
   396  }
   397  
   398  // ClearResolved removes any resolved setting on the unit.
   399  func (u *Unit) ClearResolved() error {
   400  	var result params.ErrorResults
   401  	args := params.Entities{
   402  		Entities: []params.Entity{{Tag: u.tag}},
   403  	}
   404  	err := u.st.call("ClearResolved", args, &result)
   405  	if err != nil {
   406  		return err
   407  	}
   408  	return result.OneError()
   409  }
   410  
   411  // WatchConfigSettings returns a watcher for observing changes to the
   412  // unit's service configuration settings. The unit must have a charm URL
   413  // set before this method is called, and the returned watcher will be
   414  // valid only while the unit's charm URL is not changed.
   415  func (u *Unit) WatchConfigSettings() (watcher.NotifyWatcher, error) {
   416  	var results params.NotifyWatchResults
   417  	args := params.Entities{
   418  		Entities: []params.Entity{{Tag: u.tag}},
   419  	}
   420  	err := u.st.call("WatchConfigSettings", args, &results)
   421  	if err != nil {
   422  		return nil, err
   423  	}
   424  	if len(results.Results) != 1 {
   425  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   426  	}
   427  	result := results.Results[0]
   428  	if result.Error != nil {
   429  		return nil, result.Error
   430  	}
   431  	w := watcher.NewNotifyWatcher(u.st.caller, result)
   432  	return w, nil
   433  }
   434  
   435  // JoinedRelations returns the tags of the relations the unit has joined.
   436  func (u *Unit) JoinedRelations() ([]string, error) {
   437  	var results params.StringsResults
   438  	args := params.Entities{
   439  		Entities: []params.Entity{{Tag: u.tag}},
   440  	}
   441  	err := u.st.call("JoinedRelations", args, &results)
   442  	if err != nil {
   443  		return nil, err
   444  	}
   445  	if len(results.Results) != 1 {
   446  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   447  	}
   448  	result := results.Results[0]
   449  	if result.Error != nil {
   450  		return nil, result.Error
   451  	}
   452  	return result.Result, nil
   453  }