mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-04 07:49:35 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			255 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			255 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2015 The Kubernetes Authors All rights reserved.
 | 
						|
 | 
						|
Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
you may not use this file except in compliance with the License.
 | 
						|
You may obtain a copy of the License at
 | 
						|
 | 
						|
    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
Unless required by applicable law or agreed to in writing, software
 | 
						|
distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
See the License for the specific language governing permissions and
 | 
						|
limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package record
 | 
						|
 | 
						|
import (
 | 
						|
	"reflect"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"k8s.io/kubernetes/pkg/api"
 | 
						|
	"k8s.io/kubernetes/pkg/api/unversioned"
 | 
						|
	"k8s.io/kubernetes/pkg/util"
 | 
						|
	"k8s.io/kubernetes/pkg/util/diff"
 | 
						|
)
 | 
						|
 | 
						|
func makeObjectReference(kind, name, namespace string) api.ObjectReference {
 | 
						|
	return api.ObjectReference{
 | 
						|
		Kind:       kind,
 | 
						|
		Name:       name,
 | 
						|
		Namespace:  namespace,
 | 
						|
		UID:        "C934D34AFB20242",
 | 
						|
		APIVersion: "version",
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func makeEvent(reason, message string, involvedObject api.ObjectReference) api.Event {
 | 
						|
	eventTime := unversioned.Now()
 | 
						|
	event := api.Event{
 | 
						|
		Reason:         reason,
 | 
						|
		Message:        message,
 | 
						|
		InvolvedObject: involvedObject,
 | 
						|
		Source: api.EventSource{
 | 
						|
			Component: "kubelet",
 | 
						|
			Host:      "kublet.node1",
 | 
						|
		},
 | 
						|
		Count:          1,
 | 
						|
		FirstTimestamp: eventTime,
 | 
						|
		LastTimestamp:  eventTime,
 | 
						|
		Type:           api.EventTypeNormal,
 | 
						|
	}
 | 
						|
	return event
 | 
						|
}
 | 
						|
 | 
						|
func makeEvents(num int, template api.Event) []api.Event {
 | 
						|
	events := []api.Event{}
 | 
						|
	for i := 0; i < num; i++ {
 | 
						|
		events = append(events, template)
 | 
						|
	}
 | 
						|
	return events
 | 
						|
}
 | 
						|
 | 
						|
func makeUniqueEvents(num int) []api.Event {
 | 
						|
	events := []api.Event{}
 | 
						|
	kind := "Pod"
 | 
						|
	for i := 0; i < num; i++ {
 | 
						|
		reason := strings.Join([]string{"reason", string(i)}, "-")
 | 
						|
		message := strings.Join([]string{"message", string(i)}, "-")
 | 
						|
		name := strings.Join([]string{"pod", string(i)}, "-")
 | 
						|
		namespace := strings.Join([]string{"ns", string(i)}, "-")
 | 
						|
		involvedObject := makeObjectReference(kind, name, namespace)
 | 
						|
		events = append(events, makeEvent(reason, message, involvedObject))
 | 
						|
	}
 | 
						|
	return events
 | 
						|
}
 | 
						|
 | 
						|
func makeSimilarEvents(num int, template api.Event, messagePrefix string) []api.Event {
 | 
						|
	events := makeEvents(num, template)
 | 
						|
	for i := range events {
 | 
						|
		events[i].Message = strings.Join([]string{messagePrefix, string(i), events[i].Message}, "-")
 | 
						|
	}
 | 
						|
	return events
 | 
						|
}
 | 
						|
 | 
						|
func setCount(event api.Event, count int) api.Event {
 | 
						|
	event.Count = int32(count)
 | 
						|
	return event
 | 
						|
}
 | 
						|
 | 
						|
func validateEvent(messagePrefix string, actualEvent *api.Event, expectedEvent *api.Event, t *testing.T) (*api.Event, error) {
 | 
						|
	recvEvent := *actualEvent
 | 
						|
	expectCompression := expectedEvent.Count > 1
 | 
						|
	t.Logf("%v - expectedEvent.Count is %d\n", messagePrefix, expectedEvent.Count)
 | 
						|
	// Just check that the timestamp was set.
 | 
						|
	if recvEvent.FirstTimestamp.IsZero() || recvEvent.LastTimestamp.IsZero() {
 | 
						|
		t.Errorf("%v - timestamp wasn't set: %#v", messagePrefix, recvEvent)
 | 
						|
	}
 | 
						|
	actualFirstTimestamp := recvEvent.FirstTimestamp
 | 
						|
	actualLastTimestamp := recvEvent.LastTimestamp
 | 
						|
	if actualFirstTimestamp.Equal(actualLastTimestamp) {
 | 
						|
		if expectCompression {
 | 
						|
			t.Errorf("%v - FirstTimestamp (%q) and LastTimestamp (%q) must be different to indicate event compression happened, but were the same. Actual Event: %#v", messagePrefix, actualFirstTimestamp, actualLastTimestamp, recvEvent)
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		if expectedEvent.Count == 1 {
 | 
						|
			t.Errorf("%v - FirstTimestamp (%q) and LastTimestamp (%q) must be equal to indicate only one occurrence of the event, but were different. Actual Event: %#v", messagePrefix, actualFirstTimestamp, actualLastTimestamp, recvEvent)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	// Temp clear time stamps for comparison because actual values don't matter for comparison
 | 
						|
	recvEvent.FirstTimestamp = expectedEvent.FirstTimestamp
 | 
						|
	recvEvent.LastTimestamp = expectedEvent.LastTimestamp
 | 
						|
	// Check that name has the right prefix.
 | 
						|
	if n, en := recvEvent.Name, expectedEvent.Name; !strings.HasPrefix(n, en) {
 | 
						|
		t.Errorf("%v - Name '%v' does not contain prefix '%v'", messagePrefix, n, en)
 | 
						|
	}
 | 
						|
	recvEvent.Name = expectedEvent.Name
 | 
						|
	if e, a := expectedEvent, &recvEvent; !reflect.DeepEqual(e, a) {
 | 
						|
		t.Errorf("%v - diff: %s", messagePrefix, diff.ObjectGoPrintDiff(e, a))
 | 
						|
	}
 | 
						|
	recvEvent.FirstTimestamp = actualFirstTimestamp
 | 
						|
	recvEvent.LastTimestamp = actualLastTimestamp
 | 
						|
	return actualEvent, nil
 | 
						|
}
 | 
						|
 | 
						|
// TestDefaultEventFilterFunc ensures that no events are filtered
 | 
						|
func TestDefaultEventFilterFunc(t *testing.T) {
 | 
						|
	event := makeEvent("end-of-world", "it was fun", makeObjectReference("Pod", "pod1", "other"))
 | 
						|
	if DefaultEventFilterFunc(&event) {
 | 
						|
		t.Fatalf("DefaultEventFilterFunc should always return false")
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// TestEventAggregatorByReasonFunc ensures that two events are aggregated if they vary only by event.message
 | 
						|
func TestEventAggregatorByReasonFunc(t *testing.T) {
 | 
						|
	event1 := makeEvent("end-of-world", "it was fun", makeObjectReference("Pod", "pod1", "other"))
 | 
						|
	event2 := makeEvent("end-of-world", "it was awful", makeObjectReference("Pod", "pod1", "other"))
 | 
						|
	event3 := makeEvent("nevermind", "it was a bug", makeObjectReference("Pod", "pod1", "other"))
 | 
						|
 | 
						|
	aggKey1, localKey1 := EventAggregatorByReasonFunc(&event1)
 | 
						|
	aggKey2, localKey2 := EventAggregatorByReasonFunc(&event2)
 | 
						|
	aggKey3, _ := EventAggregatorByReasonFunc(&event3)
 | 
						|
 | 
						|
	if aggKey1 != aggKey2 {
 | 
						|
		t.Errorf("Expected %v equal %v", aggKey1, aggKey2)
 | 
						|
	}
 | 
						|
	if localKey1 == localKey2 {
 | 
						|
		t.Errorf("Expected %v to not equal %v", aggKey1, aggKey3)
 | 
						|
	}
 | 
						|
	if aggKey1 == aggKey3 {
 | 
						|
		t.Errorf("Expected %v to not equal %v", aggKey1, aggKey3)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// TestEventAggregatorByReasonMessageFunc validates the proper output for an aggregate message
 | 
						|
func TestEventAggregatorByReasonMessageFunc(t *testing.T) {
 | 
						|
	expected := "(events with common reason combined)"
 | 
						|
	event1 := makeEvent("end-of-world", "it was fun", makeObjectReference("Pod", "pod1", "other"))
 | 
						|
	if actual := EventAggregatorByReasonMessageFunc(&event1); expected != actual {
 | 
						|
		t.Errorf("Expected %v got %v", expected, actual)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// TestEventCorrelator validates proper counting, aggregation of events
 | 
						|
func TestEventCorrelator(t *testing.T) {
 | 
						|
	firstEvent := makeEvent("first", "i am first", makeObjectReference("Pod", "my-pod", "my-ns"))
 | 
						|
	duplicateEvent := makeEvent("duplicate", "me again", makeObjectReference("Pod", "my-pod", "my-ns"))
 | 
						|
	uniqueEvent := makeEvent("unique", "snowflake", makeObjectReference("Pod", "my-pod", "my-ns"))
 | 
						|
	similarEvent := makeEvent("similar", "similar message", makeObjectReference("Pod", "my-pod", "my-ns"))
 | 
						|
	aggregateEvent := makeEvent(similarEvent.Reason, EventAggregatorByReasonMessageFunc(&similarEvent), similarEvent.InvolvedObject)
 | 
						|
	scenario := map[string]struct {
 | 
						|
		previousEvents  []api.Event
 | 
						|
		newEvent        api.Event
 | 
						|
		expectedEvent   api.Event
 | 
						|
		intervalSeconds int
 | 
						|
	}{
 | 
						|
		"create-a-single-event": {
 | 
						|
			previousEvents:  []api.Event{},
 | 
						|
			newEvent:        firstEvent,
 | 
						|
			expectedEvent:   setCount(firstEvent, 1),
 | 
						|
			intervalSeconds: 5,
 | 
						|
		},
 | 
						|
		"the-same-event-should-just-count": {
 | 
						|
			previousEvents:  makeEvents(1, duplicateEvent),
 | 
						|
			newEvent:        duplicateEvent,
 | 
						|
			expectedEvent:   setCount(duplicateEvent, 2),
 | 
						|
			intervalSeconds: 5,
 | 
						|
		},
 | 
						|
		"the-same-event-should-just-count-even-if-more-than-aggregate": {
 | 
						|
			previousEvents:  makeEvents(defaultAggregateMaxEvents, duplicateEvent),
 | 
						|
			newEvent:        duplicateEvent,
 | 
						|
			expectedEvent:   setCount(duplicateEvent, defaultAggregateMaxEvents+1),
 | 
						|
			intervalSeconds: 5,
 | 
						|
		},
 | 
						|
		"create-many-unique-events": {
 | 
						|
			previousEvents:  makeUniqueEvents(30),
 | 
						|
			newEvent:        uniqueEvent,
 | 
						|
			expectedEvent:   setCount(uniqueEvent, 1),
 | 
						|
			intervalSeconds: 5,
 | 
						|
		},
 | 
						|
		"similar-events-should-aggregate-event": {
 | 
						|
			previousEvents:  makeSimilarEvents(defaultAggregateMaxEvents-1, similarEvent, similarEvent.Message),
 | 
						|
			newEvent:        similarEvent,
 | 
						|
			expectedEvent:   setCount(aggregateEvent, 1),
 | 
						|
			intervalSeconds: 5,
 | 
						|
		},
 | 
						|
		"similar-events-many-times-should-count-the-aggregate": {
 | 
						|
			previousEvents:  makeSimilarEvents(defaultAggregateMaxEvents, similarEvent, similarEvent.Message),
 | 
						|
			newEvent:        similarEvent,
 | 
						|
			expectedEvent:   setCount(aggregateEvent, 2),
 | 
						|
			intervalSeconds: 5,
 | 
						|
		},
 | 
						|
		"similar-events-whose-interval-is-greater-than-aggregate-interval-do-not-aggregate": {
 | 
						|
			previousEvents:  makeSimilarEvents(defaultAggregateMaxEvents-1, similarEvent, similarEvent.Message),
 | 
						|
			newEvent:        similarEvent,
 | 
						|
			expectedEvent:   setCount(similarEvent, 1),
 | 
						|
			intervalSeconds: defaultAggregateIntervalInSeconds,
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for testScenario, testInput := range scenario {
 | 
						|
		eventInterval := time.Duration(testInput.intervalSeconds) * time.Second
 | 
						|
		clock := util.IntervalClock{Time: time.Now(), Duration: eventInterval}
 | 
						|
		correlator := NewEventCorrelator(&clock)
 | 
						|
		for i := range testInput.previousEvents {
 | 
						|
			event := testInput.previousEvents[i]
 | 
						|
			now := unversioned.NewTime(clock.Now())
 | 
						|
			event.FirstTimestamp = now
 | 
						|
			event.LastTimestamp = now
 | 
						|
			result, err := correlator.EventCorrelate(&event)
 | 
						|
			if err != nil {
 | 
						|
				t.Errorf("scenario %v: unexpected error playing back prevEvents %v", testScenario, err)
 | 
						|
			}
 | 
						|
			correlator.UpdateState(result.Event)
 | 
						|
		}
 | 
						|
 | 
						|
		// update the input to current clock value
 | 
						|
		now := unversioned.NewTime(clock.Now())
 | 
						|
		testInput.newEvent.FirstTimestamp = now
 | 
						|
		testInput.newEvent.LastTimestamp = now
 | 
						|
		result, err := correlator.EventCorrelate(&testInput.newEvent)
 | 
						|
		if err != nil {
 | 
						|
			t.Errorf("scenario %v: unexpected error correlating input event %v", testScenario, err)
 | 
						|
		}
 | 
						|
 | 
						|
		_, err = validateEvent(testScenario, result.Event, &testInput.expectedEvent, t)
 | 
						|
		if err != nil {
 | 
						|
			t.Errorf("scenario %v: unexpected error validating result %v", testScenario, err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |