1
16
17 package validation
18
19 import (
20 "fmt"
21 "reflect"
22 "time"
23
24 v1 "k8s.io/api/core/v1"
25 eventsv1beta1 "k8s.io/api/events/v1beta1"
26 apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 "k8s.io/apimachinery/pkg/runtime/schema"
29 "k8s.io/apimachinery/pkg/util/validation"
30 "k8s.io/apimachinery/pkg/util/validation/field"
31 "k8s.io/kubernetes/pkg/apis/core"
32 )
33
34 const (
35 ReportingInstanceLengthLimit = 128
36 ActionLengthLimit = 128
37 ReasonLengthLimit = 128
38 NoteLengthLimit = 1024
39 )
40
41 func ValidateEventCreate(event *core.Event, requestVersion schema.GroupVersion) field.ErrorList {
42
43 allErrs := legacyValidateEvent(event, requestVersion)
44 if requestVersion == v1.SchemeGroupVersion || requestVersion == eventsv1beta1.SchemeGroupVersion {
45
46 return allErrs
47 }
48
49
50 allErrs = append(allErrs, ValidateObjectMeta(&event.ObjectMeta, true, apimachineryvalidation.NameIsDNSSubdomain, field.NewPath("metadata"))...)
51 allErrs = append(allErrs, validateV1EventSeries(event)...)
52 zeroTime := time.Time{}
53 if event.EventTime.Time == zeroTime {
54 allErrs = append(allErrs, field.Required(field.NewPath("eventTime"), ""))
55 }
56 if event.Type != v1.EventTypeNormal && event.Type != v1.EventTypeWarning {
57 allErrs = append(allErrs, field.Invalid(field.NewPath("type"), "", fmt.Sprintf("has invalid value: %v", event.Type)))
58 }
59 if event.FirstTimestamp.Time != zeroTime {
60 allErrs = append(allErrs, field.Invalid(field.NewPath("firstTimestamp"), "", "needs to be unset"))
61 }
62 if event.LastTimestamp.Time != zeroTime {
63 allErrs = append(allErrs, field.Invalid(field.NewPath("lastTimestamp"), "", "needs to be unset"))
64 }
65 if event.Count != 0 {
66 allErrs = append(allErrs, field.Invalid(field.NewPath("count"), "", "needs to be unset"))
67 }
68 if event.Source.Component != "" || event.Source.Host != "" {
69 allErrs = append(allErrs, field.Invalid(field.NewPath("source"), "", "needs to be unset"))
70 }
71 return allErrs
72 }
73
74 func ValidateEventUpdate(newEvent, oldEvent *core.Event, requestVersion schema.GroupVersion) field.ErrorList {
75
76 allErrs := legacyValidateEvent(newEvent, requestVersion)
77 if requestVersion == v1.SchemeGroupVersion || requestVersion == eventsv1beta1.SchemeGroupVersion {
78
79 return allErrs
80 }
81
82
83 allErrs = append(allErrs, ValidateObjectMetaUpdate(&newEvent.ObjectMeta, &oldEvent.ObjectMeta, field.NewPath("metadata"))...)
84
85 if !reflect.DeepEqual(newEvent.Series, oldEvent.Series) {
86 allErrs = append(allErrs, validateV1EventSeries(newEvent)...)
87 }
88
89 allErrs = append(allErrs, ValidateImmutableField(newEvent.InvolvedObject, oldEvent.InvolvedObject, field.NewPath("involvedObject"))...)
90 allErrs = append(allErrs, ValidateImmutableField(newEvent.Reason, oldEvent.Reason, field.NewPath("reason"))...)
91 allErrs = append(allErrs, ValidateImmutableField(newEvent.Message, oldEvent.Message, field.NewPath("message"))...)
92 allErrs = append(allErrs, ValidateImmutableField(newEvent.Source, oldEvent.Source, field.NewPath("source"))...)
93 allErrs = append(allErrs, ValidateImmutableField(newEvent.FirstTimestamp, oldEvent.FirstTimestamp, field.NewPath("firstTimestamp"))...)
94 allErrs = append(allErrs, ValidateImmutableField(newEvent.LastTimestamp, oldEvent.LastTimestamp, field.NewPath("lastTimestamp"))...)
95 allErrs = append(allErrs, ValidateImmutableField(newEvent.Count, oldEvent.Count, field.NewPath("count"))...)
96 allErrs = append(allErrs, ValidateImmutableField(newEvent.Reason, oldEvent.Reason, field.NewPath("reason"))...)
97 allErrs = append(allErrs, ValidateImmutableField(newEvent.Type, oldEvent.Type, field.NewPath("type"))...)
98
99
100
101
102
103
104 newTruncated := newEvent.EventTime.Truncate(time.Microsecond).UTC()
105 oldTruncated := oldEvent.EventTime.Truncate(time.Microsecond).UTC()
106 if newTruncated != oldTruncated {
107 allErrs = append(allErrs, ValidateImmutableField(newEvent.EventTime, oldEvent.EventTime, field.NewPath("eventTime"))...)
108 }
109
110 allErrs = append(allErrs, ValidateImmutableField(newEvent.Action, oldEvent.Action, field.NewPath("action"))...)
111 allErrs = append(allErrs, ValidateImmutableField(newEvent.Related, oldEvent.Related, field.NewPath("related"))...)
112 allErrs = append(allErrs, ValidateImmutableField(newEvent.ReportingController, oldEvent.ReportingController, field.NewPath("reportingController"))...)
113 allErrs = append(allErrs, ValidateImmutableField(newEvent.ReportingInstance, oldEvent.ReportingInstance, field.NewPath("reportingInstance"))...)
114
115 return allErrs
116 }
117
118 func validateV1EventSeries(event *core.Event) field.ErrorList {
119 allErrs := field.ErrorList{}
120 zeroTime := time.Time{}
121 if event.Series != nil {
122 if event.Series.Count < 2 {
123 allErrs = append(allErrs, field.Invalid(field.NewPath("series.count"), "", "should be at least 2"))
124 }
125 if event.Series.LastObservedTime.Time == zeroTime {
126 allErrs = append(allErrs, field.Required(field.NewPath("series.lastObservedTime"), ""))
127 }
128 }
129 return allErrs
130 }
131
132
133 func legacyValidateEvent(event *core.Event, requestVersion schema.GroupVersion) field.ErrorList {
134 allErrs := field.ErrorList{}
135
136 zeroTime := time.Time{}
137
138 reportingControllerFieldName := "reportingController"
139 if requestVersion == v1.SchemeGroupVersion {
140 reportingControllerFieldName = "reportingComponent"
141 }
142
143
144 if event.EventTime.Time == zeroTime {
145
146 if len(event.InvolvedObject.Namespace) == 0 {
147
148 if event.Namespace != metav1.NamespaceNone && event.Namespace != metav1.NamespaceDefault {
149 allErrs = append(allErrs, field.Invalid(field.NewPath("involvedObject", "namespace"), event.InvolvedObject.Namespace, "does not match event.namespace"))
150 }
151 } else {
152
153 if event.Namespace != event.InvolvedObject.Namespace {
154 allErrs = append(allErrs, field.Invalid(field.NewPath("involvedObject", "namespace"), event.InvolvedObject.Namespace, "does not match event.namespace"))
155 }
156 }
157
158 } else {
159 if len(event.InvolvedObject.Namespace) == 0 && event.Namespace != metav1.NamespaceDefault && event.Namespace != metav1.NamespaceSystem {
160 allErrs = append(allErrs, field.Invalid(field.NewPath("involvedObject", "namespace"), event.InvolvedObject.Namespace, "does not match event.namespace"))
161 }
162 if len(event.ReportingController) == 0 {
163 allErrs = append(allErrs, field.Required(field.NewPath(reportingControllerFieldName), ""))
164 }
165 allErrs = append(allErrs, ValidateQualifiedName(event.ReportingController, field.NewPath(reportingControllerFieldName))...)
166 if len(event.ReportingInstance) == 0 {
167 allErrs = append(allErrs, field.Required(field.NewPath("reportingInstance"), ""))
168 }
169 if len(event.ReportingInstance) > ReportingInstanceLengthLimit {
170 allErrs = append(allErrs, field.Invalid(field.NewPath("reportingInstance"), "", fmt.Sprintf("can have at most %v characters", ReportingInstanceLengthLimit)))
171 }
172 if len(event.Action) == 0 {
173 allErrs = append(allErrs, field.Required(field.NewPath("action"), ""))
174 }
175 if len(event.Action) > ActionLengthLimit {
176 allErrs = append(allErrs, field.Invalid(field.NewPath("action"), "", fmt.Sprintf("can have at most %v characters", ActionLengthLimit)))
177 }
178 if len(event.Reason) == 0 {
179 allErrs = append(allErrs, field.Required(field.NewPath("reason"), ""))
180 }
181 if len(event.Reason) > ReasonLengthLimit {
182 allErrs = append(allErrs, field.Invalid(field.NewPath("reason"), "", fmt.Sprintf("can have at most %v characters", ReasonLengthLimit)))
183 }
184 if len(event.Message) > NoteLengthLimit {
185 allErrs = append(allErrs, field.Invalid(field.NewPath("message"), "", fmt.Sprintf("can have at most %v characters", NoteLengthLimit)))
186 }
187 }
188
189 for _, msg := range validation.IsDNS1123Subdomain(event.Namespace) {
190 allErrs = append(allErrs, field.Invalid(field.NewPath("namespace"), event.Namespace, msg))
191 }
192 return allErrs
193 }
194
View as plain text