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

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/names/v5"
     9  
    10  	"github.com/juju/juju/core/status"
    11  )
    12  
    13  // UnitAgent represents the state of an application's unit agent.
    14  type UnitAgent struct {
    15  	st   *State
    16  	tag  names.Tag
    17  	name string
    18  	status.StatusHistoryGetter
    19  }
    20  
    21  func newUnitAgent(st *State, tag names.Tag, name string) *UnitAgent {
    22  	unitAgent := &UnitAgent{
    23  		st:   st,
    24  		tag:  tag,
    25  		name: name,
    26  	}
    27  
    28  	return unitAgent
    29  }
    30  
    31  // String returns the unit agent as string.
    32  func (u *UnitAgent) String() string {
    33  	return u.name
    34  }
    35  
    36  // Status returns the status of the unit agent.
    37  func (u *UnitAgent) Status() (status.StatusInfo, error) {
    38  	info, err := getStatus(u.st.db(), u.globalKey(), "agent")
    39  	if err != nil {
    40  		return status.StatusInfo{}, errors.Trace(err)
    41  	}
    42  	// The current health spec says when a hook error occurs, the workload should
    43  	// be in error state, but the state model more correctly records the agent
    44  	// itself as being in error. So we'll do that model translation here.
    45  	// TODO(fwereade): this should absolutely not be happpening in the model.
    46  	// TODO: when fixed, also fix code in status.go for UnitAgent and backingUnit.
    47  	if info.Status == status.Error {
    48  		return status.StatusInfo{
    49  			Status:  status.Idle,
    50  			Message: "",
    51  			Data:    map[string]interface{}{},
    52  			Since:   info.Since,
    53  		}, nil
    54  	}
    55  	return info, nil
    56  }
    57  
    58  // SetStatus sets the status of the unit agent. The optional values
    59  // allow to pass additional helpful status data.
    60  func (u *UnitAgent) SetStatus(unitAgentStatus status.StatusInfo) (err error) {
    61  	unit, err := u.st.Unit(u.name)
    62  	if errors.IsNotFound(err) {
    63  		return errors.Annotate(errors.NotFoundf("agent"), "cannot set status")
    64  	}
    65  	if err != nil {
    66  		return errors.Trace(err)
    67  	}
    68  	isAssigned := unit.doc.MachineId != ""
    69  	shouldBeAssigned := unit.ShouldBeAssigned()
    70  	isPrincipal := unit.doc.Principal == ""
    71  
    72  	switch unitAgentStatus.Status {
    73  	case status.Idle, status.Executing, status.Rebooting, status.Failed:
    74  		if !isAssigned && isPrincipal && shouldBeAssigned {
    75  			return errors.Errorf("cannot set status %q until unit is assigned", unitAgentStatus.Status)
    76  		}
    77  	case status.Error:
    78  		if unitAgentStatus.Message == "" {
    79  			return errors.Errorf("cannot set status %q without info", unitAgentStatus.Status)
    80  		}
    81  	case status.Allocating:
    82  		if isAssigned {
    83  			return errors.Errorf("cannot set status %q as unit is already assigned", unitAgentStatus.Status)
    84  		}
    85  	case status.Running:
    86  		// Only CAAS units (those that require assignment) can have a status of running.
    87  		if shouldBeAssigned {
    88  			return errors.Errorf("cannot set invalid status %q", unitAgentStatus.Status)
    89  		}
    90  	case status.Lost:
    91  		return errors.Errorf("cannot set status %q", unitAgentStatus.Status)
    92  	default:
    93  		return errors.Errorf("cannot set invalid status %q", unitAgentStatus.Status)
    94  	}
    95  	return setStatus(u.st.db(), setStatusParams{
    96  		badge:     "agent",
    97  		globalKey: u.globalKey(),
    98  		status:    unitAgentStatus.Status,
    99  		message:   unitAgentStatus.Message,
   100  		rawData:   unitAgentStatus.Data,
   101  		updated:   timeOrNow(unitAgentStatus.Since, u.st.clock()),
   102  	})
   103  }
   104  
   105  // StatusHistory returns a slice of at most filter.Size StatusInfo items
   106  // or items as old as filter.Date or items newer than now - filter.Delta time
   107  // representing past statuses for this agent.
   108  func (u *UnitAgent) StatusHistory(filter status.StatusHistoryFilter) ([]status.StatusInfo, error) {
   109  	args := &statusHistoryArgs{
   110  		db:        u.st.db(),
   111  		globalKey: u.globalKey(),
   112  		filter:    filter,
   113  		clock:     u.st.clock(),
   114  	}
   115  	return statusHistory(args)
   116  }
   117  
   118  // unitAgentGlobalKey returns the global database key for the named unit.
   119  func unitAgentGlobalKey(name string) string {
   120  	return "u#" + name
   121  }
   122  
   123  // globalKey returns the global database key for the unit.
   124  func (u *UnitAgent) globalKey() string {
   125  	return unitAgentGlobalKey(u.name)
   126  }
   127  
   128  // Tag returns a names.Tag identifying this agent's unit.
   129  func (u *UnitAgent) Tag() names.Tag {
   130  	return u.tag
   131  }