Skip to content

Commit

Permalink
Add comments & modify tests
Browse files Browse the repository at this point in the history
- Added more comments
- Updated some tests
  • Loading branch information
ralongit committed Sep 24, 2023
1 parent 80cc0dd commit 1509f85
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 92 deletions.
12 changes: 4 additions & 8 deletions common/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ func ParseEventLog(msg string, extraFields ...interface{}) (eventLog string) {
// If there are extra fields, convert them to a JSON string and unmarshal into logEvent
extra := fmt.Sprintf("%s", extraFields...)

if err = json.Unmarshal([]byte(extra), &logEvent); err != nil && extra != "" {
if err = json.Unmarshal([]byte(extra), &logEvent); err != nil && extra != "" && extra != "[]" {
// If there is an error in parsing the extra fields, log the error
log.Printf("\n[ERROR] Failed to parse log extra data(%T): %s\tlog(%T):\n%v to Logz.io.\nRelated error:\n%v", extra, logEvent, extra, logEvent, err)
log.Printf("\n[ERROR] Failed to parse log extra data(%T): %s\tlog(%T):\n%v to Logz.io.\nRelated error:\n%v", extra, extra, logEvent, logEvent, err)

}
}
Expand All @@ -102,9 +102,9 @@ func ParseEventLog(msg string, extraFields ...interface{}) (eventLog string) {
}

// Convert the parsed event log byte slice to a string
eventLog = fmt.Sprintf("%s", string(parsedEventLog))
//eventLog = fmt.Sprintf("%s", string(parsedEventLog))

return eventLog
return string(parsedEventLog)
}

func SendLog(msg string, extraFields ...interface{}) {
Expand All @@ -124,9 +124,5 @@ func SendLog(msg string, extraFields ...interface{}) {
wg.Add(1)
wg.Wait()
}
} else {
// If the logz.io logger is not configured, log a message and do not send the log
log.Printf("Logz.io logger isn't configured.\nLog won't be sent:\n%s", msg)
}

}
9 changes: 0 additions & 9 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,4 @@ func TestDeployEvents(t *testing.T) {
}

}

////
//
//common.DynamicClient = common.ConfigureClusterDynamicClient()
//if common.DynamicClient != nil {
// resources.AddEventHandlers()
//}
//
//common.LogzioLogger.Stop()
}
1 change: 0 additions & 1 deletion mockLogzioListener/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ func (h *ListenerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

// Define the structure of the expected request body
type RequestBody struct {
Message string `json:"message"`
}

// Read the request body
Expand Down
85 changes: 65 additions & 20 deletions resources/resourceInformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func createResourceInformer(resourceGVR schema.GroupVersionResource, clusterClie
// AddInformerEventHandler adds a new event handler to a given resource informer.
// It logs events when a resource is added, updated, or deleted.
func AddInformerEventHandler(resourceInformer cache.SharedIndexInformer) (synced bool) {
var parsedEventLog []byte
// Check if the resource informer is nil
if resourceInformer == nil {
log.Println("[ERROR] Resource informer is nil")
Expand All @@ -61,7 +62,7 @@ func AddInformerEventHandler(resourceInformer cache.SharedIndexInformer) (synced
mux.RLock()
defer mux.RUnlock()
if synced {
StructResourceLog(map[string]interface{}{
_, parsedEventLog = StructResourceLog(map[string]interface{}{
"eventType": "ADDED",
"newObject": obj,
})
Expand All @@ -72,7 +73,7 @@ func AddInformerEventHandler(resourceInformer cache.SharedIndexInformer) (synced
mux.RLock()
defer mux.RUnlock()
if synced {
StructResourceLog(map[string]interface{}{
_, parsedEventLog = StructResourceLog(map[string]interface{}{
"eventType": "MODIFIED",
"newObject": newObj,
"oldObject": oldObj,
Expand All @@ -84,7 +85,7 @@ func AddInformerEventHandler(resourceInformer cache.SharedIndexInformer) (synced
mux.RLock()
defer mux.RUnlock()
if synced {
StructResourceLog(map[string]interface{}{
_, parsedEventLog = StructResourceLog(map[string]interface{}{
"eventType": "DELETED",
"newObject": obj,
})
Expand All @@ -97,7 +98,7 @@ func AddInformerEventHandler(resourceInformer cache.SharedIndexInformer) (synced
common.SendLog(fmt.Sprintf("[ERROR] Failed to add event handler for informer.\nERROR:\n%v", err))
return
}

common.SendLog(string(parsedEventLog))
// Create a new context that will get cancelled when an interrupt signal is received
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
Expand Down Expand Up @@ -131,6 +132,7 @@ func AddInformerEventHandler(resourceInformer cache.SharedIndexInformer) (synced
return synced

}

func AddEventHandlers() {
// Creates informer for each cluster API and events handler for each informer
resourceAPIList := map[string]string{
Expand Down Expand Up @@ -179,14 +181,32 @@ func AddEventHandlers() {
}

// EventObject transforms a raw object into a KubernetesEvent object.
// It takes a map representing the raw object and a boolean indicating whether the object is new or not.
// It takes a map representing the raw object and a boolean indicating whether the object is new or n
// ot.
func EventObject(rawObj map[string]interface{}, isNew bool) (resourceObject common.KubernetesEvent) {

// Check if the raw object or its "newObject" and "oldObject" fields are nil
if rawObj == nil || rawObj["newObject"] == nil || rawObj["oldObject"] == nil {
log.Println("[ERROR] rawObj is nil or does not have required fields: newObject/oldObject.")
// Check if the raw object is nil
if rawObj == nil {
log.Println("[ERROR] rawObj is nil.")
// Return an empty KubernetesEvent object if the raw object is invalid
return resourceObject
}

// Check if the newObject field of rawObj is nil, if it is required
if isNew && rawObj["newObject"] == nil {
log.Println("[ERROR] rawObj does not have required field: newObject.")
// Return an empty KubernetesEvent object if the raw object is invalid
return resourceObject
}

// Check if the oldObject field of rawObj is nil, if it is required
if !isNew && rawObj["oldObject"] == nil {
log.Println("[ERROR] rawObj does not have required field: oldObject.")
// Return an empty KubernetesEvent object if the raw object is invalid
return resourceObject
}

// Initialize an empty unstructured object
rawUnstructuredObj := unstructured.Unstructured{}
// Initialize a buffer to store the JSON-encoded raw object
Expand All @@ -197,9 +217,7 @@ func EventObject(rawObj map[string]interface{}, isNew bool) (resourceObject comm
// Unmarshal the JSON-encoded raw object into a KubernetesEvent object
err := json.Unmarshal(buffer.Bytes(), &resourceObject)
if err != nil {
// Log the error if unmarshalling fails
log.Printf("Failed to unmarshal resource object: %v", err)
// handle error as necessary
log.Printf("Failed to unmarshal resource object:\n%v\nError:\n%v", rawObj, err)
} else {
// If unmarshalling is successful, determine whether to set the unstructured object's content based on the "isNew" flag
if isNew {
Expand All @@ -214,45 +232,72 @@ func EventObject(rawObj map[string]interface{}, isNew bool) (resourceObject comm
return resourceObject
}

func StructResourceLog(event map[string]interface{}) (isStructured bool) {
var msg string
// StructResourceLog receives an event and logs it in a structured format.
func StructResourceLog(event map[string]interface{}) (isStructured bool, marshaledEvent []byte) {

// Check if event is nil
if event == nil {
log.Println("[ERROR] Event is nil")
return false
return false, nil
}

// Assert that event["eventType"] is a string
eventType, ok := event["eventType"].(string)
if !ok {
log.Println("[ERROR] eventType is not a string")
return false
return false, nil
}

// Initialize an empty LogEvent
logEvent := &common.LogEvent{}

// Marshal the event to a string
eventStr, _ := json.Marshal(event)
json.Unmarshal(eventStr, logEvent)

// Unmarshal the string back to a logEvent
err := json.Unmarshal(eventStr, logEvent)
if err != nil {

return false, nil
}

// Get the new event object
newEventObj := EventObject(logEvent.NewObject, true)

// Get the resource details from the new event object
resourceKind := newEventObj.Kind
resourceName := newEventObj.KubernetesMetadata.Name
resourceNamespace := newEventObj.KubernetesMetadata.Namespace
newResourceVersion := newEventObj.ResourceVersion

var msg string
// If event is a modification event, get the old event object and parse the event message accordingly
if eventType == "MODIFIED" {
oldEventObj := EventObject(logEvent.OldObject, false)
oldResourceName := oldEventObj.KubernetesMetadata.Name
oldResourceNamespace := oldEventObj.KubernetesMetadata.Namespace
oldResourceVersion := oldEventObj.KubernetesMetadata.ResourceVersion
msg = common.ParseEventMessage(eventType, oldResourceName, resourceKind, oldResourceNamespace, newResourceVersion, oldResourceVersion)

} else {
// If event is not a modification event, parse the event message with only the new event object
msg = common.ParseEventMessage(eventType, resourceName, resourceKind, resourceNamespace, newResourceVersion)
}

// Get the related cluster services for the resource
event["relatedClusterServices"] = GetClusterRelatedResources(resourceKind, resourceName, resourceNamespace)
event["message"] = msg

marshaledEvent, err := json.Marshal(event)
// Marshal the event to a string
marshaledEvent, err = json.Marshal(event)
if err != nil {
log.Printf("[ERROR] Failed to marshel resource event logs.\nERROR:\n%v", err)
}
common.SendLog(msg, marshaledEvent)

// Mark the goroutine as done
defer wg.Done()
isStructured = true

return isStructured
// Return true indicating the log is structured
isStructured = true

return isStructured, marshaledEvent
}
109 changes: 60 additions & 49 deletions resources/resourceInformer_test.go
Original file line number Diff line number Diff line change
@@ -1,84 +1,95 @@
package resources

import (
"github.com/stretchr/testify/mock"
"encoding/json"
"fmt"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic/dynamicinformer"
fakeDynamic "k8s.io/client-go/dynamic/fake"
"k8s.io/client-go/tools/cache"
"log"
"sigs.k8s.io/yaml"
"testing"
)

func createFakeResourceInformer(gvr schema.GroupVersionResource) cache.SharedIndexInformer {
fakeDynamicClient := fakeDynamic.NewSimpleDynamicClient(runtime.NewScheme())
func createFakeResourceInformer(gvr schema.GroupVersionResource, fakeDynamicClient *fakeDynamic.FakeDynamicClient) (fakeResourceInformer cache.SharedIndexInformer) {
factory := dynamicinformer.NewFilteredDynamicSharedInformerFactory(fakeDynamicClient, 0, corev1.NamespaceAll, nil)
fakeResourceInformer := factory.ForResource(gvr).Informer()
fakeResourceInformer = factory.ForResource(gvr).Informer()
if fakeResourceInformer == nil {
log.Printf("[ERROR] Resource Informer was not created")
log.Fatalf("[ERROR] Resource Informer was not created") // program will exit if this happens
} else {
log.Printf("Resource Informer created successfully")
}
return fakeResourceInformer
}

func TestCreateResourceInformer(t *testing.T) {
fakeDynamicClient := fakeDynamic.NewSimpleDynamicClient(runtime.NewScheme())
resourceGVR := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}
informer := createFakeResourceInformer(resourceGVR)
informer := createFakeResourceInformer(resourceGVR, fakeDynamicClient)

if informer == nil {
t.Errorf("Failed to create resource informer")
t.Fatalf("Failed to create resource informer") // test will fail if this happens
}
}

// Define an interface that includes the function you want to mock
type InformerCreator interface {
createFakeResourceInformer(gvr schema.GroupVersionResource, dynamicClient *fakeDynamic.FakeDynamicClient) cache.SharedIndexInformer
}
func TestEventObject(t *testing.T) {
testDeployment := GetTestDeployment()
// Marshal the struct to JSON
jsonData, err := yaml.Marshal(testDeployment)
if err != nil {
fmt.Printf("error: %s", err)
return
}

// Unmarshal the JSON to a map
var deploymentMap map[string]interface{}
err = yaml.Unmarshal(jsonData, &deploymentMap)
if err != nil {
fmt.Printf("error: %s", err)
return
}
deploymentMap["eventType"] = "ADDED"
deploymentMap["kind"] = "Deployment"
deploymentMap["newObject"] = &deploymentMap
eventObject := EventObject(deploymentMap, true)

if eventObject.Kind != "Deployment" {
t.Errorf("Failed to create event object, expected kind Deployment, got %s", eventObject.Kind)
}

if eventObject.KubernetesMetadata.Name != "test-deployment" {
t.Errorf("Failed to create event object, expected name test-deployment, got %s", eventObject.KubernetesMetadata.Name)
}

// Have your mock type implement the interface
type MockInformerCreator struct {
mock.Mock
if eventObject.KubernetesMetadata.Namespace != "default" {
t.Errorf("Failed to create event object, expected namespace default, got %s", eventObject.KubernetesMetadata.Namespace)
}
}

// Replace createResourceInformer with an instance of the interface
func TestStructResourceLog(t *testing.T) {
var deploymentMap map[string]interface{}
testDeployment := GetTestDeployment()
jsonDeployment, err := json.Marshal(testDeployment)
if err != nil {
t.Errorf("Failed to marshal test deployment.\nError:\n %v", err)
}

func TestAddEventHandlers(t *testing.T) {
// Create a new mock informer creator
fakeDynamicClient := fakeDynamic.NewSimpleDynamicClient(runtime.NewScheme())
mockInformerCreator := new(MockInformerCreator)
mockInformer := createFakeResourceInformer(schema.GroupVersionResource{Group: "", Version: "v1", Resource: "deployments"})
// Define what should be returned when the mock is called
mockInformerCreator.On("CreateFakeResourceInformer", mock.Anything, mock.Anything).Return(mockInformer)
err = json.Unmarshal(jsonDeployment, &deploymentMap)
if err != nil {
t.Errorf("Failed to unmarshal test deployment.\nError:\n %v", err)
}
deploymentEventMap := map[string]interface{}{

// Run the function that you're testing
AddEventHandlers()
"eventType": "ADDED",
"kind": "Deployment",
"newObject": deploymentMap,
}
isStructured, _ := StructResourceLog(deploymentEventMap)

// Check that the mock was called with the expected parameters
mockInformerCreator.AssertCalled(t, "CreateFakeResourceInformer", schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, fakeDynamicClient)
mockInformerCreator.AssertCalled(t, "CreateFakeResourceInformer", schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}, fakeDynamicClient)
// ... add more assertions here ...
}
func (m *MockInformerCreator) CreateFakeResourceInformer(gvr schema.GroupVersionResource, dynamicClient *fakeDynamic.FakeDynamicClient) cache.SharedIndexInformer {
args := m.Called(gvr, dynamicClient)
return args.Get(0).(cache.SharedIndexInformer)
if !isStructured {
t.Errorf("Failed to structure resource log")
}
}

//func TestAddInformerEventHandler(t *testing.T) {
// resourceGVR := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}
// mockDynamicClient := fakeDynamic.NewSimpleDynamicClient(runtime.NewScheme())
//
// informer := CreateFakeResourceInformer(resourceGVR, mockDynamicClient)
//
// if informer == nil {
// t.Errorf("Failed to create resource informer")
// }
//
// synced := AddInformerEventHandler(informer)
//
// if !synced {
// t.Errorf("Failed to add event handler for informer")
// }
//}
Loading

0 comments on commit 1509f85

Please sign in to comment.