Files
PandaX/apps/devops/services/k8s/event/event_common.go
2022-01-22 17:07:04 +08:00

238 lines
7.4 KiB
Go

package event
import (
"context"
"fmt"
v1 "k8s.io/api/core/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/kubernetes"
"pandax/apps/devops/entity/k8s"
k8scommon "pandax/apps/devops/services/k8s/common"
"pandax/apps/devops/services/k8s/dataselect"
)
// EmptyEventList is a empty list of events.
var EmptyEventList = &k8scommon.EventList{
Events: make([]k8scommon.Event, 0),
ListMeta: k8s.ListMeta{
TotalItems: 0,
},
}
// ToEvent converts event api Event to Event model object.
func ToEvent(event v1.Event) k8scommon.Event {
result := k8scommon.Event{
ObjectMeta: k8s.NewObjectMeta(event.ObjectMeta),
TypeMeta: k8s.NewTypeMeta(k8s.ResourceKindEvent),
Message: event.Message,
SourceComponent: event.Source.Component,
SourceHost: event.Source.Host,
SubObject: event.InvolvedObject.FieldPath,
SubObjectKind: event.InvolvedObject.Kind,
SubObjectName: event.InvolvedObject.Name,
SubObjectNamespace: event.InvolvedObject.Namespace,
Count: event.Count,
FirstSeen: event.FirstTimestamp,
LastSeen: event.LastTimestamp,
Reason: event.Reason,
Type: event.Type,
}
return result
}
// FailedReasonPartials is an array of partial strings to correctly filter warning events.
// Have to be lower case for correct case insensitive comparison.
// Based on k8s official events reason file:
// https://github.com/kubernetes/kubernetes/blob/886e04f1fffbb04faf8a9f9ee141143b2684ae68/pkg/kubelet/events/event.go
// Partial strings that are not in event.go file are added in order to support
// older versions of k8s which contained additional event reason messages.
var FailedReasonPartials = []string{"failed", "err", "exceeded", "invalid", "unhealthy",
"mismatch", "insufficient", "conflict", "outof", "nil", "backoff"}
// GetNodeEvents gets events associated to node with given name.
func GetNodeEvents(client *kubernetes.Clientset, nodeName string) (*v1.EventList, error) {
//scheme := runtime.NewScheme()
//groupVersion := schema.GroupVersion{Group: "", Version: "v1"}
//scheme.AddKnownTypes(groupVersion, &v1.Node{})
//
//node, err := client.CoreV1().Nodes().Get(context.TODO(), nodeName, metaV1.GetOptions{})
//
//if err != nil {
// return nil, err
//}
events, err := client.CoreV1().Events(v1.NamespaceAll).List(context.TODO(),
metaV1.ListOptions{FieldSelector: fmt.Sprintf("involvedObject.name=%v", nodeName)})
//events, err := client.CoreV1().Events(v1.NamespaceAll).Search(scheme, node)
if err != nil {
return nil, err
}
return events, nil
}
// FillEventsType is based on event Reason fills event Type in order to allow correct filtering by Type.
func FillEventsType(events []v1.Event) []v1.Event {
for i := range events {
// Fill in only events with empty type.
if len(events[i].Type) == 0 {
if isFailedReason(events[i].Reason, FailedReasonPartials...) {
events[i].Type = v1.EventTypeWarning
} else {
events[i].Type = v1.EventTypeNormal
}
}
}
return events
}
// GetResourceEvents gets events associated to specified resource.
func GetResourceEvents(client *kubernetes.Clientset, dsQuery *dataselect.DataSelectQuery, namespace, name string) (*k8scommon.EventList, error) {
resourceEvents, err := GetEvents(client, namespace, name)
if err != nil {
return EmptyEventList, err
}
events := CreateEventList(resourceEvents, dsQuery)
return &events, nil
}
// CreateEventList converts array of api events to common EventList structure
func CreateEventList(events []v1.Event, dsQuery *dataselect.DataSelectQuery) k8scommon.EventList {
eventList := k8scommon.EventList{
Events: make([]k8scommon.Event, 0),
ListMeta: k8s.ListMeta{TotalItems: len(events)},
}
events = fromCells(dataselect.GenericDataSelect(toCells(events), dsQuery))
for _, event := range events {
eventDetail := ToEvent(event)
eventList.Events = append(eventList.Events, eventDetail)
}
return eventList
}
// The code below allows to perform complex data section on []api.Event
type EventCell v1.Event
func (self EventCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
switch name {
case dataselect.NameProperty:
return dataselect.StdComparableString(self.ObjectMeta.Name)
case dataselect.CreationTimestampProperty:
return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time)
case dataselect.FirstSeenProperty:
return dataselect.StdComparableTime(self.FirstTimestamp.Time)
case dataselect.LastSeenProperty:
return dataselect.StdComparableTime(self.LastTimestamp.Time)
case dataselect.NamespaceProperty:
return dataselect.StdComparableString(self.ObjectMeta.Namespace)
case dataselect.ReasonProperty:
return dataselect.StdComparableString(self.Reason)
default:
// if name is not supported then just return a constant dummy value, sort will have no effect.
return nil
}
}
func toCells(std []v1.Event) []dataselect.DataCell {
cells := make([]dataselect.DataCell, len(std))
for i := range std {
cells[i] = EventCell(std[i])
}
return cells
}
func fromCells(cells []dataselect.DataCell) []v1.Event {
std := make([]v1.Event, len(cells))
for i := range std {
std[i] = v1.Event(cells[i].(EventCell))
}
return std
}
// GetEvents gets events associated to resource with given name.
func GetEvents(client *kubernetes.Clientset, namespace, resourceName string) ([]v1.Event, error) {
fieldSelector, err := fields.ParseSelector("involvedObject.name" + "=" + resourceName)
if err != nil {
return nil, err
}
channels := &k8scommon.ResourceChannels{
EventList: k8scommon.GetEventListChannelWithOptions(client, k8scommon.NewSameNamespaceQuery(namespace),
metaV1.ListOptions{
LabelSelector: labels.Everything().String(),
FieldSelector: fieldSelector.String(),
},
1),
}
eventList := <-channels.EventList.List
if err := <-channels.EventList.Error; err != nil {
return nil, err
}
return FillEventsType(eventList.Items), nil
}
// GetPodEvents gets pods events associated to pod name and namespace
func GetPodEvents(client *kubernetes.Clientset, namespace, podName string) ([]v1.Event, error) {
channels := &k8scommon.ResourceChannels{
PodList: k8scommon.GetPodListChannel(client, k8scommon.NewSameNamespaceQuery(namespace), 1),
EventList: k8scommon.GetEventListChannel(client, k8scommon.NewSameNamespaceQuery(namespace), 1),
}
podList := <-channels.PodList.List
if err := <-channels.PodList.Error; err != nil {
return nil, err
}
eventList := <-channels.EventList.List
if err := <-channels.EventList.Error; err != nil {
return nil, err
}
l := make([]v1.Pod, 0)
for _, pi := range podList.Items {
if pi.Name == podName {
l = append(l, pi)
}
}
events := filterEventsByPodsUID(eventList.Items, l)
return FillEventsType(events), nil
}
// GetPodsEvents gets events targeting given list of pods.
func GetPodsEvents(client *kubernetes.Clientset, namespace string, pods []v1.Pod) ([]v1.Event, error) {
nsQuery := k8scommon.NewSameNamespaceQuery(namespace)
if namespace == v1.NamespaceAll {
nsQuery = k8scommon.NewNamespaceQuery([]string{})
}
channels := &k8scommon.ResourceChannels{
EventList: k8scommon.GetEventListChannel(client, nsQuery, 1),
}
eventList := <-channels.EventList.List
if err := <-channels.EventList.Error; err != nil {
return nil, err
}
events := filterEventsByPodsUID(eventList.Items, pods)
return events, nil
}