github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/unitstate.go (about)

     1  // Copyright 2020 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"strconv"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/mgo/v3"
    11  	"github.com/juju/mgo/v3/bson"
    12  	"github.com/juju/mgo/v3/txn"
    13  
    14  	mgoutils "github.com/juju/juju/mongo/utils"
    15  )
    16  
    17  // unitStateDoc records the state persisted by the charm executing in the unit.
    18  type unitStateDoc struct {
    19  	// DocID is always the same as a unit's global key.
    20  	DocID    string `bson:"_id"`
    21  	TxnRevno int64  `bson:"txn-revno"`
    22  
    23  	// The following maps to UnitState:
    24  
    25  	// State encodes the unit's persisted charm state as a list of key-value pairs.
    26  	CharmState map[string]string `bson:"charm-state,omitempty"`
    27  
    28  	// UniterState is a serialized yaml string containing the uniters internal
    29  	// state for this unit.
    30  	UniterState string `bson:"uniter-state,omitempty"`
    31  
    32  	// RelationState is a serialized yaml string containing relation internal
    33  	// state for this unit from the uniter.
    34  	RelationState map[string]string `bson:"relation-state,omitempty"`
    35  
    36  	// StorageState is a serialized yaml string containing storage internal
    37  	// state for this unit from the uniter.
    38  	StorageState string `bson:"storage-state,omitempty"`
    39  
    40  	// SecretState is a serialized yaml string containing secret internal
    41  	// state for this unit from the uniter.
    42  	SecretState string `bson:"secret-state,omitempty"`
    43  
    44  	// MeterStatusState is a serialized yaml string containing the internal
    45  	// state for this unit's meter status worker.
    46  	MeterStatusState string `bson:"meter-status-state,omitempty"`
    47  }
    48  
    49  // charmStateMatches returns true if the State map within the unitStateDoc matches
    50  // the provided st argument.
    51  func (d *unitStateDoc) charmStateMatches(st bson.M) bool {
    52  	if len(st) != len(d.CharmState) {
    53  		return false
    54  	}
    55  
    56  	for k, v := range d.CharmState {
    57  		if st[k] != v {
    58  			return false
    59  		}
    60  	}
    61  
    62  	return true
    63  }
    64  
    65  // relationData translate the unitStateDoc's RelationState as
    66  // a map[string]string to a map[int]string, as is needed.
    67  // BSON does not allow ints as a map key.
    68  func (d *unitStateDoc) relationData() (map[int]string, error) {
    69  	if d.RelationState == nil {
    70  		return nil, nil
    71  	}
    72  	// BSON maps cannot have an int as key.
    73  	rState := make(map[int]string, len(d.RelationState))
    74  	for k, v := range d.RelationState {
    75  		kString, err := strconv.Atoi(k)
    76  		if err != nil {
    77  			return nil, err
    78  		}
    79  		rState[kString] = v
    80  	}
    81  	return rState, nil
    82  }
    83  
    84  // relationStateMatches returns true if the RelationState map within the
    85  // unitStateDoc contains all of the provided newRS map.  Assumes that
    86  // relationStateBSONFriendly has been called first.
    87  func (d *unitStateDoc) relationStateMatches(newRS map[string]string) bool {
    88  	if len(d.RelationState) != len(newRS) {
    89  		return false
    90  	}
    91  	for k, v := range newRS {
    92  		if d.RelationState[k] != v {
    93  			return false
    94  		}
    95  	}
    96  	return true
    97  }
    98  
    99  // removeUnitStateOp returns the operation needed to remove the unit state
   100  // document associated with the given globalKey.
   101  func removeUnitStateOp(mb modelBackend, globalKey string) txn.Op {
   102  	return txn.Op{
   103  		C:      unitStatesC,
   104  		Id:     mb.docID(globalKey),
   105  		Remove: true,
   106  	}
   107  }
   108  
   109  // UnitState contains the various state saved for this unit,
   110  // including from the charm itself and the uniter.
   111  type UnitState struct {
   112  	// charmState encodes the unit's persisted charm state as a list of
   113  	// key-value pairs.
   114  	charmState    map[string]string
   115  	charmStateSet bool
   116  
   117  	// uniterState is a serialized yaml string containing the uniters internal
   118  	// state for this unit.
   119  	uniterState    string
   120  	uniterStateSet bool
   121  
   122  	// relationState is a serialized yaml string containing relation internal
   123  	// state for this unit from the uniter.
   124  	relationState    map[int]string
   125  	relationStateSet bool
   126  
   127  	// storageState is a serialized yaml string containing storage internal
   128  	// state for this unit from the uniter.
   129  	storageState    string
   130  	storageStateSet bool
   131  
   132  	// secretState is a serialized yaml string containing secret internal
   133  	// state for this unit from the uniter.
   134  	secretState    string
   135  	secretStateSet bool
   136  
   137  	// meterStatusState is a serialized yaml string containing the internal
   138  	// state for the meter status worker for this unit.
   139  	meterStatusState    string
   140  	meterStatusStateSet bool
   141  }
   142  
   143  // NewUnitState returns a new UnitState struct.
   144  func NewUnitState() *UnitState {
   145  	return &UnitState{}
   146  }
   147  
   148  // Modified returns true if any of the struct have been set.
   149  func (u *UnitState) Modified() bool {
   150  	return u.relationStateSet ||
   151  		u.storageStateSet ||
   152  		u.secretStateSet ||
   153  		u.charmStateSet ||
   154  		u.uniterStateSet ||
   155  		u.meterStatusStateSet
   156  }
   157  
   158  // SetCharmState sets the charm state value.
   159  func (u *UnitState) SetCharmState(state map[string]string) {
   160  	u.charmStateSet = true
   161  	u.charmState = state
   162  }
   163  
   164  // CharmState returns the unit's stored charm state and bool indicating
   165  // whether the data was set.
   166  func (u *UnitState) CharmState() (map[string]string, bool) {
   167  	return u.charmState, u.charmStateSet
   168  }
   169  
   170  // SetUniterState sets the uniter state value.
   171  func (u *UnitState) SetUniterState(state string) {
   172  	u.uniterStateSet = true
   173  	u.uniterState = state
   174  }
   175  
   176  // UniterState returns the uniter state and bool indicating
   177  // whether the data was set.
   178  func (u *UnitState) UniterState() (string, bool) {
   179  	return u.uniterState, u.uniterStateSet
   180  }
   181  
   182  // SetRelationState sets the relation state value.
   183  func (u *UnitState) SetRelationState(state map[int]string) {
   184  	u.relationStateSet = true
   185  	u.relationState = state
   186  }
   187  
   188  // RelationState returns the relation state and bool indicating
   189  // whether the data was set.
   190  func (u *UnitState) RelationState() (map[int]string, bool) {
   191  	return u.relationState, u.relationStateSet
   192  }
   193  
   194  // relationStateBSONFriendly makes a map[int]string BSON friendly by
   195  // translating the int map key to a string.
   196  func (u *UnitState) relationStateBSONFriendly() (map[string]string, bool) {
   197  	stringData := make(map[string]string, len(u.relationState))
   198  	for k, v := range u.relationState {
   199  		stringData[strconv.Itoa(k)] = v
   200  	}
   201  	return stringData, u.relationStateSet
   202  }
   203  
   204  // SetStorageState sets the storage state value.
   205  func (u *UnitState) SetStorageState(state string) {
   206  	u.storageStateSet = true
   207  	u.storageState = state
   208  }
   209  
   210  // StorageState returns the storage state and bool indicating
   211  // whether the data was set.
   212  func (u *UnitState) StorageState() (string, bool) {
   213  	return u.storageState, u.storageStateSet
   214  }
   215  
   216  // SetSecretState sets the secret state value.
   217  func (u *UnitState) SetSecretState(state string) {
   218  	u.secretStateSet = true
   219  	u.secretState = state
   220  }
   221  
   222  // SecretState returns the secret state and bool indicating
   223  // whether the data was set.
   224  func (u *UnitState) SecretState() (string, bool) {
   225  	return u.secretState, u.secretStateSet
   226  }
   227  
   228  // SetMeterStatusState sets the state value for meter state.
   229  func (u *UnitState) SetMeterStatusState(state string) {
   230  	u.meterStatusStateSet = true
   231  	u.meterStatusState = state
   232  }
   233  
   234  // MeterStatusState returns the meter status state and a bool to indicate
   235  // whether the data was set.
   236  func (u *UnitState) MeterStatusState() (string, bool) {
   237  	return u.meterStatusState, u.meterStatusStateSet
   238  }
   239  
   240  // SetState replaces the currently stored state for a unit with the contents
   241  // of the provided UnitState.
   242  //
   243  // Use this for testing, otherwise use SetStateOperation.
   244  func (u *Unit) SetState(unitState *UnitState, limits UnitStateSizeLimits) error {
   245  	modelOp := u.SetStateOperation(unitState, limits)
   246  	return u.st.ApplyOperation(modelOp)
   247  }
   248  
   249  // SetStateOperation returns a ModelOperation for replacing the currently
   250  // stored state for a unit with the contents of the provided UnitState.
   251  func (u *Unit) SetStateOperation(unitState *UnitState, limits UnitStateSizeLimits) ModelOperation {
   252  	return &unitSetStateOperation{u: u, newState: unitState, limits: limits}
   253  }
   254  
   255  // State returns the persisted state for a unit.
   256  func (u *Unit) State() (*UnitState, error) {
   257  	us := NewUnitState()
   258  
   259  	// Normally this would be if Life() != Alive.  However the uniter
   260  	// needs to read its state during the Dying period for the case of
   261  	// hook failures just before dying.  It's unclear whether this is
   262  	// an actual scenario or just part of the UniterSuite unit tests.
   263  	// See TestUniterDyingReaction.
   264  	if u.Life() == Dead {
   265  		return us, errors.NotFoundf("unit %s", u.Name())
   266  	}
   267  
   268  	coll, closer := u.st.db().GetCollection(unitStatesC)
   269  	defer closer()
   270  
   271  	var stDoc unitStateDoc
   272  	if err := coll.FindId(u.globalKey()).One(&stDoc); err != nil {
   273  		if err == mgo.ErrNotFound {
   274  			return us, nil
   275  		}
   276  		return us, errors.Trace(err)
   277  	}
   278  
   279  	if stDoc.RelationState != nil {
   280  		rState, err := stDoc.relationData()
   281  		if err != nil {
   282  			return us, errors.Trace(err)
   283  		}
   284  		us.SetRelationState(rState)
   285  	}
   286  
   287  	if stDoc.CharmState != nil {
   288  		charmState := make(map[string]string, len(stDoc.CharmState))
   289  		for k, v := range stDoc.CharmState {
   290  			charmState[mgoutils.UnescapeKey(k)] = v
   291  		}
   292  		us.SetCharmState(charmState)
   293  	}
   294  
   295  	us.SetUniterState(stDoc.UniterState)
   296  	us.SetStorageState(stDoc.StorageState)
   297  	us.SetSecretState(stDoc.SecretState)
   298  	us.SetMeterStatusState(stDoc.MeterStatusState)
   299  
   300  	return us, nil
   301  }
   302  
   303  // UnitStateSizeLimits defines the quota limits that are enforced when updating
   304  // the state (charm and uniter) of a unit.
   305  type UnitStateSizeLimits struct {
   306  	// The maximum allowed size for the charm state. It can be set to zero
   307  	// to bypass the charm state quota checks.
   308  	// quota checks will be
   309  	MaxCharmStateSize int
   310  
   311  	// The maximum allowed size for the uniter's state. It can be set to
   312  	// zero to bypass the uniter state quota checks.
   313  	MaxAgentStateSize int
   314  }