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 }