1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package v1alpha1
17
18 import (
19 "encoding/json"
20 "fmt"
21 "reflect"
22 "strconv"
23 "strings"
24
25 "github.com/alecthomas/units"
26 "github.com/google/uuid"
27 "github.com/pkg/errors"
28 "k8s.io/apimachinery/pkg/util/validation/field"
29 )
30
31 func (in *PhysicalMachineChaosSpec) Default(root interface{}, field *reflect.StructField) {
32 if in == nil {
33 return
34 }
35
36 if len(in.UID) == 0 {
37 in.UID = uuid.New().String()
38 }
39
40 for i := range in.Address {
41
42 if !strings.HasPrefix(in.Address[i], "http") {
43 in.Address[i] = fmt.Sprintf("http://%s", in.Address[i])
44 }
45 }
46 }
47
48 func (in *PhysicalMachineChaosSpec) Validate(root interface{}, path *field.Path) field.ErrorList {
49 allErrs := field.ErrorList{}
50
51
52 var inInterface map[string]interface{}
53 inrec, err := json.Marshal(in)
54 if err != nil {
55 allErrs = append(allErrs,
56 field.Invalid(path.Child("spec"), in, err.Error()))
57 }
58
59 err = json.Unmarshal(inrec, &inInterface)
60 if err != nil {
61 allErrs = append(allErrs,
62 field.Invalid(path.Child("spec"), in, err.Error()))
63 }
64
65 skipConfigCheck := false
66 if _, ok := inInterface[string(in.Action)]; !ok {
67 skipConfigCheck = true
68 allErrs = append(allErrs,
69 field.Invalid(path.Child("spec"), in,
70 "the configuration corresponding to action is empty"))
71 }
72
73
74 if len(in.Address) == 0 {
75 allErrs = append(allErrs,
76 field.Invalid(path.Child("address"), in.Address, "the address is empty"))
77 }
78 for _, address := range in.Address {
79 if len(address) == 0 {
80 allErrs = append(allErrs,
81 field.Invalid(path.Child("address"), in.Address, "the address is empty"))
82 }
83 }
84
85 if skipConfigCheck {
86 return allErrs
87 }
88
89 var validateConfigErr error
90 switch in.Action {
91 case PMStressCPUAction:
92 validateConfigErr = validateStressCPUAction(in.StressCPU)
93 case PMStressMemAction:
94 validateConfigErr = validateStressMemAction(in.StressMemory)
95 case PMDiskWritePayloadAction:
96 validateConfigErr = validateDiskPayloadAction(in.DiskWritePayload)
97 case PMDiskReadPayloadAction:
98 validateConfigErr = validateDiskPayloadAction(in.DiskReadPayload)
99 case PMDiskFillAction:
100 validateConfigErr = validateDiskFillAction(in.DiskFill)
101 case PMNetworkCorruptAction:
102 validateConfigErr = validateNetworkCorruptAction(in.NetworkCorrupt)
103 case PMNetworkDuplicateAction:
104 validateConfigErr = validateNetworkDuplicateAction(in.NetworkDuplicate)
105 case PMNetworkLossAction:
106 validateConfigErr = validateNetworkLossAction(in.NetworkLoss)
107 case PMNetworkDelayAction:
108 validateConfigErr = validateNetworkDelayAction(in.NetworkDelay)
109 case PMNetworkPartitionAction:
110 validateConfigErr = validateNetworkPartitionAction(in.NetworkPartition)
111 case PMNetworkBandwidthAction:
112 validateConfigErr = validateNetworkBandwidthAction(in.NetworkBandwidth)
113 case PMNetworkDNSAction:
114 validateConfigErr = validateNetworkDNSAction(in.NetworkDNS)
115 case PMProcessAction:
116 validateConfigErr = validateProcessAction(in.Process)
117 case PMJVMExceptionAction:
118 validateConfigErr = validateJVMExceptionAction(in.JVMException)
119 case PMJVMGCAction:
120 validateConfigErr = validateJVMGCAction(in.JVMGC)
121 case PMJVMLatencyAction:
122 validateConfigErr = validateJVMLatencyAction(in.JVMLatency)
123 case PMJVMReturnAction:
124 validateConfigErr = validateJVMReturnAction(in.JVMReturn)
125 case PMJVMStressAction:
126 validateConfigErr = validateJVMStressAction(in.JVMStress)
127 case PMJVMRuleDataAction:
128 validateConfigErr = validateJVMRuleDataAction(in.JVMRuleData)
129 case PMClockAction:
130 validateConfigErr = validateClockAction(in.Clock)
131 default:
132 }
133
134 if validateConfigErr != nil {
135 allErrs = append(allErrs,
136 field.Invalid(path.Child("spec"), in,
137 validateConfigErr.Error()))
138 }
139
140 return allErrs
141 }
142
143 func validateStressCPUAction(spec *StressCPUSpec) error {
144 if spec.Load == 0 {
145 return errors.New("load can't be 0")
146 }
147
148 if spec.Workers == 0 {
149 return errors.New("workers can't be 0")
150 }
151
152 return nil
153 }
154
155 func validateStressMemAction(spec *StressMemorySpec) error {
156 if len(spec.Size) == 0 {
157 return errors.New("size is required")
158 }
159
160 if _, err := ParseUnit(spec.Size); err != nil {
161 return err
162 }
163
164 return nil
165 }
166
167 func validateDiskPayloadAction(spec *DiskPayloadSpec) error {
168 if spec.PayloadProcessNum == 0 {
169 return errors.New("payload-process-num can't be 0")
170 }
171
172 if len(spec.Size) == 0 {
173 return errors.New("size is required")
174 }
175
176 if _, err := ParseUnit(spec.Size); err != nil {
177 return err
178 }
179
180 return nil
181 }
182
183 func validateDiskFillAction(spec *DiskFillSpec) error {
184 if len(spec.Size) == 0 {
185 return errors.New("size is required")
186 }
187
188 if _, err := ParseUnit(spec.Size); err != nil {
189 return err
190 }
191
192 return nil
193 }
194
195 func validateNetworkCommon(spec *NetworkCommonSpec) error {
196 if !CheckPercent(spec.Correlation, true) {
197 return errors.Errorf("correlation %s is invalid", spec.Correlation)
198 }
199
200 if len(spec.Device) == 0 {
201 return errors.New("device is required")
202 }
203
204 if len(spec.IPAddress) == 0 && len(spec.Hostname) == 0 {
205 return errors.New("one of ip-address and hostname is required")
206 }
207
208 return nil
209 }
210
211 func validateNetworkCorruptAction(spec *NetworkCorruptSpec) error {
212 if err := validateNetworkCommon(&spec.NetworkCommonSpec); err != nil {
213 return err
214 }
215
216 if !CheckPercent(spec.Percent, false) {
217 return errors.New("percent is invalid")
218 }
219
220 return nil
221 }
222
223 func validateNetworkDuplicateAction(spec *NetworkDuplicateSpec) error {
224 if err := validateNetworkCommon(&spec.NetworkCommonSpec); err != nil {
225 return err
226 }
227
228 if !CheckPercent(spec.Percent, false) {
229 return errors.New("percent is invalid")
230 }
231
232 return nil
233 }
234
235 func validateNetworkLossAction(spec *NetworkLossSpec) error {
236 if err := validateNetworkCommon(&spec.NetworkCommonSpec); err != nil {
237 return err
238 }
239
240 if !CheckPercent(spec.Percent, false) {
241 return errors.New("percent is invalid")
242 }
243
244 return nil
245 }
246
247 func validateNetworkDelayAction(spec *NetworkDelaySpec) error {
248 if err := validateNetworkCommon(&spec.NetworkCommonSpec); err != nil {
249 return err
250 }
251
252 if len(spec.Latency) == 0 {
253 return errors.New("latency is invalid")
254 }
255
256 return nil
257 }
258
259 func validateNetworkPartitionAction(spec *NetworkPartitionSpec) error {
260 if len(spec.Device) == 0 {
261 return errors.New("device is required")
262 }
263
264 if len(spec.IPAddress) == 0 && len(spec.Hostname) == 0 {
265 return errors.New("one of ip-address and hostname is required")
266 }
267
268 if spec.Direction != "to" && spec.Direction != "from" {
269 return errors.New("direction should be one of 'to' and 'from'")
270 }
271
272 if len(spec.AcceptTCPFlags) > 0 && spec.IPProtocol != "tcp" {
273 return errors.Errorf("protocol should be 'tcp' when set accept-tcp-flags")
274 }
275
276 return nil
277 }
278
279 func validateNetworkBandwidthAction(spec *NetworkBandwidthSpec) error {
280 if len(spec.Device) == 0 {
281 return errors.New("device is required")
282 }
283
284 if len(spec.Rate) == 0 || spec.Limit == 0 || spec.Buffer == 0 {
285 return errors.Errorf("rate, limit and buffer both are required when action is bandwidth")
286 }
287
288 return nil
289 }
290
291 func validateNetworkDNSAction(spec *NetworkDNSSpec) error {
292 if (len(spec.DNSDomainName) != 0 && len(spec.DNSIp) == 0) || (len(spec.DNSDomainName) == 0 && len(spec.DNSIp) != 0) {
293 return errors.Errorf("DNS host %s must match a DNS ip %s", spec.DNSDomainName, spec.DNSIp)
294 }
295
296 return nil
297 }
298
299 func validateProcessAction(spec *ProcessSpec) error {
300 if len(spec.Process) == 0 {
301 return errors.New("process is required")
302 }
303
304 if spec.Signal == 0 {
305 return errors.New("signal is required")
306 }
307
308 return nil
309 }
310
311 func validateJVMClassMethod(spec *JVMClassMethodSpec) error {
312 if len(spec.Class) == 0 {
313 return errors.New("class is required")
314 }
315
316 if len(spec.Method) == 0 {
317 return errors.New("method is required")
318 }
319
320 return nil
321 }
322
323 func validateJVMExceptionAction(spec *JVMExceptionSpec) error {
324 if err := CheckPid(spec.Pid); err != nil {
325 return err
326 }
327
328 if err := validateJVMClassMethod(&spec.JVMClassMethodSpec); err != nil {
329 return err
330 }
331
332 if len(spec.ThrowException) == 0 {
333 return errors.New("exception is required")
334 }
335
336 return nil
337 }
338
339 func validateJVMGCAction(spec *JVMGCSpec) error {
340 return CheckPid(spec.Pid)
341 }
342
343 func validateJVMLatencyAction(spec *JVMLatencySpec) error {
344 if err := CheckPid(spec.Pid); err != nil {
345 return err
346 }
347
348 if err := validateJVMClassMethod(&spec.JVMClassMethodSpec); err != nil {
349 return err
350 }
351
352 if spec.LatencyDuration == 0 {
353 return errors.New("latency is required")
354 }
355
356 return nil
357 }
358
359 func validateJVMReturnAction(spec *JVMReturnSpec) error {
360 if err := CheckPid(spec.Pid); err != nil {
361 return err
362 }
363
364 if err := validateJVMClassMethod(&spec.JVMClassMethodSpec); err != nil {
365 return err
366 }
367
368 if len(spec.ReturnValue) == 0 {
369 return errors.New("value is required")
370 }
371
372 return nil
373 }
374
375 func validateJVMStressAction(spec *JVMStressSpec) error {
376 if err := CheckPid(spec.Pid); err != nil {
377 return err
378 }
379
380 if spec.CPUCount == 0 && len(spec.MemoryType) == 0 {
381 return errors.New("one of cpu-count and mem-type is required")
382 }
383
384 if spec.CPUCount > 0 && len(spec.MemoryType) > 0 {
385 return errors.New("inject stress on both CPU and memory is not support")
386 }
387
388 return nil
389 }
390
391 func validateJVMRuleDataAction(spec *JVMRuleDataSpec) error {
392 if err := CheckPid(spec.Pid); err != nil {
393 return err
394 }
395
396 if len(spec.RuleData) == 0 {
397 return errors.New("rule-data is required")
398 }
399
400 return nil
401 }
402
403 func validateClockAction(spec *ClockSpec) error {
404 if err := CheckPid(spec.Pid); err != nil {
405 return err
406 }
407
408 if len(spec.TimeOffset) == 0 {
409 return errors.New("time-offset is required")
410 }
411
412 return nil
413 }
414
415 func CheckPid(pid int) error {
416 if pid == 0 {
417 return errors.New("pid is required")
418 }
419
420 if pid < 0 {
421 return errors.New("pid is invalid")
422 }
423
424 return nil
425 }
426
427 func CheckPercent(p string, allowZero bool) bool {
428 if len(p) == 0 {
429 return allowZero
430 }
431
432 v, err := strconv.ParseFloat(p, 32)
433 if err != nil {
434 return false
435 }
436
437 if v == 0 && !allowZero {
438 return false
439 }
440
441 if v < 0 || v > 100 {
442 return false
443 }
444
445 return true
446 }
447
448 var (
449
450 shortBinaryUnitMap = units.MakeUnitMap("", "c", 1024)
451 binaryUnitMap = units.MakeUnitMap("iB", "c", 1024)
452 decimalUnitMap = units.MakeUnitMap("B", "c", 1000)
453 )
454
455
456
457
458 func ParseUnit(s string) (uint64, error) {
459 if _, err := strconv.Atoi(s); err == nil {
460 s += "B"
461 }
462 if n, err := units.ParseUnit(s, shortBinaryUnitMap); err == nil {
463 return uint64(n), nil
464 }
465
466 if n, err := units.ParseUnit(s, binaryUnitMap); err == nil {
467 return uint64(n), nil
468 }
469
470 if n, err := units.ParseUnit(s, decimalUnitMap); err == nil {
471 return uint64(n), nil
472 }
473 return 0, fmt.Errorf("units: unknown unit %s", s)
474 }
475
476 func (in *NetworkBandwidthSpec) Validate(root interface{}, path *field.Path) field.ErrorList {
477 allErrs := field.ErrorList{}
478
479 if len(in.Rate) == 0 {
480 allErrs = append(allErrs,
481 field.Invalid(path.Child("rate"), in.Rate, "rate is required"))
482 }
483
484 return allErrs
485 }
486