1
2
3
4
5
6
7
8
9
10
11
12
13
14 package v1alpha1
15
16 import (
17 "fmt"
18 "strconv"
19
20 "k8s.io/apimachinery/pkg/runtime"
21 "k8s.io/apimachinery/pkg/util/validation/field"
22 logf "sigs.k8s.io/controller-runtime/pkg/log"
23 "sigs.k8s.io/controller-runtime/pkg/webhook"
24 )
25
26
27 var jvmchaoslog = logf.Log.WithName("jvmchaos-resource")
28
29
30
31 var _ webhook.Defaulter = &JVMChaos{}
32
33
34 func (in *JVMChaos) Default() {
35 jvmchaoslog.Info("default", "name", in.Name)
36
37 in.Spec.Selector.DefaultNamespace(in.GetNamespace())
38 }
39
40
41
42 var _ ChaosValidator = &JVMChaos{}
43
44
45 func (in *JVMChaos) ValidateCreate() error {
46 jvmchaoslog.Info("validate create", "name", in.Name)
47
48 return in.Validate()
49 }
50
51
52 func (in *JVMChaos) ValidateUpdate(old runtime.Object) error {
53 jvmchaoslog.Info("validate update", "name", in.Name)
54
55 return in.Validate()
56 }
57
58
59 func (in *JVMChaos) ValidateDelete() error {
60 jvmchaoslog.Info("validate delete", "name", in.Name)
61
62
63 return nil
64 }
65
66
67 func (in *JVMChaos) Validate() error {
68 specField := field.NewPath("spec")
69 allErrs := in.ValidateScheduler(specField)
70 allErrs = append(allErrs, in.ValidatePodMode(specField)...)
71 allErrs = append(allErrs, in.validateJvmChaos(specField)...)
72 if len(allErrs) > 0 {
73 return fmt.Errorf(allErrs.ToAggregate().Error())
74 }
75
76 return nil
77 }
78
79
80 func (in *JVMChaos) ValidateScheduler(spec *field.Path) field.ErrorList {
81 return ValidateScheduler(in, spec)
82 }
83
84
85 func (in *JVMChaos) ValidatePodMode(spec *field.Path) field.ErrorList {
86 return ValidatePodMode(in.Spec.Value, in.Spec.Mode, spec.Child("value"))
87 }
88
89
90 func (in *JVMChaos) GetSelectSpec() []SelectSpec {
91 return []SelectSpec{&in.Spec}
92 }
93
94 func (in *JVMChaos) validateJvmChaos(spec *field.Path) field.ErrorList {
95 allErrs := field.ErrorList{}
96 targetField := spec.Child("target")
97 actionField := spec.Child("action")
98 flagsField := spec.Child("flags")
99 matcherField := spec.Child("matcher")
100 if actions, ok := JvmSpec[in.Spec.Target]; ok {
101
102 if actionPR, actionOK := actions[in.Spec.Action]; actionOK {
103 if actionPR.Flags != nil {
104 allErrs = append(allErrs, in.validateParameterRules(in.Spec.Flags, actionPR.Flags, flagsField, targetField, actionField)...)
105 }
106
107 if actionPR.Matchers != nil {
108 allErrs = append(allErrs, in.validateParameterRules(in.Spec.Matchers, actionPR.Matchers, matcherField, targetField, actionField)...)
109 }
110
111 } else {
112 supportActions := make([]JVMChaosAction, 0)
113 for k := range actions {
114 supportActions = append(supportActions, k)
115 }
116
117 notSupportedError := field.NotSupported(actionField, in.Spec.Action, toString(supportActions))
118 errorMsg := fmt.Sprintf("target: %s does not match action: %s, action detail error: %s",
119 in.Spec.Target, in.Spec.Action, notSupportedError)
120 allErrs = append(allErrs, field.Invalid(targetField, in.Spec.Target, errorMsg))
121 }
122 } else {
123 allErrs = append(allErrs, field.Invalid(targetField, in.Spec.Target, "unknown JVM chaos target"))
124 }
125
126 return allErrs
127 }
128
129 func toString(actions []JVMChaosAction) []string {
130 ret := make([]string, 0)
131 for _, act := range actions {
132 ret = append(ret, string(act))
133 }
134 return ret
135 }
136
137 func (in *JVMChaos) validateParameterRules(parameters map[string]string, rules []ParameterRules, parent *field.Path, target *field.Path, action *field.Path) field.ErrorList {
138 allErrs := field.ErrorList{}
139 for _, rule := range rules {
140 innerField := parent.Child(rule.Name)
141
142 var value = ""
143 var exist = false
144 if parameters != nil {
145 value, exist = parameters[rule.Name]
146 }
147 if rule.Required && !exist {
148 errorMsg := fmt.Sprintf("with %s: %s, %s: %s", target, in.Spec.Target, action, in.Spec.Action)
149 allErrs = append(allErrs, field.Required(innerField, errorMsg))
150 }
151
152 if exist && rule.Required && rule.ParameterType == StringType {
153 if len(value) == 0 {
154 errorMsg := fmt.Sprintf("%s:%s cannot be empty", innerField, value)
155 allErrs = append(allErrs, field.Invalid(innerField, value, errorMsg))
156 }
157 }
158
159 if exist && rule.ParameterType == IntType {
160 _, err := strconv.Atoi(value)
161 if err != nil {
162 errorMsg := fmt.Sprintf("%s:%s cannot parse as Int", innerField, value)
163 allErrs = append(allErrs, field.Invalid(innerField, value, errorMsg))
164 }
165 }
166
167 if exist && rule.ParameterType == BoolType {
168 _, err := strconv.ParseBool(value)
169 if err != nil {
170 errorMsg := fmt.Sprintf("%s:%s cannot parse as boolean", innerField, value)
171 allErrs = append(allErrs, field.Invalid(innerField, value, errorMsg))
172 }
173 }
174 }
175 return allErrs
176 }
177
178
179
180
181
182
183 var JvmSpec = map[JVMChaosTarget]map[JVMChaosAction]ActionParameterRules{
184 SERVLET: {
185 JVMDelayAction: ActionParameterRules{
186 Flags: []ParameterRules{
187 {Name: "time", ParameterType: IntType, Required: true},
188 {Name: "offset", ParameterType: IntType},
189 },
190 Matchers: []ParameterRules{
191 {Name: "effect-count", ParameterType: IntType},
192 {Name: "effect-percent", ParameterType: IntType},
193 {Name: "method"},
194 {Name: "querystring"},
195 {Name: "requestpath"},
196 },
197 },
198 JVMExceptionAction: ActionParameterRules{
199 Flags: []ParameterRules{
200 {Name: "exception", Required: true},
201 {Name: "exception-message"},
202 },
203 Matchers: []ParameterRules{
204 {Name: "effect-count", ParameterType: IntType},
205 {Name: "effect-percent", ParameterType: IntType},
206 {Name: "method"},
207 {Name: "querystring"},
208 {Name: "requestpath"},
209 },
210 },
211 },
212 PSQL: {
213 JVMDelayAction: ActionParameterRules{
214 Flags: []ParameterRules{
215 {Name: "time", ParameterType: IntType, Required: true},
216 {Name: "offset", ParameterType: IntType},
217 },
218 Matchers: []ParameterRules{
219 {Name: "effect-count", ParameterType: IntType},
220 {Name: "effect-percent", ParameterType: IntType},
221 {Name: "sqltype"},
222 {Name: "database"},
223 {Name: "port", ParameterType: IntType},
224 {Name: "host"},
225 {Name: "table"},
226 },
227 },
228 JVMExceptionAction: ActionParameterRules{
229 Flags: []ParameterRules{
230 {Name: "exception", Required: true},
231 {Name: "exception-message"},
232 },
233 Matchers: []ParameterRules{
234 {Name: "effect-count", ParameterType: IntType},
235 {Name: "effect-percent", ParameterType: IntType},
236 {Name: "sqltype"},
237 {Name: "database"},
238 {Name: "port", ParameterType: IntType},
239 {Name: "host"},
240 {Name: "table"},
241 },
242 },
243 },
244 MYSQL: {
245 JVMDelayAction: ActionParameterRules{
246 Flags: []ParameterRules{
247 {Name: "time", ParameterType: IntType, Required: true},
248 {Name: "offset", ParameterType: IntType},
249 },
250 Matchers: []ParameterRules{
251 {Name: "effect-count", ParameterType: IntType},
252 {Name: "effect-percent", ParameterType: IntType},
253 {Name: "sqltype"},
254 {Name: "database"},
255 {Name: "port", ParameterType: IntType},
256 {Name: "host"},
257 {Name: "table"},
258 },
259 },
260 JVMExceptionAction: ActionParameterRules{
261 Flags: []ParameterRules{
262 {Name: "exception", Required: true},
263 {Name: "exception-message"},
264 },
265 Matchers: []ParameterRules{
266 {Name: "effect-count", ParameterType: IntType},
267 {Name: "effect-percent", ParameterType: IntType},
268 {Name: "sqltype"},
269 {Name: "database"},
270 {Name: "port", ParameterType: IntType},
271 {Name: "host"},
272 {Name: "table"},
273 },
274 },
275 },
276 JEDIS: {
277 JVMDelayAction: ActionParameterRules{
278 Flags: []ParameterRules{
279 {Name: "time", ParameterType: IntType, Required: true},
280 {Name: "offset", ParameterType: IntType},
281 },
282 Matchers: []ParameterRules{
283 {Name: "effect-count", ParameterType: IntType},
284 {Name: "effect-percent", ParameterType: IntType},
285 {Name: "cmd"},
286 {Name: "key"},
287 },
288 },
289 JVMExceptionAction: ActionParameterRules{
290 Flags: []ParameterRules{
291 {Name: "exception", Required: true},
292 {Name: "exception-message"},
293 },
294 Matchers: []ParameterRules{
295 {Name: "effect-count", ParameterType: IntType},
296 {Name: "effect-percent", ParameterType: IntType},
297 {Name: "cmd"},
298 {Name: "key"},
299 },
300 },
301 },
302 HTTP: {
303 JVMDelayAction: ActionParameterRules{
304 Flags: []ParameterRules{
305 {Name: "time", ParameterType: IntType, Required: true},
306 {Name: "offset", ParameterType: IntType},
307 },
308 Matchers: []ParameterRules{
309 {Name: "effect-count", ParameterType: IntType},
310 {Name: "effect-percent", ParameterType: IntType},
311 {Name: "httpclient4", ParameterType: BoolType},
312 {Name: "rest", ParameterType: BoolType},
313 {Name: "httpclient3", ParameterType: BoolType},
314 {Name: "okhttp3", ParameterType: BoolType},
315 {Name: "uri", Required: true},
316 },
317 },
318 JVMExceptionAction: ActionParameterRules{
319 Flags: []ParameterRules{
320 {Name: "exception", Required: true},
321 {Name: "exception-message"},
322 },
323 Matchers: []ParameterRules{
324 {Name: "effect-count", ParameterType: IntType},
325 {Name: "effect-percent", ParameterType: IntType},
326 {Name: "httpclient4", ParameterType: BoolType},
327 {Name: "rest", ParameterType: BoolType},
328 {Name: "httpclient3", ParameterType: BoolType},
329 {Name: "okhttp3", ParameterType: BoolType},
330 {Name: "uri", Required: true},
331 },
332 },
333 },
334 RABBITMQ: {
335 JVMDelayAction: ActionParameterRules{
336 Flags: []ParameterRules{
337 {Name: "time", ParameterType: IntType, Required: true},
338 {Name: "offset", ParameterType: IntType},
339 },
340 Matchers: []ParameterRules{
341 {Name: "effect-count", ParameterType: IntType},
342 {Name: "effect-percent", ParameterType: IntType},
343 {Name: "routingkey"},
344 {Name: "producer", ParameterType: BoolType},
345 {Name: "topic"},
346 {Name: "exchange"},
347 {Name: "consumer", ParameterType: BoolType},
348 },
349 },
350 JVMExceptionAction: ActionParameterRules{
351 Flags: []ParameterRules{
352 {Name: "exception", Required: true},
353 {Name: "exception-message"},
354 },
355 Matchers: []ParameterRules{
356 {Name: "effect-count", ParameterType: IntType},
357 {Name: "effect-percent", ParameterType: IntType},
358 {Name: "routingkey"},
359 {Name: "producer", ParameterType: BoolType},
360 {Name: "topic"},
361 {Name: "exchange"},
362 {Name: "consumer", ParameterType: BoolType},
363 },
364 },
365 },
366 TARS: {
367 JVMDelayAction: ActionParameterRules{
368 Flags: []ParameterRules{
369 {Name: "time", ParameterType: IntType, Required: true},
370 {Name: "offset", ParameterType: IntType},
371 },
372 Matchers: []ParameterRules{
373 {Name: "effect-count", ParameterType: IntType},
374 {Name: "effect-percent", ParameterType: IntType},
375 {Name: "servant", ParameterType: BoolType},
376 {Name: "functionname"},
377 {Name: "client", ParameterType: BoolType},
378 {Name: "servantname", Required: true},
379 },
380 },
381 JVMExceptionAction: ActionParameterRules{
382 Flags: []ParameterRules{
383 {Name: "exception", Required: true},
384 {Name: "exception-message"},
385 },
386 Matchers: []ParameterRules{
387 {Name: "effect-count", ParameterType: IntType},
388 {Name: "effect-percent", ParameterType: IntType},
389 {Name: "servant", ParameterType: BoolType},
390 {Name: "functionname"},
391 {Name: "client", ParameterType: BoolType},
392 {Name: "servantname", Required: true},
393 },
394 },
395 },
396 DUBBO: {
397 JVMDelayAction: ActionParameterRules{
398 Flags: []ParameterRules{
399 {Name: "time", ParameterType: IntType, Required: true},
400 {Name: "offset", ParameterType: IntType},
401 },
402 Matchers: []ParameterRules{
403 {Name: "effect-count", ParameterType: IntType},
404 {Name: "effect-percent", ParameterType: IntType},
405 {Name: "appname"},
406 {Name: "provider", ParameterType: BoolType},
407 {Name: "service"},
408 {Name: "version"},
409 {Name: "consumer", ParameterType: BoolType},
410 {Name: "methodname"},
411 {Name: "group"},
412 },
413 },
414 JVMExceptionAction: ActionParameterRules{
415 Flags: []ParameterRules{
416 {Name: "exception", Required: true},
417 {Name: "exception-message"},
418 },
419 Matchers: []ParameterRules{
420 {Name: "effect-count", ParameterType: IntType},
421 {Name: "effect-percent", ParameterType: IntType},
422 {Name: "appname"},
423 {Name: "provider", ParameterType: BoolType},
424 {Name: "service"},
425 {Name: "version"},
426 {Name: "consumer", ParameterType: BoolType},
427 {Name: "methodname"},
428 {Name: "group"},
429 },
430 },
431 JVMThreadPoolFullAction: ActionParameterRules{
432 Matchers: []ParameterRules{
433 {Name: "effect-count", ParameterType: IntType},
434 {Name: "effect-percent", ParameterType: IntType},
435 {Name: "provider", ParameterType: BoolType},
436 },
437 },
438 },
439 JVM: {
440 JVMDelayAction: ActionParameterRules{
441 Flags: []ParameterRules{
442 {Name: "time", ParameterType: IntType, Required: true},
443 {Name: "offset", ParameterType: IntType},
444 },
445 Matchers: []ParameterRules{
446 {Name: "effect-count", ParameterType: IntType},
447 {Name: "effect-percent", ParameterType: IntType},
448 {Name: "classname", Required: true},
449 {Name: "after", ParameterType: BoolType},
450 {Name: "methodname", Required: true},
451 },
452 },
453 JVMExceptionAction: ActionParameterRules{
454 Flags: []ParameterRules{
455 {Name: "exception", Required: true},
456 {Name: "exception-message"},
457 },
458 Matchers: []ParameterRules{
459 {Name: "effect-count", ParameterType: IntType},
460 {Name: "effect-percent", ParameterType: IntType},
461 {Name: "classname", Required: true},
462 {Name: "after", ParameterType: BoolType},
463 {Name: "methodname", Required: true},
464 },
465 },
466 JVMCodeCacheFillingAction: ActionParameterRules{},
467 JVMCpuFullloadAction: ActionParameterRules{
468 Flags: []ParameterRules{
469 {Name: "cpu-count", ParameterType: IntType},
470 },
471 },
472 JVMThrowDeclaredExceptionAction: ActionParameterRules{
473 Matchers: []ParameterRules{
474 {Name: "effect-count", ParameterType: IntType},
475 {Name: "effect-percent", ParameterType: IntType},
476 {Name: "classname", Required: true},
477 {Name: "after", ParameterType: BoolType},
478 {Name: "methodname", Required: true},
479 },
480 },
481 JVMReturnAction: ActionParameterRules{
482 Flags: []ParameterRules{
483 {Name: "value", Required: true},
484 },
485 Matchers: []ParameterRules{
486 {Name: "effect-count", ParameterType: IntType},
487 {Name: "effect-percent", ParameterType: IntType},
488 {Name: "classname", Required: true},
489 {Name: "after", ParameterType: BoolType},
490 {Name: "methodname", Required: true},
491 },
492 },
493 JVMScriptAction: ActionParameterRules{
494 Flags: []ParameterRules{
495 {Name: "script-file"},
496 {Name: "script-type"},
497 {Name: "script-content"},
498 {Name: "script-name"},
499 {Name: "external-jar"},
500 {Name: "external-jar-path"},
501 },
502 Matchers: []ParameterRules{
503 {Name: "effect-count", ParameterType: IntType},
504 {Name: "effect-percent", ParameterType: IntType},
505 {Name: "classname", Required: true},
506 {Name: "after", ParameterType: BoolType},
507 {Name: "methodname", Required: true},
508 },
509 },
510 JVMOOMAction: ActionParameterRules{
511 Flags: []ParameterRules{
512 {Name: "area", Required: true},
513 {Name: "wild-mode", ParameterType: BoolType},
514 {Name: "interval", ParameterType: IntType},
515 {Name: "block", ParameterType: IntType},
516 },
517 },
518 },
519 DRUID: {
520 JVMConnectionPoolFullAction: ActionParameterRules{
521 Matchers: []ParameterRules{
522 {Name: "effect-count", ParameterType: IntType},
523 {Name: "effect-percent", ParameterType: IntType},
524 },
525 },
526 },
527 REDISSON: {
528 JVMDelayAction: ActionParameterRules{
529 Flags: []ParameterRules{
530 {Name: "time", ParameterType: IntType, Required: true},
531 {Name: "offset", ParameterType: IntType},
532 },
533 Matchers: []ParameterRules{
534 {Name: "effect-count", ParameterType: IntType},
535 {Name: "effect-percent", ParameterType: IntType},
536 {Name: "cmd"},
537 {Name: "key"},
538 },
539 },
540 JVMExceptionAction: ActionParameterRules{
541 Flags: []ParameterRules{
542 {Name: "exception", Required: true},
543 {Name: "exception-message"},
544 },
545 Matchers: []ParameterRules{
546 {Name: "effect-count", ParameterType: IntType},
547 {Name: "effect-percent", ParameterType: IntType},
548 {Name: "cmd"},
549 {Name: "key"},
550 },
551 },
552 },
553 ROCKETMQ: {
554 JVMDelayAction: ActionParameterRules{
555 Flags: []ParameterRules{
556 {Name: "time", ParameterType: IntType, Required: true},
557 {Name: "offset", ParameterType: IntType},
558 },
559 Matchers: []ParameterRules{
560 {Name: "effect-count", ParameterType: IntType},
561 {Name: "effect-percent", ParameterType: IntType},
562 {Name: "producerGroup"},
563 {Name: "producer"},
564 {Name: "topic", Required: true},
565 {Name: "consumerGroup"},
566 },
567 },
568 JVMExceptionAction: ActionParameterRules{
569 Flags: []ParameterRules{
570 {Name: "exception", Required: true},
571 {Name: "exception-message"},
572 },
573 Matchers: []ParameterRules{
574 {Name: "effect-count", ParameterType: IntType},
575 {Name: "effect-percent", ParameterType: IntType},
576 {Name: "producerGroup"},
577 {Name: "producer"},
578 {Name: "topic", Required: true},
579 {Name: "consumerGroup"},
580 },
581 },
582 },
583 MONGODB: {
584 JVMDelayAction: ActionParameterRules{
585 Flags: []ParameterRules{
586 {Name: "time", ParameterType: IntType, Required: true},
587 {Name: "offset", ParameterType: IntType},
588 },
589 Matchers: []ParameterRules{
590 {Name: "effect-count", ParameterType: IntType},
591 {Name: "effect-percent", ParameterType: IntType},
592 {Name: "sqltype"},
593 {Name: "database"},
594 {Name: "false"},
595 },
596 },
597 JVMExceptionAction: ActionParameterRules{
598 Flags: []ParameterRules{
599 {Name: "exception", Required: true},
600 {Name: "exception-message"},
601 },
602 Matchers: []ParameterRules{
603 {Name: "effect-count", ParameterType: IntType},
604 {Name: "effect-percent", ParameterType: IntType},
605 {Name: "sqltype"},
606 {Name: "database"},
607 {Name: "false"},
608 },
609 },
610 },
611 }
612
613
614 type ActionParameterRules struct {
615
616 Flags []ParameterRules
617
618
619 Matchers []ParameterRules
620 }
621
622
623 type ParameterType string
624
625 const (
626
627 IntType ParameterType = "int"
628
629
630 BoolType ParameterType = "bool"
631
632
633 StringType ParameterType = "string"
634 )
635
636
637 type ParameterRules struct {
638
639 Name string
640
641
642 ParameterType ParameterType
643
644
645 Required bool
646 }
647