github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/api/agent/uniter/relationunit.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  	"github.com/juju/names/v5"
     9  
    10  	"github.com/juju/juju/rpc/params"
    11  )
    12  
    13  // This module implements a subset of the interface provided by
    14  // state.RelationUnit, as needed by the uniter API.
    15  // Most of this is pretty much a verbatim copy of the code in
    16  // state/relationunit.go, except for a few API-specific changes.
    17  
    18  // RelationUnit holds information about a single unit in a relation,
    19  // and allows clients to conveniently access unit-specific
    20  // functionality.
    21  type RelationUnit struct {
    22  	st       *State
    23  	relation *Relation
    24  	unitTag  names.UnitTag
    25  	appTag   names.ApplicationTag
    26  	endpoint Endpoint
    27  }
    28  
    29  // Relation returns the relation associated with the unit.
    30  func (ru *RelationUnit) Relation() *Relation {
    31  	return ru.relation
    32  }
    33  
    34  // Endpoint returns the relation endpoint that defines the unit's
    35  // participation in the relation.
    36  func (ru *RelationUnit) Endpoint() Endpoint {
    37  	return ru.endpoint
    38  }
    39  
    40  // EnterScope ensures that the unit has entered its scope in the relation.
    41  // When the unit has already entered its relation scope, EnterScope will report
    42  // success but make no changes to state.
    43  //
    44  // Otherwise, assuming both the relation and the unit are alive, it will enter
    45  // scope.
    46  //
    47  // If the unit is a principal and the relation has container scope, EnterScope
    48  // will also create the required subordinate unit, if it does not already exist;
    49  // this is because there's no point having a principal in scope if there is no
    50  // corresponding subordinate to join it.
    51  //
    52  // Once a unit has entered a scope, it stays in scope without further
    53  // intervention; the relation will not be able to become Dead until all units
    54  // have departed its scopes.
    55  //
    56  // NOTE: Unlike state.RelatioUnit.EnterScope(), this method does not take
    57  // settings, because uniter only uses this to supply the unit's private
    58  // address, but this is not done at the server-side by the API.
    59  func (ru *RelationUnit) EnterScope() error {
    60  	var result params.ErrorResults
    61  	args := params.RelationUnits{
    62  		RelationUnits: []params.RelationUnit{{
    63  			Relation: ru.relation.tag.String(),
    64  			Unit:     ru.unitTag.String(),
    65  		}},
    66  	}
    67  	err := ru.st.facade.FacadeCall("EnterScope", args, &result)
    68  	if err != nil {
    69  		return errors.Trace(err)
    70  	}
    71  	return errors.Trace(result.OneError())
    72  }
    73  
    74  // LeaveScope signals that the unit has left its scope in the relation.
    75  // After the unit has left its relation scope, it is no longer a member
    76  // of the relation; if the relation is dying when its last member unit
    77  // leaves, it is removed immediately. It is not an error to leave a scope
    78  // that the unit is not, or never was, a member of.
    79  func (ru *RelationUnit) LeaveScope() error {
    80  	var result params.ErrorResults
    81  	args := params.RelationUnits{
    82  		RelationUnits: []params.RelationUnit{{
    83  			Relation: ru.relation.tag.String(),
    84  			Unit:     ru.unitTag.String(),
    85  		}},
    86  	}
    87  	err := ru.st.facade.FacadeCall("LeaveScope", args, &result)
    88  	if err != nil {
    89  		return errors.Trace(err)
    90  	}
    91  	return errors.Trace(result.OneError())
    92  }
    93  
    94  // Settings returns a Settings which allows access to the unit's settings
    95  // within the relation.
    96  func (ru *RelationUnit) Settings() (*Settings, error) {
    97  	var results params.SettingsResults
    98  	args := params.RelationUnits{
    99  		RelationUnits: []params.RelationUnit{{
   100  			Relation: ru.relation.tag.String(),
   101  			Unit:     ru.unitTag.String(),
   102  		}},
   103  	}
   104  	err := ru.st.facade.FacadeCall("ReadSettings", args, &results)
   105  	if err != nil {
   106  		return nil, errors.Trace(err)
   107  	}
   108  	if len(results.Results) != 1 {
   109  		return nil, errors.Errorf("expected 1 result, got %d", len(results.Results))
   110  	}
   111  	result := results.Results[0]
   112  	if result.Error != nil {
   113  		return nil, errors.Trace(result.Error)
   114  	}
   115  	return newSettings(ru.relation.tag.String(), ru.unitTag.String(), result.Settings), nil
   116  }
   117  
   118  // ApplicationSettings returns a Settings which allows access to this unit's
   119  // application settings within the relation. This can only be used from the
   120  // leader unit. Calling it from a non-Leader generates a NotLeader error.
   121  func (ru *RelationUnit) ApplicationSettings() (*Settings, error) {
   122  	var result params.SettingsResult
   123  	arg := params.RelationUnit{
   124  		Relation: ru.relation.tag.String(),
   125  		Unit:     ru.unitTag.String(),
   126  	}
   127  	if err := ru.st.facade.FacadeCall("ReadLocalApplicationSettings", arg, &result); err != nil {
   128  		return nil, errors.Trace(err)
   129  	} else if result.Error != nil {
   130  		return nil, errors.Trace(result.Error)
   131  	}
   132  	return newSettings(ru.relation.tag.String(), ru.appTag.String(), result.Settings), nil
   133  }
   134  
   135  // ReadSettings returns a map holding the settings of the unit with the
   136  // supplied name within this relation. An error will be returned if the
   137  // relation no longer exists, or if the unit's application is not part of the
   138  // relation, or the settings are invalid; but mere non-existence of the
   139  // unit is not grounds for an error, because the unit settings are
   140  // guaranteed to persist for the lifetime of the relation, regardless
   141  // of the lifetime of the unit.
   142  func (ru *RelationUnit) ReadSettings(name string) (params.Settings, error) {
   143  	var tag names.Tag
   144  	if names.IsValidUnit(name) {
   145  		tag = names.NewUnitTag(name)
   146  	} else if names.IsValidApplication(name) {
   147  		tag = names.NewApplicationTag(name)
   148  	} else {
   149  		return nil, errors.Errorf("%q is not a valid unit or application", name)
   150  	}
   151  	var results params.SettingsResults
   152  	args := params.RelationUnitPairs{
   153  		RelationUnitPairs: []params.RelationUnitPair{{
   154  			Relation:   ru.relation.tag.String(),
   155  			LocalUnit:  ru.unitTag.String(),
   156  			RemoteUnit: tag.String(),
   157  		}},
   158  	}
   159  	err := ru.st.facade.FacadeCall("ReadRemoteSettings", args, &results)
   160  	if err != nil {
   161  		return nil, errors.Trace(err)
   162  	}
   163  	if len(results.Results) != 1 {
   164  		return nil, errors.Errorf("expected 1 result, got %d", len(results.Results))
   165  	}
   166  	result := results.Results[0]
   167  	if result.Error != nil {
   168  		return nil, errors.Trace(result.Error)
   169  	}
   170  	return result.Settings, nil
   171  }