github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/meterstatus/state.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package meterstatus
     5  
     6  import (
     7  	"os"
     8  	"time"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/utils/v3"
    12  	"gopkg.in/yaml.v2"
    13  
    14  	"github.com/juju/juju/rpc/params"
    15  )
    16  
    17  // State represents the worker's internal state.
    18  type State struct {
    19  	Code         string        `yaml:"status-code"`
    20  	Info         string        `yaml:"status-info"`
    21  	Disconnected *Disconnected `yaml:"disconnected,omitempty"`
    22  }
    23  
    24  // Disconnected stores the information relevant to the inactive meter status worker.
    25  type Disconnected struct {
    26  	Disconnected int64       `yaml:"disconnected-at,omitempty"`
    27  	State        WorkerState `yaml:"disconnected-state,omitempty"`
    28  }
    29  
    30  // When returns the time when the unit was disconnected.
    31  func (d Disconnected) When() time.Time {
    32  	return time.Unix(d.Disconnected, 0)
    33  }
    34  
    35  //go:generate go run go.uber.org/mock/mockgen -package mocks -destination mocks/interface_mock.go github.com/juju/juju/worker/meterstatus UnitStateAPI,StateReadWriter
    36  
    37  // StateReadWriter is implemented by types that can read and write the meter
    38  // worker's internal state.
    39  type StateReadWriter interface {
    40  	Read() (*State, error)
    41  	Write(*State) error
    42  }
    43  
    44  // UnitStateAPI describes the API for reading/writing unit state data from/to
    45  // the controller.
    46  type UnitStateAPI interface {
    47  	State() (params.UnitStateResult, error)
    48  	SetState(params.SetUnitStateArg) error
    49  }
    50  
    51  var _ StateReadWriter = (*ControllerBackedState)(nil)
    52  
    53  // ControllerBackedState is a StateReadWriter that uses the controller as its
    54  // backing store.
    55  type ControllerBackedState struct {
    56  	api UnitStateAPI
    57  }
    58  
    59  // NewControllerBackedState returns a new ControllerBackedState that uses the
    60  // provided UnitStateAPI to communicate with the controller.
    61  func NewControllerBackedState(api UnitStateAPI) *ControllerBackedState {
    62  	return &ControllerBackedState{api: api}
    63  }
    64  
    65  // Read the current meter status information from the controller.
    66  func (cbs *ControllerBackedState) Read() (*State, error) {
    67  	ust, err := cbs.api.State()
    68  	if err != nil {
    69  		return nil, errors.Trace(err)
    70  	}
    71  
    72  	if ust.MeterStatusState == "" {
    73  		return nil, errors.NotFoundf("state")
    74  	}
    75  
    76  	var st State
    77  	if err := yaml.Unmarshal([]byte(ust.MeterStatusState), &st); err != nil {
    78  		return nil, errors.Trace(err)
    79  	}
    80  
    81  	return &st, nil
    82  }
    83  
    84  // Write the supplied status information to the controller.
    85  func (cbs *ControllerBackedState) Write(st *State) error {
    86  	data, err := yaml.Marshal(st)
    87  	if err != nil {
    88  		return errors.Trace(err)
    89  	}
    90  
    91  	dataStr := string(data)
    92  	return errors.Trace(
    93  		cbs.api.SetState(params.SetUnitStateArg{
    94  			MeterStatusState: &dataStr,
    95  		}),
    96  	)
    97  }
    98  
    99  var _ StateReadWriter = (*DiskBackedState)(nil)
   100  
   101  // DiskBackedState stores the meter status on disk.
   102  type DiskBackedState struct {
   103  	path string
   104  }
   105  
   106  // NewDiskBackedState creates a DiskBackedState instance that uses path for
   107  // reading/writing the meter status state.
   108  func NewDiskBackedState(path string) *DiskBackedState {
   109  	return &DiskBackedState{path: path}
   110  }
   111  
   112  // Read the current meter status information from disk.
   113  func (dbs *DiskBackedState) Read() (*State, error) {
   114  	var st State
   115  	if err := utils.ReadYaml(dbs.path, &st); err != nil {
   116  		if os.IsNotExist(err) {
   117  			return nil, errors.NotFoundf("state file")
   118  		}
   119  		return nil, errors.Trace(err)
   120  	}
   121  
   122  	return &st, nil
   123  }
   124  
   125  // Write the supplied status information to disk.
   126  func (dbs *DiskBackedState) Write(st *State) error {
   127  	return errors.Trace(utils.WriteYaml(dbs.path, st))
   128  }