@@ -107,13 +107,19 @@ type NetworkDisruptionHostSpec struct {
107107 ConnState string `json:"connState,omitempty" chaos_validate:"omitempty,oneofci=new est"`
108108 // +kubebuilder:validation:Enum=pod;node;pod-fallback-node;node-fallback-pod;""
109109 DNSResolver string `json:"dnsResolver,omitempty" chaos_validate:"omitempty,oneofci=pod node pod-fallback-node node-fallback-pod"`
110+ // +kubebuilder:validation:Minimum=1
111+ // +kubebuilder:validation:Maximum=100
112+ Percentage * int `json:"percentage,omitempty" chaos_validate:"omitempty,gte=1,lte=100"`
110113}
111114
112115type NetworkDisruptionServiceSpec struct {
113116 Name string `json:"name"`
114117 Namespace string `json:"namespace"`
115118 // +optional
116119 Ports []NetworkDisruptionServicePortSpec `json:"ports,omitempty" chaos_validate:"omitempty,dive"`
120+ // +kubebuilder:validation:Minimum=1
121+ // +kubebuilder:validation:Maximum=100
122+ Percentage * int `json:"percentage,omitempty" chaos_validate:"omitempty,gte=1,lte=100"`
117123}
118124
119125type NetworkDisruptionServicePortSpec struct {
@@ -358,20 +364,20 @@ func (s *NetworkDisruptionSpec) GenerateArgs() []string {
358364
359365 // append hosts
360366 for _ , host := range s .Hosts {
361- if host .DNSResolver == "" {
362- args = append (args , "--hosts" , fmt .Sprintf ("%s;%d;%s;%s;%s" , host .Host , host .Port , host .Protocol , host .Flow , host .ConnState ))
363- } else {
364- args = append (args , "--hosts" , fmt .Sprintf ("%s;%d;%s;%s;%s;%s" , host .Host , host .Port , host .Protocol , host .Flow , host .ConnState , host .DNSResolver ))
367+ hostStr := fmt .Sprintf ("%s;%d;%s;%s;%s;%s" , host .Host , host .Port , host .Protocol , host .Flow , host .ConnState , host .DNSResolver )
368+ if host .Percentage != nil {
369+ hostStr = fmt .Sprintf ("%s;%d" , hostStr , * host .Percentage )
365370 }
371+ args = append (args , "--hosts" , hostStr )
366372 }
367373
368374 // append allowed hosts
369375 for _ , host := range s .AllowedHosts {
370- if host .DNSResolver == "" {
371- args = append (args , "--allowed-hosts" , fmt .Sprintf ("%s;%d;%s;%s;%s" , host .Host , host .Port , host .Protocol , host .Flow , host .ConnState ))
372- } else {
373- args = append (args , "--allowed-hosts" , fmt .Sprintf ("%s;%d;%s;%s;%s;%s" , host .Host , host .Port , host .Protocol , host .Flow , host .ConnState , host .DNSResolver ))
376+ hostStr := fmt .Sprintf ("%s;%d;%s;%s;%s;%s" , host .Host , host .Port , host .Protocol , host .Flow , host .ConnState , host .DNSResolver )
377+ if host .Percentage != nil {
378+ hostStr = fmt .Sprintf ("%s;%d" , hostStr , * host .Percentage )
374379 }
380+ args = append (args , "--allowed-hosts" , hostStr )
375381 }
376382
377383 // append services
@@ -381,7 +387,11 @@ func (s *NetworkDisruptionSpec) GenerateArgs() []string {
381387 ports += fmt .Sprintf (";%d-%s" , port .Port , port .Name )
382388 }
383389
384- args = append (args , "--services" , fmt .Sprintf ("%s;%s%s" , service .Name , service .Namespace , ports ))
390+ serviceStr := fmt .Sprintf ("%s;%s%s" , service .Name , service .Namespace , ports )
391+ if service .Percentage != nil {
392+ serviceStr = fmt .Sprintf ("%s;%d" , serviceStr , * service .Percentage )
393+ }
394+ args = append (args , "--services" , serviceStr )
385395 }
386396
387397 if s .HTTP != nil {
@@ -607,7 +617,7 @@ func (s *NetworkDisruptionCloudSpec) Explain() []string {
607617}
608618
609619// NetworkDisruptionHostSpecFromString parses the given hosts to host specs
610- // The expected format for hosts is <host>;<port>;<protocol>;<flow>;<connState>;<dnsResolver>
620+ // The expected format for hosts is <host>;<port>;<protocol>;<flow>;<connState>;<dnsResolver>;<percentage>
611621func NetworkDisruptionHostSpecFromString (hosts []string ) ([]NetworkDisruptionHostSpec , error ) {
612622 var err error
613623
@@ -620,9 +630,10 @@ func NetworkDisruptionHostSpecFromString(hosts []string) ([]NetworkDisruptionHos
620630 flow := ""
621631 connState := ""
622632 dnsResolver := ""
633+ var percentage * int
623634
624- // parse host with format <host>;<port>;<protocol>;<flow>;<connState>;<dnsResolver>
625- parsedHost := strings .SplitN (host , ";" , 6 )
635+ // parse host with format <host>;<port>;<protocol>;<flow>;<connState>;<dnsResolver>;<percentage>
636+ parsedHost := strings .SplitN (host , ";" , 7 )
626637
627638 // cast port to int if specified
628639 if len (parsedHost ) > 1 && parsedHost [1 ] != "" {
@@ -652,6 +663,15 @@ func NetworkDisruptionHostSpecFromString(hosts []string) ([]NetworkDisruptionHos
652663 dnsResolver = parsedHost [5 ]
653664 }
654665
666+ // get percentage if specified
667+ if len (parsedHost ) > 6 && parsedHost [6 ] != "" {
668+ pct , err := strconv .Atoi (parsedHost [6 ])
669+ if err != nil {
670+ return nil , fmt .Errorf ("unexpected percentage parameter in %s: %w" , host , err )
671+ }
672+ percentage = & pct
673+ }
674+
655675 // generate host spec
656676 parsedHosts = append (parsedHosts , NetworkDisruptionHostSpec {
657677 Host : parsedHost [0 ],
@@ -660,28 +680,44 @@ func NetworkDisruptionHostSpecFromString(hosts []string) ([]NetworkDisruptionHos
660680 Flow : flow ,
661681 ConnState : connState ,
662682 DNSResolver : dnsResolver ,
683+ Percentage : percentage ,
663684 })
664685 }
665686
666687 return parsedHosts , nil
667688}
668689
669690// NetworkDisruptionServiceSpecFromString parses the given services to service specs
670- // The expected format for services is <serviceName>;<serviceNamespace>
691+ // The expected format for services is <serviceName>;<serviceNamespace>;<port-value>-<port-name>;<port-value>-<port-name>...;<percentage>
692+ // The percentage field is optional and should be a plain integer at the end (no dash)
671693func NetworkDisruptionServiceSpecFromString (services []string ) ([]NetworkDisruptionServiceSpec , error ) {
672694 parsedServices := []NetworkDisruptionServiceSpec {}
673695
674696 // parse given services
675697 for _ , service := range services {
676- // parse service with format <name>;<namespace>;<port-value>-<port-name>;<port-value>-<port-name>...
698+ // parse service with format <name>;<namespace>;<port-value>-<port-name>;<port-value>-<port-name>...;<percentage>
677699 parsedService := strings .Split (service , ";" )
678700 if len (parsedService ) < 2 {
679- return nil , fmt .Errorf ("service format is expected to follow '<name>;<namespace>;<port-value>-<port-name>;<port-value>-<port-name>', unexpected format detected: %s" , service )
701+ return nil , fmt .Errorf ("service format is expected to follow '<name>;<namespace>;<port-value>-<port-name>;<port-value>-<port-name>;<percentage> ', unexpected format detected: %s" , service )
680702 }
681703
682704 ports := []NetworkDisruptionServicePortSpec {}
705+ var percentage * int
706+ portFields := parsedService [2 :]
707+
708+ // Check if the last field is a percentage (plain integer without dash)
709+ if len (portFields ) > 0 {
710+ lastField := portFields [len (portFields )- 1 ]
711+ if ! strings .Contains (lastField , "-" ) {
712+ // This might be a percentage
713+ if pct , err := strconv .Atoi (lastField ); err == nil {
714+ percentage = & pct
715+ portFields = portFields [:len (portFields )- 1 ] // Remove percentage from port fields
716+ }
717+ }
718+ }
683719
684- for _ , unparsedPort := range parsedService [ 2 :] {
720+ for _ , unparsedPort := range portFields {
685721 // <port-value>-<port-name>
686722 portValue , portName , ok := strings .Cut (unparsedPort , "-" )
687723 if ! ok {
@@ -701,9 +737,10 @@ func NetworkDisruptionServiceSpecFromString(services []string) ([]NetworkDisrupt
701737
702738 // generate service spec
703739 parsedServices = append (parsedServices , NetworkDisruptionServiceSpec {
704- Name : parsedService [0 ],
705- Namespace : parsedService [1 ],
706- Ports : ports ,
740+ Name : parsedService [0 ],
741+ Namespace : parsedService [1 ],
742+ Ports : ports ,
743+ Percentage : percentage ,
707744 })
708745 }
709746
0 commit comments