1
2
3
4
5
6
7
8
9
10
11
12
13
14 package v1alpha1
15
16 import (
17 "errors"
18 "fmt"
19 "strconv"
20 "strings"
21 "time"
22
23 "k8s.io/apimachinery/pkg/runtime"
24 "k8s.io/apimachinery/pkg/util/validation/field"
25 logf "sigs.k8s.io/controller-runtime/pkg/log"
26 "sigs.k8s.io/controller-runtime/pkg/webhook"
27 )
28
29 const (
30
31 DefaultJitter = "0ms"
32
33
34 DefaultCorrelation = "0"
35 )
36
37
38 var networkchaoslog = logf.Log.WithName("networkchaos-resource")
39
40
41
42 var _ webhook.Defaulter = &NetworkChaos{}
43
44
45 func (in *NetworkChaos) Default() {
46 networkchaoslog.Info("default", "name", in.Name)
47
48 in.Spec.Selector.DefaultNamespace(in.GetNamespace())
49
50 if in.Spec.Target != nil {
51 in.Spec.Target.TargetSelector.DefaultNamespace(in.GetNamespace())
52 }
53
54
55 if in.Spec.Direction == "" {
56 in.Spec.Direction = To
57 }
58
59 in.Spec.DefaultDelay()
60 }
61
62
63 func (in *NetworkChaosSpec) DefaultDelay() {
64 if in.Delay != nil {
65 if in.Delay.Jitter == "" {
66 in.Delay.Jitter = DefaultJitter
67 }
68 if in.Delay.Correlation == "" {
69 in.Delay.Correlation = DefaultCorrelation
70 }
71 }
72 }
73
74
75
76 var _ ChaosValidator = &NetworkChaos{}
77
78
79 func (in *NetworkChaos) ValidateCreate() error {
80 networkchaoslog.Info("validate create", "name", in.Name)
81 return in.Validate()
82 }
83
84
85 func (in *NetworkChaos) ValidateUpdate(old runtime.Object) error {
86 networkchaoslog.Info("validate update", "name", in.Name)
87 return in.Validate()
88 }
89
90
91 func (in *NetworkChaos) ValidateDelete() error {
92 networkchaoslog.Info("validate delete", "name", in.Name)
93
94
95 return nil
96 }
97
98
99 func (in *NetworkChaos) Validate() error {
100 specField := field.NewPath("spec")
101 allErrs := in.ValidateScheduler(specField)
102 allErrs = append(allErrs, in.ValidatePodMode(specField)...)
103 allErrs = append(allErrs, in.ValidateTargets(specField)...)
104
105 if in.Spec.Delay != nil {
106 allErrs = append(allErrs, in.Spec.Delay.validateDelay(specField.Child("delay"))...)
107 }
108 if in.Spec.Loss != nil {
109 allErrs = append(allErrs, in.Spec.Loss.validateLoss(specField.Child("loss"))...)
110 }
111 if in.Spec.Duplicate != nil {
112 allErrs = append(allErrs, in.Spec.Duplicate.validateDuplicate(specField.Child("duplicate"))...)
113 }
114 if in.Spec.Corrupt != nil {
115 allErrs = append(allErrs, in.Spec.Corrupt.validateCorrupt(specField.Child("corrupt"))...)
116 }
117 if in.Spec.Bandwidth != nil {
118 allErrs = append(allErrs, in.Spec.Bandwidth.validateBandwidth(specField.Child("bandwidth"))...)
119 }
120
121 if in.Spec.Target != nil {
122 allErrs = append(allErrs, in.Spec.Target.validateTarget(specField.Child("target"))...)
123 }
124
125 if len(allErrs) > 0 {
126 return fmt.Errorf(allErrs.ToAggregate().Error())
127 }
128 return nil
129 }
130
131
132 func (in *NetworkChaos) ValidateScheduler(spec *field.Path) field.ErrorList {
133 return ValidateScheduler(in, spec)
134 }
135
136
137 func (in *NetworkChaos) ValidatePodMode(spec *field.Path) field.ErrorList {
138 return ValidatePodMode(in.Spec.Value, in.Spec.Mode, spec.Child("value"))
139 }
140
141
142 func (in *NetworkChaos) GetSelectSpec() []SelectSpec {
143 selectSpecs := []SelectSpec{&in.Spec}
144
145 if in.Spec.Direction != To {
146 selectSpecs = append(selectSpecs, in.Spec.Target)
147 }
148 return selectSpecs
149 }
150
151
152 func (in *NetworkChaos) ValidateTargets(target *field.Path) field.ErrorList {
153 allErrs := field.ErrorList{}
154
155 if (in.Spec.Direction == From || in.Spec.Direction == Both) &&
156 in.Spec.ExternalTargets != nil && in.Spec.Action != PartitionAction {
157 allErrs = append(allErrs,
158 field.Invalid(target.Child("direction"), in.Spec.Direction,
159 fmt.Sprintf("external targets cannot be used with `from` and `both` direction in netem action yet")))
160 }
161
162 if (in.Spec.Direction == From || in.Spec.Direction == Both) && in.Spec.Target == nil {
163 if in.Spec.Action != PartitionAction {
164 allErrs = append(allErrs,
165 field.Invalid(target.Child("direction"), in.Spec.Direction,
166 fmt.Sprintf("`from` and `both` direction cannot be used when targets is empty in netem action")))
167 } else if in.Spec.ExternalTargets == nil {
168 allErrs = append(allErrs,
169 field.Invalid(target.Child("direction"), in.Spec.Direction,
170 fmt.Sprintf("`from` and `both` direction cannot be used when targets and external targets are both empty")))
171 }
172 }
173
174
175
176 return allErrs
177 }
178
179
180 func (in *DelaySpec) validateDelay(delay *field.Path) field.ErrorList {
181 allErrs := field.ErrorList{}
182 _, err := time.ParseDuration(in.Latency)
183 if err != nil {
184 allErrs = append(allErrs,
185 field.Invalid(delay.Child("latency"), in.Latency,
186 fmt.Sprintf("parse latency field error:%s", err)))
187 }
188 _, err = time.ParseDuration(in.Jitter)
189 if err != nil {
190 allErrs = append(allErrs,
191 field.Invalid(delay.Child("jitter"), in.Jitter,
192 fmt.Sprintf("parse jitter field error:%s", err)))
193 }
194
195 _, err = strconv.ParseFloat(in.Correlation, 32)
196 if err != nil {
197 allErrs = append(allErrs,
198 field.Invalid(delay.Child("correlation"), in.Correlation,
199 fmt.Sprintf("parse correlation field error:%s", err)))
200 }
201
202 if in.Reorder != nil {
203 allErrs = append(allErrs, in.Reorder.validateReorder(delay.Child("reorder"))...)
204 }
205 return allErrs
206 }
207
208
209 func (in *ReorderSpec) validateReorder(reorder *field.Path) field.ErrorList {
210 allErrs := field.ErrorList{}
211 _, err := strconv.ParseFloat(in.Reorder, 32)
212 if err != nil {
213 allErrs = append(allErrs,
214 field.Invalid(reorder.Child("reorder"), in.Reorder,
215 fmt.Sprintf("parse reorder field error:%s", err)))
216 }
217
218 _, err = strconv.ParseFloat(in.Correlation, 32)
219 if err != nil {
220 allErrs = append(allErrs,
221 field.Invalid(reorder.Child("correlation"), in.Correlation,
222 fmt.Sprintf("parse correlation field error:%s", err)))
223 }
224 return allErrs
225 }
226
227
228 func (in *LossSpec) validateLoss(loss *field.Path) field.ErrorList {
229 allErrs := field.ErrorList{}
230
231 _, err := strconv.ParseFloat(in.Loss, 32)
232 if err != nil {
233 allErrs = append(allErrs,
234 field.Invalid(loss.Child("loss"), in.Loss,
235 fmt.Sprintf("parse loss field error:%s", err)))
236 }
237
238 _, err = strconv.ParseFloat(in.Correlation, 32)
239 if err != nil {
240 allErrs = append(allErrs,
241 field.Invalid(loss.Child("correlation"), in.Correlation,
242 fmt.Sprintf("parse correlation field error:%s", err)))
243 }
244
245 return allErrs
246 }
247
248
249 func (in *DuplicateSpec) validateDuplicate(duplicate *field.Path) field.ErrorList {
250 allErrs := field.ErrorList{}
251 _, err := strconv.ParseFloat(in.Duplicate, 32)
252 if err != nil {
253 allErrs = append(allErrs,
254 field.Invalid(duplicate.Child("duplicate"), in.Duplicate,
255 fmt.Sprintf("parse duplicate field error:%s", err)))
256 }
257
258 _, err = strconv.ParseFloat(in.Correlation, 32)
259 if err != nil {
260 allErrs = append(allErrs,
261 field.Invalid(duplicate.Child("correlation"), in.Correlation,
262 fmt.Sprintf("parse correlation field error:%s", err)))
263 }
264 return allErrs
265 }
266
267
268 func (in *CorruptSpec) validateCorrupt(corrupt *field.Path) field.ErrorList {
269 allErrs := field.ErrorList{}
270 _, err := strconv.ParseFloat(in.Corrupt, 32)
271 if err != nil {
272 allErrs = append(allErrs,
273 field.Invalid(corrupt.Child("corrupt"), in.Corrupt,
274 fmt.Sprintf("parse corrupt field error:%s", err)))
275 }
276
277 _, err = strconv.ParseFloat(in.Correlation, 32)
278 if err != nil {
279 allErrs = append(allErrs,
280 field.Invalid(corrupt.Child("correlation"), in.Correlation,
281 fmt.Sprintf("parse correlation field error:%s", err)))
282 }
283 return allErrs
284 }
285
286
287 func (in *BandwidthSpec) validateBandwidth(bandwidth *field.Path) field.ErrorList {
288 allErrs := field.ErrorList{}
289 _, err := ConvertUnitToBytes(in.Rate)
290
291 if err != nil {
292 allErrs = append(allErrs,
293 field.Invalid(bandwidth.Child("rate"), in.Rate,
294 fmt.Sprintf("parse rate field error:%s", err)))
295 }
296 return allErrs
297 }
298
299
300 func (in *Target) validateTarget(target *field.Path) field.ErrorList {
301 modes := []PodMode{OnePodMode, AllPodMode, FixedPodMode, FixedPercentPodMode, RandomMaxPercentPodMode}
302
303 for _, mode := range modes {
304 if in.TargetMode == mode {
305 return ValidatePodMode(in.TargetValue, in.TargetMode, target.Child("value"))
306 }
307 }
308
309 return field.ErrorList{field.Invalid(target.Child("mode"), in.TargetMode,
310 fmt.Sprintf("mode %s not supported", in.TargetMode))}
311 }
312
313 func ConvertUnitToBytes(nu string) (uint64, error) {
314
315 s := strings.ToLower(strings.TrimSpace(nu))
316
317 for i, u := range []string{"tbps", "gbps", "mbps", "kbps", "bps"} {
318 if strings.HasSuffix(s, u) {
319 ts := strings.TrimSuffix(s, u)
320 s := strings.TrimSpace(ts)
321
322 n, err := strconv.ParseUint(s, 10, 64)
323
324 if err != nil {
325 return 0, err
326 }
327
328
329 for j := 4 - i; j > 0; j-- {
330 n = n * 1024
331 }
332
333 return n, nil
334 }
335 }
336
337 return 0, errors.New("invalid unit")
338 }
339