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 }