github.com/cilium/cilium@v1.16.2/pkg/endpoint/status.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package endpoint 5 6 import ( 7 "sort" 8 9 "github.com/cilium/cilium/api/v1/models" 10 "github.com/cilium/cilium/pkg/lock" 11 "github.com/cilium/cilium/pkg/time" 12 ) 13 14 type StatusCode int 15 16 const ( 17 OK StatusCode = 0 18 Warning StatusCode = -1 19 Failure StatusCode = -2 20 Disabled StatusCode = -3 21 ) 22 23 // StatusType represents the type for the given status, higher the value, higher 24 // the priority. 25 type StatusType int 26 27 const ( 28 BPF StatusType = 200 29 Policy StatusType = 100 30 Other StatusType = 0 31 ) 32 33 type Status struct { 34 Code StatusCode `json:"code"` 35 Msg string `json:"msg"` 36 Type StatusType `json:"status-type"` 37 State string `json:"state"` 38 } 39 40 func (sc StatusCode) String() string { 41 switch sc { 42 case OK: 43 return "OK" 44 case Warning: 45 return "Warning" 46 case Failure: 47 return "Failure" 48 case Disabled: 49 return "Disabled" 50 default: 51 return "Unknown code" 52 } 53 } 54 55 func (s Status) String() string { 56 if s.Msg == "" { 57 return s.Code.String() 58 } 59 return s.Code.String() + " - " + s.Msg 60 } 61 62 type StatusResponse struct { 63 KVStore Status `json:"kvstore"` 64 Docker Status `json:"docker"` 65 Kubernetes Status `json:"kubernetes"` 66 Cilium Status `json:"cilium"` 67 IPAMStatus map[string][]string `json:",omitempty"` 68 } 69 70 // statusLogMsg represents a log message. 71 type statusLogMsg struct { 72 Status Status `json:"status"` 73 Timestamp time.Time `json:"timestamp"` 74 } 75 76 // statusLog represents a slice of statusLogMsg. 77 type statusLog []*statusLogMsg 78 79 // componentStatus represents a map of a single statusLogMsg by StatusType. 80 type componentStatus map[StatusType]*statusLogMsg 81 82 // contains checks if the given `s` statusLogMsg is present in the 83 // priorityStatus. 84 func (ps componentStatus) contains(s *statusLogMsg) bool { 85 return ps[s.Status.Type] == s 86 } 87 88 // statusTypeSlice represents a slice of StatusType, is used for sorting 89 // purposes. 90 type statusTypeSlice []StatusType 91 92 // Len returns the length of the slice. 93 func (p statusTypeSlice) Len() int { return len(p) } 94 95 // Less returns true if the element `j` is less than element `i`. 96 // *It's reversed* so that we can sort the slice by high to lowest priority. 97 func (p statusTypeSlice) Less(i, j int) bool { return p[i] > p[j] } 98 99 // Swap swaps element in `i` with element in `j`. 100 func (p statusTypeSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 101 102 // sortByPriority returns a statusLog ordered from highest priority to lowest. 103 func (ps componentStatus) sortByPriority() statusLog { 104 prs := statusTypeSlice{} 105 for k := range ps { 106 prs = append(prs, k) 107 } 108 sort.Sort(prs) 109 slogSorted := statusLog{} 110 for _, pr := range prs { 111 slogSorted = append(slogSorted, ps[pr]) 112 } 113 return slogSorted 114 } 115 116 // EndpointStatus represents the endpoint status. 117 type EndpointStatus struct { 118 // CurrentStatuses is the last status of a given priority. 119 CurrentStatuses componentStatus `json:"current-status,omitempty"` 120 // Contains the last maxLogs messages for this endpoint. 121 Log statusLog `json:"log,omitempty"` 122 // Index is the index in the statusLog, is used to keep track the next 123 // available position to write a new log message. 124 Index int `json:"index"` 125 // indexMU is the Mutex for the CurrentStatus and Log RW operations. 126 indexMU lock.RWMutex 127 } 128 129 func NewEndpointStatus() *EndpointStatus { 130 return &EndpointStatus{ 131 CurrentStatuses: componentStatus{}, 132 Log: statusLog{}, 133 } 134 } 135 136 func (e *EndpointStatus) lastIndex() int { 137 lastIndex := e.Index - 1 138 if lastIndex < 0 { 139 return maxLogs - 1 140 } 141 return lastIndex 142 } 143 144 // getAndIncIdx returns current free slot index and increments the index to the 145 // next index that can be overwritten. 146 func (e *EndpointStatus) getAndIncIdx() int { 147 idx := e.Index 148 e.Index++ 149 if e.Index >= maxLogs { 150 e.Index = 0 151 } 152 // Lets skip the CurrentStatus message from the log to prevent removing 153 // non-OK status! 154 if e.Index < len(e.Log) && 155 e.CurrentStatuses.contains(e.Log[e.Index]) && 156 e.Log[e.Index].Status.Code != OK { 157 e.Index++ 158 if e.Index >= maxLogs { 159 e.Index = 0 160 } 161 } 162 return idx 163 } 164 165 // addStatusLog adds statusLogMsg to endpoint log. 166 // example of e.Log's contents where maxLogs = 3 and Index = 0 167 // [index] - Priority - Code 168 // [0] - BPF - OK 169 // [1] - Policy - Failure 170 // [2] - BPF - OK 171 // With this log, the CurrentStatus will keep [1] for Policy priority and [2] 172 // for BPF priority. 173 // 174 // Whenever a new statusLogMsg is received, that log will be kept in the 175 // CurrentStatus map for the statusLogMsg's priority. 176 // The CurrentStatus map, ensures non of the failure messages are deleted for 177 // higher priority messages and vice versa. 178 func (e *EndpointStatus) addStatusLog(s *statusLogMsg) { 179 e.CurrentStatuses[s.Status.Type] = s 180 idx := e.getAndIncIdx() 181 if len(e.Log) < maxLogs { 182 e.Log = append(e.Log, s) 183 } else { 184 e.Log[idx] = s 185 } 186 } 187 188 func (e *EndpointStatus) GetModel() []*models.EndpointStatusChange { 189 e.indexMU.RLock() 190 defer e.indexMU.RUnlock() 191 192 list := []*models.EndpointStatusChange{} 193 for i := e.lastIndex(); ; i-- { 194 if i < 0 { 195 i = maxLogs - 1 196 } 197 if i < len(e.Log) && e.Log[i] != nil { 198 list = append(list, &models.EndpointStatusChange{ 199 Timestamp: e.Log[i].Timestamp.Format(time.RFC3339), 200 Code: e.Log[i].Status.Code.String(), 201 Message: e.Log[i].Status.Msg, 202 State: models.EndpointState(e.Log[i].Status.State), 203 }) 204 } 205 if i == e.Index { 206 break 207 } 208 } 209 return list 210 } 211 212 func (e *EndpointStatus) CurrentStatus() StatusCode { 213 e.indexMU.RLock() 214 defer e.indexMU.RUnlock() 215 sP := e.CurrentStatuses.sortByPriority() 216 for _, v := range sP { 217 if v.Status.Code != OK { 218 return v.Status.Code 219 } 220 } 221 return OK 222 } 223 224 func (e *EndpointStatus) String() string { 225 return e.CurrentStatus().String() 226 }