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