diff --git a/cmd/kube-apiserver/app/aggregator.go b/cmd/kube-apiserver/app/aggregator.go index 571c5fd59f7d..04a33d166fc8 100644 --- a/cmd/kube-apiserver/app/aggregator.go +++ b/cmd/kube-apiserver/app/aggregator.go @@ -252,6 +252,7 @@ var apiVersionPriorities = map[schema.GroupVersion]priority{ {Group: "extensions", Version: "v1beta1"}: {group: 17900, version: 1}, // to my knowledge, nothing below here collides {Group: "apps", Version: "v1"}: {group: 17800, version: 15}, + {Group: "events.k8s.io", Version: "v1"}: {group: 17750, version: 15}, {Group: "events.k8s.io", Version: "v1beta1"}: {group: 17750, version: 5}, {Group: "authentication.k8s.io", Version: "v1"}: {group: 17700, version: 15}, {Group: "authentication.k8s.io", Version: "v1beta1"}: {group: 17700, version: 9}, diff --git a/hack/.golint_failures b/hack/.golint_failures index ca1c26271adb..796b020e17bd 100644 --- a/hack/.golint_failures +++ b/hack/.golint_failures @@ -28,6 +28,7 @@ pkg/apis/core/v1/helper/qos pkg/apis/core/validation pkg/apis/discovery/v1alpha1 pkg/apis/discovery/v1beta1 +pkg/apis/events/v1 pkg/apis/events/v1beta1 pkg/apis/extensions/v1beta1 pkg/apis/flowcontrol/v1alpha1 @@ -228,6 +229,7 @@ staging/src/k8s.io/api/certificates/v1beta1 staging/src/k8s.io/api/coordination/v1 staging/src/k8s.io/api/coordination/v1beta1 staging/src/k8s.io/api/core/v1 +staging/src/k8s.io/api/events/v1 staging/src/k8s.io/api/events/v1beta1 staging/src/k8s.io/api/extensions/v1beta1 staging/src/k8s.io/api/imagepolicy/v1alpha1 diff --git a/hack/.import-aliases b/hack/.import-aliases index 4c68ee3adbd7..7f5820167901 100644 --- a/hack/.import-aliases +++ b/hack/.import-aliases @@ -19,6 +19,7 @@ "k8s.io/api/core/v1": "v1", "k8s.io/api/discovery/v1alpha1": "discoveryv1alpha1", "k8s.io/api/discovery/v1beta1": "discoveryv1beta1", + "k8s.io/api/events/v1": "eventsv1", "k8s.io/api/events/v1beta1": "eventsv1beta1", "k8s.io/api/extensions/v1beta1": "extensionsv1beta1", "k8s.io/api/imagepolicy/v1alpha1": "imagepolicyv1alpha1", diff --git a/hack/lib/init.sh b/hack/lib/init.sh index b4681778aa37..8b77b29cf444 100755 --- a/hack/lib/init.sh +++ b/hack/lib/init.sh @@ -87,6 +87,7 @@ coordination.k8s.io/v1 \ discovery.k8s.io/v1alpha1 \ discovery.k8s.io/v1beta1 \ extensions/v1beta1 \ +events.k8s.io/v1 \ events.k8s.io/v1beta1 \ imagepolicy.k8s.io/v1alpha1 \ networking.k8s.io/v1 \ diff --git a/pkg/apis/core/validation/events.go b/pkg/apis/core/validation/events.go index 971918d53720..a0f92e9d3818 100644 --- a/pkg/apis/core/validation/events.go +++ b/pkg/apis/core/validation/events.go @@ -18,9 +18,14 @@ package validation import ( "fmt" + "reflect" "time" + "k8s.io/api/core/v1" + eventsv1beta1 "k8s.io/api/events/v1beta1" + apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/kubernetes/pkg/apis/core" @@ -33,8 +38,88 @@ const ( NoteLengthLimit = 1024 ) -// ValidateEvent makes sure that the event makes sense. -func ValidateEvent(event *core.Event) field.ErrorList { +func ValidateEventCreate(event *core.Event, requestVersion schema.GroupVersion) field.ErrorList { + // Make sure events always pass legacy validation. + allErrs := legacyValidateEvent(event) + if requestVersion == v1.SchemeGroupVersion || requestVersion == eventsv1beta1.SchemeGroupVersion { + // No further validation for backwards compatibility. + return allErrs + } + + // Strict validation applies to creation via events.k8s.io/v1 API and newer. + allErrs = append(allErrs, ValidateObjectMeta(&event.ObjectMeta, true, apimachineryvalidation.NameIsDNSSubdomain, field.NewPath("metadata"))...) + allErrs = append(allErrs, validateV1EventSeries(event)...) + zeroTime := time.Time{} + if event.EventTime.Time == zeroTime { + allErrs = append(allErrs, field.Required(field.NewPath("eventTime"), "")) + } + if event.Type != v1.EventTypeNormal && event.Type != v1.EventTypeWarning { + allErrs = append(allErrs, field.Invalid(field.NewPath("type"), "", fmt.Sprintf("has invalid value: %v", event.Type))) + } + if event.FirstTimestamp.Time != zeroTime { + allErrs = append(allErrs, field.Invalid(field.NewPath("firstTimestamp"), "", "needs to be unset")) + } + if event.LastTimestamp.Time != zeroTime { + allErrs = append(allErrs, field.Invalid(field.NewPath("lastTimestamp"), "", "needs to be unset")) + } + if event.Count != 0 { + allErrs = append(allErrs, field.Invalid(field.NewPath("count"), "", "needs to be unset")) + } + if event.Source.Component != "" || event.Source.Host != "" { + allErrs = append(allErrs, field.Invalid(field.NewPath("source"), "", "needs to be unset")) + } + return allErrs +} + +func ValidateEventUpdate(newEvent, oldEvent *core.Event, requestVersion schema.GroupVersion) field.ErrorList { + // Make sure the new event always passes legacy validation. + allErrs := legacyValidateEvent(newEvent) + if requestVersion == v1.SchemeGroupVersion || requestVersion == eventsv1beta1.SchemeGroupVersion { + // No further validation for backwards compatibility. + return allErrs + } + + // Strict validation applies to update via events.k8s.io/v1 API and newer. + allErrs = append(allErrs, ValidateObjectMetaUpdate(&newEvent.ObjectMeta, &oldEvent.ObjectMeta, field.NewPath("metadata"))...) + // if the series was modified, validate the new data + if !reflect.DeepEqual(newEvent.Series, oldEvent.Series) { + allErrs = append(allErrs, validateV1EventSeries(newEvent)...) + } + + allErrs = append(allErrs, ValidateImmutableField(newEvent.InvolvedObject, oldEvent.InvolvedObject, field.NewPath("involvedObject"))...) + allErrs = append(allErrs, ValidateImmutableField(newEvent.Reason, oldEvent.Reason, field.NewPath("reason"))...) + allErrs = append(allErrs, ValidateImmutableField(newEvent.Message, oldEvent.Message, field.NewPath("message"))...) + allErrs = append(allErrs, ValidateImmutableField(newEvent.Source, oldEvent.Source, field.NewPath("source"))...) + allErrs = append(allErrs, ValidateImmutableField(newEvent.FirstTimestamp, oldEvent.FirstTimestamp, field.NewPath("firstTimestamp"))...) + allErrs = append(allErrs, ValidateImmutableField(newEvent.LastTimestamp, oldEvent.LastTimestamp, field.NewPath("lastTimestamp"))...) + allErrs = append(allErrs, ValidateImmutableField(newEvent.Count, oldEvent.Count, field.NewPath("count"))...) + allErrs = append(allErrs, ValidateImmutableField(newEvent.Reason, oldEvent.Reason, field.NewPath("reason"))...) + allErrs = append(allErrs, ValidateImmutableField(newEvent.Type, oldEvent.Type, field.NewPath("type"))...) + allErrs = append(allErrs, ValidateImmutableField(newEvent.EventTime, oldEvent.EventTime, field.NewPath("eventTime"))...) + allErrs = append(allErrs, ValidateImmutableField(newEvent.Action, oldEvent.Action, field.NewPath("action"))...) + allErrs = append(allErrs, ValidateImmutableField(newEvent.Related, oldEvent.Related, field.NewPath("related"))...) + allErrs = append(allErrs, ValidateImmutableField(newEvent.ReportingController, oldEvent.ReportingController, field.NewPath("reportingController"))...) + allErrs = append(allErrs, ValidateImmutableField(newEvent.ReportingInstance, oldEvent.ReportingInstance, field.NewPath("reportingInstance"))...) + + return allErrs +} + +func validateV1EventSeries(event *core.Event) field.ErrorList { + allErrs := field.ErrorList{} + zeroTime := time.Time{} + if event.Series != nil { + if event.Series.Count < 2 { + allErrs = append(allErrs, field.Invalid(field.NewPath("series.count"), "", fmt.Sprintf("should be at least 2"))) + } + if event.Series.LastObservedTime.Time == zeroTime { + allErrs = append(allErrs, field.Required(field.NewPath("series.lastObservedTime"), "")) + } + } + return allErrs +} + +// legacyValidateEvent makes sure that the event makes sense. +func legacyValidateEvent(event *core.Event) field.ErrorList { allErrs := field.ErrorList{} // Because go zeroTime := time.Time{} diff --git a/pkg/apis/core/validation/events_test.go b/pkg/apis/core/validation/events_test.go index 835862ba983b..e9a2f69c3166 100644 --- a/pkg/apis/core/validation/events_test.go +++ b/pkg/apis/core/validation/events_test.go @@ -20,11 +20,14 @@ import ( "testing" "time" + "k8s.io/api/core/v1" + eventsv1 "k8s.io/api/events/v1" + eventsv1beta1 "k8s.io/api/events/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/kubernetes/pkg/apis/core" ) -func TestValidateEvent(t *testing.T) { +func TestValidateEventForCoreV1Events(t *testing.T) { table := []struct { *core.Event valid bool @@ -214,13 +217,18 @@ func TestValidateEvent(t *testing.T) { } for _, item := range table { - if e, a := item.valid, len(ValidateEvent(item.Event)) == 0; e != a { - t.Errorf("%v: expected %v, got %v: %v", item.Event.Name, e, a, ValidateEvent(item.Event)) + createErrs := ValidateEventCreate(item.Event, v1.SchemeGroupVersion) + if e, a := item.valid, len(createErrs) == 0; e != a { + t.Errorf("%v: expected %v, got %v: %v", item.Event.Name, e, a, createErrs) + } + updateErrs := ValidateEventUpdate(item.Event, &core.Event{}, v1.SchemeGroupVersion) + if e, a := item.valid, len(updateErrs) == 0; e != a { + t.Errorf("%v: expected %v, got %v: %v", item.Event.Name, e, a, updateErrs) } } } -func TestValidateNewEvent(t *testing.T) { +func TestValidateEventForNewV1beta1Events(t *testing.T) { someTime := metav1.MicroTime{Time: time.Unix(1505828956, 0)} table := []struct { *core.Event @@ -384,8 +392,923 @@ zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz`, } for _, item := range table { - if e, a := item.valid, len(ValidateEvent(item.Event)) == 0; e != a { - t.Errorf("%v: expected %v, got %v: %v", item.msg, e, a, ValidateEvent(item.Event)) + createErrs := ValidateEventCreate(item.Event, eventsv1beta1.SchemeGroupVersion) + if e, a := item.valid, len(createErrs) == 0; e != a { + t.Errorf("%v: expected %v, got %v: %v", item.msg, e, a, createErrs) + } + updateErrs := ValidateEventUpdate(item.Event, &core.Event{}, eventsv1beta1.SchemeGroupVersion) + if e, a := item.valid, len(updateErrs) == 0; e != a { + t.Errorf("%v: expected %v, got %v: %v", item.msg, e, a, updateErrs) + } + } +} + +func TestValidateEventCreateForNewV1Events(t *testing.T) { + someTime := metav1.MicroTime{Time: time.Unix(1505828956, 0)} + table := []struct { + *core.Event + valid bool + msg string + }{ + { + Event: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Because", + Type: "Normal", + }, + valid: true, + msg: "valid new event", + }, + { + Event: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: metav1.NamespaceSystem, + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Reason: "Because", + }, + valid: false, + msg: "missing name in objectMeta", + }, + { + Event: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Reason: "Because", + }, + valid: false, + msg: "missing namespace in objectMeta", + }, + { + Event: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceDefault, + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + }, + valid: false, + msg: "missing EventTime", + }, + { + Event: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "my-contr@ller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Because", + }, + valid: false, + msg: "not qualified reportingController", + }, + { + Event: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", + Action: "Do", + Reason: "Because", + }, + valid: false, + msg: "too long reporting instance", + }, + { + Event: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + }, + valid: false, + msg: "missing reason", + }, + { + Event: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Reason: "Because", + }, + valid: false, + msg: "missing action", + }, + { + Event: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Because", + Message: `zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz +zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz +zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz +zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz +zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz +zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz +zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz +zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz +zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz +zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz +zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz`, + }, + valid: false, + msg: "too long message", + }, + { + Event: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Because", + Type: "invalid-type", + }, + valid: false, + msg: "invalid type", + }, + { + Event: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Because", + Type: "Normal", + FirstTimestamp: metav1.Time{Time: time.Unix(1505828956, 0)}, + }, + valid: false, + msg: "non-empty firstTimestamp", + }, + { + Event: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Because", + Type: "Normal", + LastTimestamp: metav1.Time{Time: time.Unix(1505828956, 0)}, + }, + valid: false, + msg: "non-empty lastTimestamp", + }, + { + Event: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Because", + Type: "Normal", + Count: 123, + }, + valid: false, + msg: "non-empty count", + }, + { + Event: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Because", + Type: "Normal", + Source: core.EventSource{ + Host: "host", + }, + }, + valid: false, + msg: "non-empty source", + }, + { + Event: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Because", + Type: "Normal", + Series: &core.EventSeries{ + Count: 0, + LastObservedTime: someTime, + }, + }, + valid: false, + msg: "non-nil series with cound < 2", + }, + { + Event: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Because", + Type: "Normal", + Series: &core.EventSeries{ + Count: 2, + }, + }, + valid: false, + msg: "non-nil series with empty lastObservedTime", + }, + } + + for _, item := range table { + createErrs := ValidateEventCreate(item.Event, eventsv1.SchemeGroupVersion) + if e, a := item.valid, len(createErrs) == 0; e != a { + t.Errorf("%v: expected %v, got %v: %v", item.msg, e, a, createErrs) + } + } +} + +func TestValidateEventUpdateForNewV1Events(t *testing.T) { + someTime := metav1.MicroTime{Time: time.Unix(1505828956, 0)} + table := []struct { + newEvent *core.Event + oldEvent *core.Event + valid bool + msg string + }{ + { + newEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "2", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v2", + Kind: "Node", + }, + Series: &core.EventSeries{ + Count: 2, + LastObservedTime: someTime, + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + oldEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "2", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v2", + Kind: "Node", + }, + Series: &core.EventSeries{ + Count: 1, + LastObservedTime: someTime, + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + valid: true, + msg: "valid new updated event", + }, + { + newEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v2", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + oldEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + valid: false, + msg: "forbidden updates to involvedObject", + }, + { + newEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees-new", + Type: "Normal", + }, + oldEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + valid: false, + msg: "forbidden updates to reason", + }, + { + newEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + Message: "new-message", + }, + oldEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + Message: "message", + }, + valid: false, + msg: "forbidden updates to message", + }, + { + newEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + oldEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + Source: core.EventSource{ + Host: "host", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + valid: false, + msg: "forbidden updates to source", + }, + { + newEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + oldEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + FirstTimestamp: metav1.Time{Time: time.Unix(1505828956, 0)}, + }, + valid: false, + msg: "forbidden updates to firstTimestamp", + }, + { + newEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + oldEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + LastTimestamp: metav1.Time{Time: time.Unix(1505828956, 0)}, + }, + valid: false, + msg: "forbidden updates to lastTimestamp", + }, + { + newEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + oldEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + Count: 2, + }, + valid: false, + msg: "forbidden updates to count", + }, + { + newEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Warning", + }, + oldEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + valid: false, + msg: "forbidden updates to type", + }, + { + newEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: metav1.MicroTime{Time: time.Unix(1505828999, 0)}, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + oldEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + valid: false, + msg: "forbidden updates to eventTime", + }, + { + newEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Undo", + Reason: "Yeees", + Type: "Normal", + }, + oldEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + valid: false, + msg: "forbidden updates to action", + }, + { + newEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + Related: &core.ObjectReference{ + APIVersion: "v1", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + oldEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + valid: false, + msg: "forbidden updates to related", + }, + { + newEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller/new", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + oldEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + valid: false, + msg: "forbidden updates to reportingController", + }, + { + newEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz-new", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + oldEvent: &core.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: metav1.NamespaceSystem, + ResourceVersion: "1", + }, + InvolvedObject: core.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + }, + EventTime: someTime, + ReportingController: "k8s.io/my-controller", + ReportingInstance: "node-xyz", + Action: "Do", + Reason: "Yeees", + Type: "Normal", + }, + valid: false, + msg: "forbidden updates to reportingInstance", + }, + } + + for _, item := range table { + updateErrs := ValidateEventUpdate(item.newEvent, item.oldEvent, eventsv1.SchemeGroupVersion) + if e, a := item.valid, len(updateErrs) == 0; e != a { + t.Errorf("%v: expected %v, got %v: %v", item.msg, e, a, updateErrs) } } } diff --git a/pkg/apis/events/install/install.go b/pkg/apis/events/install/install.go index 28ac59e9170c..71ce8a7060e3 100644 --- a/pkg/apis/events/install/install.go +++ b/pkg/apis/events/install/install.go @@ -23,6 +23,7 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/apis/events" + "k8s.io/kubernetes/pkg/apis/events/v1" "k8s.io/kubernetes/pkg/apis/events/v1beta1" ) @@ -34,5 +35,6 @@ func init() { func Install(scheme *runtime.Scheme) { utilruntime.Must(events.AddToScheme(scheme)) utilruntime.Must(v1beta1.AddToScheme(scheme)) - utilruntime.Must(scheme.SetVersionPriority(v1beta1.SchemeGroupVersion)) + utilruntime.Must(v1.AddToScheme(scheme)) + utilruntime.Must(scheme.SetVersionPriority(v1beta1.SchemeGroupVersion, v1.SchemeGroupVersion)) } diff --git a/pkg/apis/events/v1/conversion.go b/pkg/apis/events/v1/conversion.go new file mode 100644 index 000000000000..379ceeb96302 --- /dev/null +++ b/pkg/apis/events/v1/conversion.go @@ -0,0 +1,58 @@ +/* +Copyright 2020 The Kubernetes Authors. + +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 v1 + +import ( + v1 "k8s.io/api/events/v1" + conversion "k8s.io/apimachinery/pkg/conversion" + k8s_api "k8s.io/kubernetes/pkg/apis/core" + k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1" +) + +func Convert_v1_Event_To_core_Event(in *v1.Event, out *k8s_api.Event, s conversion.Scope) error { + if err := autoConvert_v1_Event_To_core_Event(in, out, s); err != nil { + return err + } + if err := k8s_api_v1.Convert_v1_ObjectReference_To_core_ObjectReference(&in.Regarding, &out.InvolvedObject, s); err != nil { + return err + } + if err := k8s_api_v1.Convert_v1_EventSource_To_core_EventSource(&in.DeprecatedSource, &out.Source, s); err != nil { + return err + } + out.Message = in.Note + out.FirstTimestamp = in.DeprecatedFirstTimestamp + out.LastTimestamp = in.DeprecatedLastTimestamp + out.Count = in.DeprecatedCount + return nil +} + +func Convert_core_Event_To_v1_Event(in *k8s_api.Event, out *v1.Event, s conversion.Scope) error { + if err := autoConvert_core_Event_To_v1_Event(in, out, s); err != nil { + return err + } + if err := k8s_api_v1.Convert_core_ObjectReference_To_v1_ObjectReference(&in.InvolvedObject, &out.Regarding, s); err != nil { + return err + } + if err := k8s_api_v1.Convert_core_EventSource_To_v1_EventSource(&in.Source, &out.DeprecatedSource, s); err != nil { + return err + } + out.Note = in.Message + out.DeprecatedFirstTimestamp = in.FirstTimestamp + out.DeprecatedLastTimestamp = in.LastTimestamp + out.DeprecatedCount = in.Count + return nil +} diff --git a/pkg/apis/events/v1/doc.go b/pkg/apis/events/v1/doc.go new file mode 100644 index 000000000000..a5f69e470df9 --- /dev/null +++ b/pkg/apis/events/v1/doc.go @@ -0,0 +1,24 @@ +/* +Copyright 2020 The Kubernetes Authors. + +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. +*/ + +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/events +// +k8s:conversion-gen-external-types=k8s.io/api/events/v1 +// +k8s:defaulter-gen=TypeMeta +// +k8s:defaulter-gen-input=../../../../vendor/k8s.io/api/events/v1 + +// +groupName=events.k8s.io + +package v1 // import "k8s.io/kubernetes/pkg/apis/events/v1" diff --git a/pkg/apis/events/v1/register.go b/pkg/apis/events/v1/register.go new file mode 100644 index 000000000000..35785dc094f0 --- /dev/null +++ b/pkg/apis/events/v1/register.go @@ -0,0 +1,45 @@ +/* +Copyright 2020 The Kubernetes Authors. + +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 v1 + +import ( + eventsv1 "k8s.io/api/events/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// GroupName is the group name use in this package +const GroupName = "events.k8s.io" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + localSchemeBuilder = &eventsv1.SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(RegisterDefaults) +} diff --git a/pkg/master/master.go b/pkg/master/master.go index 14e50e06bb54..faf03a21936b 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -44,6 +44,7 @@ import ( coordinationapiv1beta1 "k8s.io/api/coordination/v1beta1" apiv1 "k8s.io/api/core/v1" discoveryv1beta1 "k8s.io/api/discovery/v1beta1" + eventsv1 "k8s.io/api/events/v1" eventsv1beta1 "k8s.io/api/events/v1beta1" extensionsapiv1beta1 "k8s.io/api/extensions/v1beta1" flowcontrolv1alpha1 "k8s.io/api/flowcontrol/v1alpha1" @@ -612,6 +613,7 @@ func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig { coordinationapiv1.SchemeGroupVersion, coordinationapiv1beta1.SchemeGroupVersion, discoveryv1beta1.SchemeGroupVersion, + eventsv1.SchemeGroupVersion, eventsv1beta1.SchemeGroupVersion, extensionsapiv1beta1.SchemeGroupVersion, networkingapiv1.SchemeGroupVersion, diff --git a/pkg/master/storageversionhashdata/data.go b/pkg/master/storageversionhashdata/data.go index 760d8fdad912..96321273102a 100644 --- a/pkg/master/storageversionhashdata/data.go +++ b/pkg/master/storageversionhashdata/data.go @@ -103,5 +103,6 @@ var GVRToStorageVersionHash = map[string]string{ "admissionregistration.k8s.io/v1beta1/validatingwebhookconfigurations": "P9NhrezfnWE=", "admissionregistration.k8s.io/v1/mutatingwebhookconfigurations": "yxW1cpLtfp8=", "admissionregistration.k8s.io/v1/validatingwebhookconfigurations": "P9NhrezfnWE=", + "events.k8s.io/v1/events": "r2yiGXH7wu8=", "events.k8s.io/v1beta1/events": "r2yiGXH7wu8=", } diff --git a/pkg/registry/core/event/storage/storage_test.go b/pkg/registry/core/event/storage/storage_test.go index c17323d713b5..134de72b8bf9 100644 --- a/pkg/registry/core/event/storage/storage_test.go +++ b/pkg/registry/core/event/storage/storage_test.go @@ -18,6 +18,7 @@ package storage import ( "testing" + "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -46,16 +47,26 @@ func newStorage(t *testing.T) (*REST, *etcd3testing.EtcdTestServer) { } func validNewEvent(namespace string) *api.Event { + someTime := metav1.MicroTime{Time: time.Unix(1505828956, 0)} return &api.Event{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", Namespace: namespace, }, - Reason: "forTesting", InvolvedObject: api.ObjectReference{ Name: "bar", Namespace: namespace, }, + EventTime: someTime, + ReportingController: "test-controller", + ReportingInstance: "test-node", + Action: "Do", + Reason: "forTesting", + Type: "Normal", + Series: &api.EventSeries{ + Count: 2, + LastObservedTime: someTime, + }, } } @@ -85,13 +96,13 @@ func TestUpdate(t *testing.T) { // valid updateFunc func(obj runtime.Object) runtime.Object { object := obj.(*api.Event) - object.Reason = "forDifferentTesting" + object.Series.Count = 100 return object }, // invalid updateFunc func(obj runtime.Object) runtime.Object { object := obj.(*api.Event) - object.InvolvedObject.Namespace = "different-namespace" + object.ReportingController = "" return object }, ) diff --git a/pkg/registry/core/event/strategy.go b/pkg/registry/core/event/strategy.go index d253dd53adaa..9c1c93c7c621 100644 --- a/pkg/registry/core/event/strategy.go +++ b/pkg/registry/core/event/strategy.go @@ -23,7 +23,9 @@ import ( "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" + genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/storage" @@ -57,8 +59,9 @@ func (eventStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Obje } func (eventStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { + groupVersion := requestGroupVersion(ctx) event := obj.(*api.Event) - return validation.ValidateEvent(event) + return validation.ValidateEventCreate(event, groupVersion) } // Canonicalize normalizes the object after validation. @@ -70,8 +73,10 @@ func (eventStrategy) AllowCreateOnUpdate() bool { } func (eventStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { + groupVersion := requestGroupVersion(ctx) event := obj.(*api.Event) - return validation.ValidateEvent(event) + oldEvent := obj.(*api.Event) + return validation.ValidateEventUpdate(event, oldEvent, groupVersion) } func (eventStrategy) AllowUnconditionalUpdate() bool { @@ -113,3 +118,11 @@ func ToSelectableFields(event *api.Event) fields.Set { } return generic.MergeFieldsSets(objectMetaFieldsSet, specificFieldsSet) } + +// requestGroupVersion returns the group/version associated with the given context, or a zero-value group/version. +func requestGroupVersion(ctx context.Context) schema.GroupVersion { + if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found { + return schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion} + } + return schema.GroupVersion{} +} diff --git a/pkg/registry/events/event/strategy.go b/pkg/registry/events/event/strategy.go index 4f556b3fef86..03d771c1e00d 100644 --- a/pkg/registry/events/event/strategy.go +++ b/pkg/registry/events/event/strategy.go @@ -23,13 +23,15 @@ import ( "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" + genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/generic" apistorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api/legacyscheme" - api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/apis/core/validation" + coreapi "k8s.io/kubernetes/pkg/apis/core" + corevalidation "k8s.io/kubernetes/pkg/apis/core/validation" ) // eventStrategy implements verification logic for Pod Presets. @@ -56,8 +58,9 @@ func (eventStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Obje // Validate validates a new Event. func (eventStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { - event := obj.(*api.Event) - return validation.ValidateEvent(event) + groupVersion := requestGroupVersion(ctx) + event := obj.(*coreapi.Event) + return corevalidation.ValidateEventCreate(event, groupVersion) } // Canonicalize normalizes the object after validation. @@ -70,8 +73,10 @@ func (eventStrategy) Canonicalize(obj runtime.Object) {} // ValidateUpdate is the default update validation for an end user. func (eventStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { - event := obj.(*api.Event) - return validation.ValidateEvent(event) + groupVersion := requestGroupVersion(ctx) + event := obj.(*coreapi.Event) + oldEvent := old.(*coreapi.Event) + return corevalidation.ValidateEventUpdate(event, oldEvent, groupVersion) } // AllowUnconditionalUpdate is the default update policy for Event objects. @@ -80,13 +85,13 @@ func (eventStrategy) AllowUnconditionalUpdate() bool { } // SelectableFields returns a field set that represents the object. -func SelectableFields(pip *api.Event) fields.Set { +func SelectableFields(pip *coreapi.Event) fields.Set { return generic.ObjectMetaFieldsSet(&pip.ObjectMeta, true) } // GetAttrs returns labels and fields of a given object for filtering purposes. func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) { - pip, ok := obj.(*api.Event) + pip, ok := obj.(*coreapi.Event) if !ok { return nil, nil, fmt.Errorf("given object is not a Event") } @@ -102,3 +107,11 @@ func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionP GetAttrs: GetAttrs, } } + +// requestGroupVersion returns the group/version associated with the given context, or a zero-value group/version. +func requestGroupVersion(ctx context.Context) schema.GroupVersion { + if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found { + return schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion} + } + return schema.GroupVersion{} +} diff --git a/pkg/registry/events/rest/storage_events.go b/pkg/registry/events/rest/storage_events.go index 0c9480e63d1c..6ee689e10af9 100644 --- a/pkg/registry/events/rest/storage_events.go +++ b/pkg/registry/events/rest/storage_events.go @@ -19,6 +19,7 @@ package rest import ( "time" + eventsapiv1 "k8s.io/api/events/v1" eventsapiv1beta1 "k8s.io/api/events/v1beta1" "k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/rest" @@ -45,6 +46,13 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag apiGroupInfo.VersionedResourcesStorageMap[eventsapiv1beta1.SchemeGroupVersion.Version] = storageMap } } + if apiResourceConfigSource.VersionEnabled(eventsapiv1.SchemeGroupVersion) { + if storageMap, err := p.v1Storage(apiResourceConfigSource, restOptionsGetter); err != nil { + return genericapiserver.APIGroupInfo{}, false, err + } else { + apiGroupInfo.VersionedResourcesStorageMap[eventsapiv1.SchemeGroupVersion.Version] = storageMap + } + } return apiGroupInfo, true, nil } @@ -61,6 +69,18 @@ func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorag return storage, err } +func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) { + storage := map[string]rest.Storage{} + // events + eventsStorage, err := eventstore.NewREST(restOptionsGetter, uint64(p.TTL.Seconds())) + if err != nil { + return storage, err + } + storage["events"] = eventsStorage + + return storage, err +} + func (p RESTStorageProvider) GroupName() string { return events.GroupName } diff --git a/staging/src/k8s.io/api/events/v1/doc.go b/staging/src/k8s.io/api/events/v1/doc.go new file mode 100644 index 000000000000..6e320e06340c --- /dev/null +++ b/staging/src/k8s.io/api/events/v1/doc.go @@ -0,0 +1,23 @@ +/* +Copyright 2020 The Kubernetes Authors. + +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. +*/ + +// +k8s:deepcopy-gen=package +// +k8s:protobuf-gen=package +// +k8s:openapi-gen=true + +// +groupName=events.k8s.io + +package v1 // import "k8s.io/api/events/v1" diff --git a/staging/src/k8s.io/api/events/v1/register.go b/staging/src/k8s.io/api/events/v1/register.go new file mode 100644 index 000000000000..ca90e6cbeed9 --- /dev/null +++ b/staging/src/k8s.io/api/events/v1/register.go @@ -0,0 +1,53 @@ +/* +Copyright 2020 The Kubernetes Authors. + +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 v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// GroupName is the group name use in this package +const GroupName = "events.k8s.io" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + // TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api. + // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes. + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &Event{}, + &EventList{}, + ) + + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/staging/src/k8s.io/api/events/v1/types.go b/staging/src/k8s.io/api/events/v1/types.go new file mode 100644 index 000000000000..07ede554224a --- /dev/null +++ b/staging/src/k8s.io/api/events/v1/types.go @@ -0,0 +1,119 @@ +/* +Copyright 2020 The Kubernetes Authors. + +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 v1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Event is a report of an event somewhere in the cluster. It generally denotes some state change in the system. +type Event struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // eventTime is the time when this Event was first observed. It is required. + EventTime metav1.MicroTime `json:"eventTime" protobuf:"bytes,2,opt,name=eventTime"` + + // series is data about the Event series this event represents or nil if it's a singleton Event. + // +optional + Series *EventSeries `json:"series,omitempty" protobuf:"bytes,3,opt,name=series"` + + // reportingController is the name of the controller that emitted this Event, e.g. `kubernetes.io/kubelet`. + // This field cannot be empty for new Events. + // +optional + ReportingController string `json:"reportingController,omitempty" protobuf:"bytes,4,opt,name=reportingController"` + + // reportingInstance is the ID of the controller instance, e.g. `kubelet-xyzf`. + // This field cannot be empty for new Events and it can have at most 128 characters. + // +optional + ReportingInstance string `json:"reportingInstance,omitempty" protobuf:"bytes,5,opt,name=reportingInstance"` + + // action is what action was taken/failed regarding to the regarding object. It is machine-readable. + // This field can have at most 128 characters. + // +optional + Action string `json:"action,omitempty" protobuf:"bytes,6,name=action"` + + // reason is why the action was taken. It is human-readable. + // This field can have at most 128 characters. + // +optional + Reason string `json:"reason,omitempty" protobuf:"bytes,7,name=reason"` + + // regarding contains the object this Event is about. In most cases it's an Object reporting controller + // implements, e.g. ReplicaSetController implements ReplicaSets and this event is emitted because + // it acts on some changes in a ReplicaSet object. + // +optional + Regarding corev1.ObjectReference `json:"regarding,omitempty" protobuf:"bytes,8,opt,name=regarding"` + + // related is the optional secondary object for more complex actions. E.g. when regarding object triggers + // a creation or deletion of related object. + // +optional + Related *corev1.ObjectReference `json:"related,omitempty" protobuf:"bytes,9,opt,name=related"` + + // note is a human-readable description of the status of this operation. + // Maximal length of the note is 1kB, but libraries should be prepared to + // handle values up to 64kB. + // +optional + Note string `json:"note,omitempty" protobuf:"bytes,10,opt,name=note"` + + // type is the type of this event (Normal, Warning), new types could be added in the future. + // It is machine-readable. + // +optional + Type string `json:"type,omitempty" protobuf:"bytes,11,opt,name=type"` + + // deprecatedSource is the deprecated field assuring backward compatibility with core.v1 Event type. + // +optional + DeprecatedSource corev1.EventSource `json:"deprecatedSource,omitempty" protobuf:"bytes,12,opt,name=deprecatedSource"` + // deprecatedFirstTimestamp is the deprecated field assuring backward compatibility with core.v1 Event type. + // +optional + DeprecatedFirstTimestamp metav1.Time `json:"deprecatedFirstTimestamp,omitempty" protobuf:"bytes,13,opt,name=deprecatedFirstTimestamp"` + // deprecatedLastTimestamp is the deprecated field assuring backward compatibility with core.v1 Event type. + // +optional + DeprecatedLastTimestamp metav1.Time `json:"deprecatedLastTimestamp,omitempty" protobuf:"bytes,14,opt,name=deprecatedLastTimestamp"` + // deprecatedCount is the deprecated field assuring backward compatibility with core.v1 Event type. + // +optional + DeprecatedCount int32 `json:"deprecatedCount,omitempty" protobuf:"varint,15,opt,name=deprecatedCount"` +} + +// EventSeries contain information on series of events, i.e. thing that was/is happening +// continuously for some time. How often to update the EventSeries is up to the event reporters. +// The default event reporter in "k8s.io/client-go/tools/events/event_broadcaster.go" shows +// how this struct is updated on heartbeats and can guide customized reporter implementations. +type EventSeries struct { + // count is the number of occurrences in this series up to the last heartbeat time. + Count int32 `json:"count" protobuf:"varint,1,opt,name=count"` + // lastObservedTime is the time when last Event from the series was seen before last heartbeat. + LastObservedTime metav1.MicroTime `json:"lastObservedTime" protobuf:"bytes,2,opt,name=lastObservedTime"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// EventList is a list of Event objects. +type EventList struct { + metav1.TypeMeta `json:",inline"` + // Standard list metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // +optional + metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // items is a list of schema objects. + Items []Event `json:"items" protobuf:"bytes,2,rep,name=items"` +} diff --git a/staging/src/k8s.io/api/events/v1beta1/types.go b/staging/src/k8s.io/api/events/v1beta1/types.go index 03a7518f5673..e2ed214b0639 100644 --- a/staging/src/k8s.io/api/events/v1beta1/types.go +++ b/staging/src/k8s.io/api/events/v1beta1/types.go @@ -32,60 +32,65 @@ type Event struct { // +optional metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - // Required. Time when this Event was first observed. + // eventTime is the time when this Event was first observed. It is required. EventTime metav1.MicroTime `json:"eventTime" protobuf:"bytes,2,opt,name=eventTime"` - // Data about the Event series this event represents or nil if it's a singleton Event. + // series is data about the Event series this event represents or nil if it's a singleton Event. // +optional Series *EventSeries `json:"series,omitempty" protobuf:"bytes,3,opt,name=series"` - // Name of the controller that emitted this Event, e.g. `kubernetes.io/kubelet`. + // reportingController is the name of the controller that emitted this Event, e.g. `kubernetes.io/kubelet`. + // This field cannot be empty for new Events. // +optional ReportingController string `json:"reportingController,omitempty" protobuf:"bytes,4,opt,name=reportingController"` - // ID of the controller instance, e.g. `kubelet-xyzf`. + // reportingInstance is the ID of the controller instance, e.g. `kubelet-xyzf`. + // This field cannot be empty for new Events and it can have at most 128 characters. // +optional ReportingInstance string `json:"reportingInstance,omitempty" protobuf:"bytes,5,opt,name=reportingInstance"` - // What action was taken/failed regarding to the regarding object. + // action is what action was taken/failed regarding to the regarding object. It is machine-readable. + // This field can have at most 128 characters. // +optional Action string `json:"action,omitempty" protobuf:"bytes,6,name=action"` - // Why the action was taken. + // reason is why the action was taken. It is human-readable. + // This field can have at most 128 characters. + // +optional Reason string `json:"reason,omitempty" protobuf:"bytes,7,name=reason"` - // The object this Event is about. In most cases it's an Object reporting controller implements. - // E.g. ReplicaSetController implements ReplicaSets and this event is emitted because + // regarding contains the object this Event is about. In most cases it's an Object reporting controller + // implements, e.g. ReplicaSetController implements ReplicaSets and this event is emitted because // it acts on some changes in a ReplicaSet object. // +optional Regarding corev1.ObjectReference `json:"regarding,omitempty" protobuf:"bytes,8,opt,name=regarding"` - // Optional secondary object for more complex actions. E.g. when regarding object triggers + // related is the optional secondary object for more complex actions. E.g. when regarding object triggers // a creation or deletion of related object. // +optional Related *corev1.ObjectReference `json:"related,omitempty" protobuf:"bytes,9,opt,name=related"` - // Optional. A human-readable description of the status of this operation. + // note is a human-readable description of the status of this operation. // Maximal length of the note is 1kB, but libraries should be prepared to // handle values up to 64kB. // +optional Note string `json:"note,omitempty" protobuf:"bytes,10,opt,name=note"` - // Type of this event (Normal, Warning), new types could be added in the - // future. + // type is the type of this event (Normal, Warning), new types could be added in the future. + // It is machine-readable. // +optional Type string `json:"type,omitempty" protobuf:"bytes,11,opt,name=type"` - // Deprecated field assuring backward compatibility with core.v1 Event type + // deprecatedSource is the deprecated field assuring backward compatibility with core.v1 Event type. // +optional DeprecatedSource corev1.EventSource `json:"deprecatedSource,omitempty" protobuf:"bytes,12,opt,name=deprecatedSource"` - // Deprecated field assuring backward compatibility with core.v1 Event type + // deprecatedFirstTimestamp is the deprecated field assuring backward compatibility with core.v1 Event type. // +optional DeprecatedFirstTimestamp metav1.Time `json:"deprecatedFirstTimestamp,omitempty" protobuf:"bytes,13,opt,name=deprecatedFirstTimestamp"` - // Deprecated field assuring backward compatibility with core.v1 Event type + // deprecatedLastTimestamp is the deprecated field assuring backward compatibility with core.v1 Event type. // +optional DeprecatedLastTimestamp metav1.Time `json:"deprecatedLastTimestamp,omitempty" protobuf:"bytes,14,opt,name=deprecatedLastTimestamp"` - // Deprecated field assuring backward compatibility with core.v1 Event type + // deprecatedCount is the deprecated field assuring backward compatibility with core.v1 Event type. // +optional DeprecatedCount int32 `json:"deprecatedCount,omitempty" protobuf:"varint,15,opt,name=deprecatedCount"` } @@ -93,9 +98,9 @@ type Event struct { // EventSeries contain information on series of events, i.e. thing that was/is happening // continuously for some time. type EventSeries struct { - // Number of occurrences in this series up to the last heartbeat time + // count is the number of occurrences in this series up to the last heartbeat time. Count int32 `json:"count" protobuf:"varint,1,opt,name=count"` - // Time when last Event from the series was seen before last heartbeat. + // lastObservedTime is the time when last Event from the series was seen before last heartbeat. LastObservedTime metav1.MicroTime `json:"lastObservedTime" protobuf:"bytes,2,opt,name=lastObservedTime"` // +k8s:deprecated=state,protobuf=3 @@ -113,6 +118,6 @@ type EventList struct { // +optional metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - // Items is a list of schema objects. + // items is a list of schema objects. Items []Event `json:"items" protobuf:"bytes,2,rep,name=items"` } diff --git a/staging/src/k8s.io/api/roundtrip_test.go b/staging/src/k8s.io/api/roundtrip_test.go index e3c147b7fcca..beafa9d1a255 100644 --- a/staging/src/k8s.io/api/roundtrip_test.go +++ b/staging/src/k8s.io/api/roundtrip_test.go @@ -42,6 +42,7 @@ import ( coordinationv1 "k8s.io/api/coordination/v1" coordinationv1beta1 "k8s.io/api/coordination/v1beta1" corev1 "k8s.io/api/core/v1" + eventsv1 "k8s.io/api/events/v1" eventsv1beta1 "k8s.io/api/events/v1beta1" extensionsv1beta1 "k8s.io/api/extensions/v1beta1" imagepolicyv1alpha1 "k8s.io/api/imagepolicy/v1alpha1" @@ -92,6 +93,7 @@ var groups = []runtime.SchemeBuilder{ coordinationv1.SchemeBuilder, coordinationv1beta1.SchemeBuilder, corev1.SchemeBuilder, + eventsv1.SchemeBuilder, eventsv1beta1.SchemeBuilder, extensionsv1beta1.SchemeBuilder, imagepolicyv1alpha1.SchemeBuilder, diff --git a/test/integration/etcd/data.go b/test/integration/etcd/data.go index 339c219fe328..c096ef7de62a 100644 --- a/test/integration/etcd/data.go +++ b/test/integration/etcd/data.go @@ -206,6 +206,14 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes }, // -- + // k8s.io/kubernetes/pkg/apis/events/v1 + gvr("events.k8s.io", "v1", "events"): { + Stub: `{"metadata": {"name": "event3"}, "regarding": {"namespace": "` + namespace + `"}, "note": "some data here", "eventTime": "2017-08-09T15:04:05.000000Z", "reportingInstance": "node-xyz", "reportingController": "k8s.io/my-controller", "action": "DidNothing", "reason": "Laziness", "type": "Normal"}`, + ExpectedEtcdPath: "/registry/events/" + namespace + "/event3", + ExpectedGVK: gvkP("", "v1", "Event"), + }, + // -- + // k8s.io/kubernetes/pkg/apis/events/v1beta1 gvr("events.k8s.io", "v1beta1", "events"): { Stub: `{"metadata": {"name": "event2"}, "regarding": {"namespace": "` + namespace + `"}, "note": "some data here", "eventTime": "2017-08-09T15:04:05.000000Z", "reportingInstance": "node-xyz", "reportingController": "k8s.io/my-controller", "action": "DidNothing", "reason": "Laziness"}`, diff --git a/vendor/modules.txt b/vendor/modules.txt index cd6a7f710d5d..d237856f785e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1560,6 +1560,7 @@ k8s.io/api/coordination/v1beta1 k8s.io/api/core/v1 k8s.io/api/discovery/v1alpha1 k8s.io/api/discovery/v1beta1 +k8s.io/api/events/v1 k8s.io/api/events/v1beta1 k8s.io/api/extensions/v1beta1 k8s.io/api/flowcontrol/v1alpha1 @@ -1887,6 +1888,7 @@ k8s.io/client-go/informers/discovery k8s.io/client-go/informers/discovery/v1alpha1 k8s.io/client-go/informers/discovery/v1beta1 k8s.io/client-go/informers/events +k8s.io/client-go/informers/events/v1 k8s.io/client-go/informers/events/v1beta1 k8s.io/client-go/informers/extensions k8s.io/client-go/informers/extensions/v1beta1 @@ -1962,6 +1964,8 @@ k8s.io/client-go/kubernetes/typed/discovery/v1alpha1 k8s.io/client-go/kubernetes/typed/discovery/v1alpha1/fake k8s.io/client-go/kubernetes/typed/discovery/v1beta1 k8s.io/client-go/kubernetes/typed/discovery/v1beta1/fake +k8s.io/client-go/kubernetes/typed/events/v1 +k8s.io/client-go/kubernetes/typed/events/v1/fake k8s.io/client-go/kubernetes/typed/events/v1beta1 k8s.io/client-go/kubernetes/typed/events/v1beta1/fake k8s.io/client-go/kubernetes/typed/extensions/v1beta1 @@ -2016,6 +2020,7 @@ k8s.io/client-go/listers/coordination/v1beta1 k8s.io/client-go/listers/core/v1 k8s.io/client-go/listers/discovery/v1alpha1 k8s.io/client-go/listers/discovery/v1beta1 +k8s.io/client-go/listers/events/v1 k8s.io/client-go/listers/events/v1beta1 k8s.io/client-go/listers/extensions/v1beta1 k8s.io/client-go/listers/flowcontrol/v1alpha1