github.com/verrazzano/verrazzano@v1.7.1/tools/vz/pkg/internal/util/cluster/events.go (about) 1 // Copyright (c) 2021, 2024, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 // Package cluster handles cluster analysis 5 package cluster 6 7 import ( 8 encjson "encoding/json" 9 "fmt" 10 "io" 11 "os" 12 "sync" 13 14 "github.com/verrazzano/verrazzano/tools/vz/pkg/constants" 15 "github.com/verrazzano/verrazzano/tools/vz/pkg/internal/util/files" 16 "go.uber.org/zap" 17 corev1 "k8s.io/api/core/v1" 18 ) 19 20 var eventListMap = make(map[string]*corev1.EventList) 21 var eventCacheMutex = &sync.Mutex{} 22 23 // GetEventList gets an event list 24 func GetEventList(log *zap.SugaredLogger, path string) (eventList *corev1.EventList, err error) { 25 // Check the cache first 26 eventList = getEventListIfPresent(path) 27 if eventList != nil { 28 log.Debugf("Returning cached eventList for %s", path) 29 return eventList, nil 30 } 31 32 // Not found in the cache, get it from the file 33 file, err := os.Open(path) 34 if err != nil { 35 log.Debugf("file %s not found", path) 36 return nil, err 37 } 38 defer file.Close() 39 40 fileBytes, err := io.ReadAll(file) 41 if err != nil { 42 log.Debugf("Failed reading Json file %s", path) 43 return nil, err 44 } 45 err = encjson.Unmarshal(fileBytes, &eventList) 46 if err != nil { 47 log.Debugf("Failed to unmarshal eventList at %s", path) 48 return nil, err 49 } 50 putEventListIfNotPresent(path, eventList) 51 return eventList, nil 52 } 53 54 // TODO: Need to add an optional time range to the get events, that will allow us to find events related to 55 // pods/services/etc that happened only within a given time range 56 57 // GetEventsRelatedToPod gets events related to a pod 58 func GetEventsRelatedToPod(log *zap.SugaredLogger, clusterRoot string, pod corev1.Pod, timeRange *files.TimeRange) (podEvents []corev1.Event, err error) { 59 allEvents, err := GetEventList(log, files.FormFilePathInNamespace(clusterRoot, pod.ObjectMeta.Namespace, constants.EventsJSON)) 60 if err != nil { 61 return nil, err 62 } 63 if allEvents == nil || len(allEvents.Items) == 0 { 64 return nil, nil 65 } 66 podEvents = make([]corev1.Event, 0, 1) 67 for _, event := range allEvents.Items { 68 if event.InvolvedObject.Kind == "Pod" && 69 event.InvolvedObject.Namespace == pod.ObjectMeta.Namespace && 70 event.InvolvedObject.Name == pod.ObjectMeta.Name { 71 podEvents = append(podEvents, event) 72 } 73 } 74 return podEvents, nil 75 } 76 77 // GetEventsRelatedToService gets events related to a service 78 func GetEventsRelatedToService(log *zap.SugaredLogger, clusterRoot string, service corev1.Service, timeRange *files.TimeRange) (serviceEvents []corev1.Event, err error) { 79 log.Debugf("GetEventsRelatedToService called for %s in namespace %s", service.ObjectMeta.Name, service.ObjectMeta.Namespace) 80 allEvents, err := GetEventList(log, files.FormFilePathInNamespace(clusterRoot, service.ObjectMeta.Namespace, constants.EventsJSON)) 81 if err != nil { 82 return nil, err 83 } 84 if allEvents == nil || len(allEvents.Items) == 0 { 85 log.Debugf("GetEventsRelatedToService: No events found") 86 return nil, nil 87 } 88 serviceEvents = make([]corev1.Event, 0, 1) 89 for _, event := range allEvents.Items { 90 log.Debugf("Checking event involved object kind: %s, namespace: %s, name: %s", event.InvolvedObject.Kind, event.InvolvedObject.Namespace, event.InvolvedObject.Name) 91 if event.InvolvedObject.Kind == "Service" && 92 event.InvolvedObject.Namespace == service.ObjectMeta.Namespace && 93 event.InvolvedObject.Name == service.ObjectMeta.Name { 94 log.Debugf("event matched service") 95 serviceEvents = append(serviceEvents, event) 96 } 97 } 98 return serviceEvents, nil 99 } 100 101 // GetEventsRelatedToComponentNamespace gets events related to a component namespace 102 func GetEventsRelatedToComponentNamespace(log *zap.SugaredLogger, clusterRoot string, componentNamespace string, timeRange *files.TimeRange) (componentEvents []corev1.Event, err error) { 103 log.Debugf("GetEventsRelatedToComponentNs called for component in namespace %s", componentNamespace) 104 105 podFile := files.FormFilePathInNamespace(clusterRoot, componentNamespace, podsJSON) 106 podList, err := GetPodList(log, podFile) 107 if err != nil { 108 log.Debugf("Failed to get the list of pods for the given pod file %s, skipping", podFile) 109 } 110 if podList == nil { 111 log.Debugf("No pod was returned, skipping") 112 return nil, nil 113 } 114 115 for _, pod := range podList.Items { 116 eventList, err := GetEventsRelatedToPod(log, clusterRoot, pod, nil) 117 if err != nil { 118 log.Debugf("Failed to get events for %s pod in namespace %s", pod.Name, pod.Namespace) 119 continue 120 } 121 componentEvents = append(componentEvents, eventList...) 122 } 123 return componentEvents, nil 124 } 125 126 // CheckEventsForWarnings goes through the events list for a specific Component/Service/Pod 127 // and checks if there exists an event with type Warning and returns the corresponding reason and message 128 // as an additional supporting data 129 func CheckEventsForWarnings(log *zap.SugaredLogger, events []corev1.Event, eventType string, timeRange *files.TimeRange) (message []string, err error) { 130 log.Debugf("Searching the events list for any additional support data") 131 var finalEventList []corev1.Event 132 for _, event := range events { 133 foundInvolvedObject := false 134 for index, previousEvent := range finalEventList { 135 // If we already iterated through an event for the same involved object with given eventType 136 // And that event occurred before this current event, replace it accordingly 137 if isInvolvedObjectSame(event, previousEvent) && event.LastTimestamp.Time.After(previousEvent.LastTimestamp.Time) { 138 foundInvolvedObject = true 139 // Remove the previous event from the final list 140 finalEventList = append(finalEventList[:index], finalEventList[index+1:]...) 141 if event.Type == eventType { 142 finalEventList = append(finalEventList, event) 143 } 144 break 145 } 146 } 147 // No previous event for this specific involved object 148 // Add to the finalEventList, if event type matches 149 if !foundInvolvedObject && event.Type == eventType { 150 finalEventList = append(finalEventList, event) 151 } 152 } 153 154 for _, event := range finalEventList { 155 if len(event.Message) == 0 { 156 continue 157 } 158 message = append(message, fmt.Sprintf("Namespace: %s, %s %s, Message: %s, Reason: %s", event.InvolvedObject.Namespace, event.InvolvedObject.Kind, event.InvolvedObject.Name, event.Message, event.Reason)) 159 } 160 161 return message, nil 162 } 163 164 func isInvolvedObjectSame(eventA corev1.Event, eventB corev1.Event) bool { 165 return eventA.InvolvedObject.Kind == eventB.InvolvedObject.Kind && 166 (eventA.InvolvedObject.Name == eventB.InvolvedObject.Name) && 167 eventA.InvolvedObject.Namespace == eventB.InvolvedObject.Namespace 168 } 169 170 func getEventListIfPresent(path string) (eventList *corev1.EventList) { 171 eventCacheMutex.Lock() 172 eventListTest := eventListMap[path] 173 if eventListTest != nil { 174 eventList = eventListTest 175 } 176 eventCacheMutex.Unlock() 177 return eventList 178 } 179 180 func putEventListIfNotPresent(path string, eventList *corev1.EventList) { 181 eventCacheMutex.Lock() 182 eventListInMap := eventListMap[path] 183 if eventListInMap == nil { 184 eventListMap[path] = eventList 185 } 186 eventCacheMutex.Unlock() 187 }