Skip to content

Commit

Permalink
Unify fault injection commands (#356)
Browse files Browse the repository at this point in the history
* Add IntOrStr type
* Use IntOrString for fault ports
* Unify pod and service fault injection commands

Signed-off-by: Pablo Chacin <[email protected]>
  • Loading branch information
pablochacin authored Oct 23, 2023
1 parent b5d3813 commit 75eb36e
Show file tree
Hide file tree
Showing 13 changed files with 476 additions and 244 deletions.
14 changes: 8 additions & 6 deletions e2e/disruptors/pod_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import (
"time"

"github.com/grafana/xk6-disruptor/pkg/agent/protocol"
"github.com/grafana/xk6-disruptor/pkg/types/intstr"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/intstr"
k8sintstr "k8s.io/apimachinery/pkg/util/intstr"

"github.com/grafana/xk6-disruptor/pkg/disruptors"
"github.com/grafana/xk6-disruptor/pkg/kubernetes"
Expand Down Expand Up @@ -71,7 +73,7 @@ func Test_PodDisruptor(t *testing.T) {
port: 80,
injector: func(d disruptors.PodDisruptor) error {
fault := disruptors.HTTPFault{
Port: 80,
Port: intstr.FromInt32(80),
ErrorRate: 1.0,
ErrorCode: 500,
}
Expand All @@ -97,7 +99,7 @@ func Test_PodDisruptor(t *testing.T) {
port: 9000,
injector: func(d disruptors.PodDisruptor) error {
fault := disruptors.GrpcFault{
Port: 9000,
Port: intstr.FromInt32(9000),
ErrorRate: 1.0,
StatusCode: 14,
Exclude: "grpc.reflection.v1alpha.ServerReflection,grpc.reflection.v1.ServerReflection",
Expand Down Expand Up @@ -135,7 +137,7 @@ func Test_PodDisruptor(t *testing.T) {
namespace,
tc.pod,
tc.service,
intstr.FromInt(tc.port),
k8sintstr.FromInt(tc.port),
30*time.Second,
)
if err != nil {
Expand Down Expand Up @@ -225,7 +227,7 @@ func Test_PodDisruptor(t *testing.T) {
namespace,
fixtures.BuildHttpbinPod(),
service,
intstr.FromInt(80),
k8sintstr.FromInt(80),
30*time.Second,
)
if err != nil {
Expand All @@ -251,7 +253,7 @@ func Test_PodDisruptor(t *testing.T) {
}

fault := disruptors.HTTPFault{
Port: 80,
Port: intstr.FromInt32(80),
ErrorRate: 1.0,
ErrorCode: 500,
}
Expand Down
7 changes: 4 additions & 3 deletions e2e/disruptors/service_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"time"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/intstr"
k8sintstr "k8s.io/apimachinery/pkg/util/intstr"

"github.com/grafana/xk6-disruptor/pkg/disruptors"
"github.com/grafana/xk6-disruptor/pkg/kubernetes"
Expand All @@ -18,6 +18,7 @@ import (
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/deploy"
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/fixtures"
"github.com/grafana/xk6-disruptor/pkg/testutils/e2e/kubernetes/namespace"
"github.com/grafana/xk6-disruptor/pkg/types/intstr"
)

func Test_ServiceDisruptor(t *testing.T) {
Expand Down Expand Up @@ -65,7 +66,7 @@ func Test_ServiceDisruptor(t *testing.T) {
port: 80,
injector: func(d disruptors.ServiceDisruptor) error {
fault := disruptors.HTTPFault{
Port: 80,
Port: intstr.FromInt32(80),
ErrorRate: 1.0,
ErrorCode: 500,
}
Expand Down Expand Up @@ -100,7 +101,7 @@ func Test_ServiceDisruptor(t *testing.T) {
namespace,
tc.pod,
tc.service,
intstr.FromInt(tc.port),
k8sintstr.FromInt(tc.port),
30*time.Second,
)
if err != nil {
Expand Down
39 changes: 37 additions & 2 deletions pkg/api/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package api

import (
"fmt"
"math"
"reflect"
"time"

"github.com/grafana/xk6-disruptor/pkg/types/intstr"
)

// Convert converts from a generic object received from the JS interface via goja into a go type.
Expand All @@ -17,6 +20,7 @@ import (
// string <-- string
// time.Duration <-- string
// time.Time <-- string (only in RFC3339 format)
// IntOrStr <-- string or int64 (only supports int32 values)
//
// (1) TODO: support other key types, such as numeric and attempt conversion from the string key
func Convert(value interface{}, target interface{}) error {
Expand Down Expand Up @@ -47,6 +51,14 @@ func Convert(value interface{}, target interface{}) error {
if targetValue.Type().String() == "time.Duration" {
return convertDuration(value, target)
}

if targetValue.Type().String() == "intstr.IntOrString" {
return convertIntOrString(value, target)
}
case reflect.String:
if targetValue.Type().String() == "intstr.IntOrString" {
return convertIntOrString(value, target)
}
}

// try default conversions
Expand Down Expand Up @@ -147,8 +159,7 @@ func convertDuration(value interface{}, target interface{}) error {
}

targetValue.Set(reflect.ValueOf(duration))

return err
return nil
}

func convertTime(value interface{}, target interface{}) error {
Expand All @@ -167,3 +178,27 @@ func convertTime(value interface{}, target interface{}) error {
targetValue.Set(reflect.ValueOf(timeValue))
return nil
}

func convertIntOrString(value interface{}, target interface{}) error {
targetValue := reflect.ValueOf(target).Elem()

int64Value, ok := value.(int64)
if ok {
// check overflow here to avoid panic in the conversion
if int64Value > math.MaxInt32 || int64Value < math.MinInt32 {
return fmt.Errorf("value overflows int32 range: %d", int64Value)
}
intOrStrValue := intstr.FromInt32(int32(int64Value))
targetValue.Set(reflect.ValueOf(intOrStrValue))
return nil
}

stringValue, ok := value.(string)
if ok {
intOrStrValue := intstr.FromString(stringValue)
targetValue.Set(reflect.ValueOf(intOrStrValue))
return nil
}

return fmt.Errorf("expected int or string value got %s", reflect.TypeOf(value))
}
71 changes: 50 additions & 21 deletions pkg/api/convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"reflect"
"testing"
"time"

"github.com/grafana/xk6-disruptor/pkg/types/intstr"
)

func Test_Conversions(t *testing.T) {
Expand All @@ -13,13 +15,15 @@ func Test_Conversions(t *testing.T) {
SubfieldString string
}
type TypedFields struct {
FieldString string
FieldDuration time.Duration
FieldInt int64
FieldFloat float64
FieldStruct StructField
FieldMap map[string]string
FieldArray []string
IntOrStrStr intstr.IntOrString
IntOrStrInt intstr.IntOrString
String string
Duration time.Duration
Int int64
Float float64
Struct StructField
Map map[string]string
Array []string
}

testCases := []struct {
Expand All @@ -43,6 +47,27 @@ func Test_Conversions(t *testing.T) {
expected: int64(1),
expectError: false,
},
{
description: "IntOrString int conversion",
value: int64(1),
target: new(intstr.IntOrString),
expected: intstr.FromInt32(1),
expectError: false,
},
{
description: "IntOrString string to int conversion",
value: "1",
target: new(intstr.IntOrString),
expected: intstr.FromInt32(1),
expectError: false,
},
{
description: "IntOrString string conversion",
value: "one",
target: new(intstr.IntOrString),
expected: intstr.FromString("one"),
expectError: false,
},
{
description: "Float to Int conversion",
value: float64(1.0),
Expand Down Expand Up @@ -153,31 +178,35 @@ func Test_Conversions(t *testing.T) {
{
description: "Struct field conversion",
value: map[string]interface{}{
"fieldString": "string",
"fieldInt": int64(1),
"fieldDuration": "1s",
"fieldFloat": float64(1.0),
"fieldStruct": map[string]interface{}{
"intOrStrStr": "uno",
"intOrStrInt": int64(1),
"string": "string",
"int": int64(1),
"duration": "1s",
"float": float64(1.0),
"struct": map[string]interface{}{
"subfieldInt": int64(0),
"subfieldString": "string",
},
"fieldMap": map[string]interface{}{
"map": map[string]interface{}{
"key": "value",
},
"fieldArray": []interface{}{"string"},
"array": []interface{}{"string"},
},
target: &TypedFields{},
expected: TypedFields{
FieldString: "string",
FieldInt: 1,
FieldDuration: time.Second,
FieldFloat: 1.0,
FieldStruct: StructField{
IntOrStrStr: intstr.FromString("uno"),
IntOrStrInt: intstr.FromInt32(1),
String: "string",
Int: 1,
Duration: time.Second,
Float: 1.0,
Struct: StructField{
SubfieldInt: 0,
SubfieldString: "string",
},
FieldArray: []string{"string"},
FieldMap: map[string]string{
Array: []string{"string"},
Map: map[string]string{
"key": "value",
},
},
Expand Down
Loading

0 comments on commit 75eb36e

Please sign in to comment.