github.com/noironetworks/cilium-net@v1.6.12/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  }