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  }