This fixes an issue with the type switch that was never able to fall (#88)

into cases of []<number>, being <number> a number type such as int,
float32, float64. This is because Go can't type cast slices of
interface{} out right because it's impossible to know the true types of
the slice members beforehand.

Signed-off-by: Tomás Pinho <me@tomaspinho.com>
This commit is contained in:
Tomás Pinho
2019-11-05 08:43:25 +00:00
committed by Arjun
parent f6b2aede5b
commit 0790bc351a
2 changed files with 40 additions and 39 deletions

View File

@@ -89,17 +89,38 @@ func (g *JSONPathMetricsGetter) GetMetric(pod *corev1.Pod) (float64, error) {
return float64(res), nil
case float64:
return res, nil
case []int:
return reduce(intsToFloat64s(res), g.aggregator)
case []float32:
return reduce(float32sToFloat64s(res), g.aggregator)
case []float64:
return reduce(res, g.aggregator)
case []interface{}:
s, err := castSlice(res)
if err != nil {
return 0, err
}
return reduce(s, g.aggregator)
default:
return 0, fmt.Errorf("unsupported type %T", res)
}
}
// castSlice takes a slice of interface and returns a slice of float64 if all
// values in slice were castable, else returns an error
func castSlice(in []interface{}) ([]float64, error) {
out := []float64{}
for _, v := range in {
switch v := v.(type) {
case int:
out = append(out, float64(v))
case float32:
out = append(out, float64(v))
case float64:
out = append(out, v)
default:
return nil, fmt.Errorf("slice was returned by JSONPath, but value inside is unsupported: %T", v)
}
}
return out, nil
}
// getPodMetrics returns the content of the pods metrics endpoint.
func getPodMetrics(pod *corev1.Pod, scheme, path string, port int) ([]byte, error) {
if pod.Status.PodIP == "" {
@@ -144,24 +165,6 @@ func getPodMetrics(pod *corev1.Pod, scheme, path string, port int) ([]byte, erro
return data, nil
}
// intsToFloat64s will convert a slice of int to a slice of float64
func intsToFloat64s(in []int) (out []float64) {
out = []float64{}
for _, v := range in {
out = append(out, float64(v))
}
return
}
// float32sToFloat64s will convert a slice of float32 to a slice of float64
func float32sToFloat64s(in []float32) (out []float64) {
out = []float64{}
for _, v := range in {
out = append(out, float64(v))
}
return
}
// reduce will reduce a slice of numbers given a aggregator function's name. If it's empty or not recognized, an error is returned.
func reduce(values []float64, aggregator string) (float64, error) {
switch aggregator {

View File

@@ -65,24 +65,22 @@ func TestNewJSONPathMetricsGetter(t *testing.T) {
require.Error(t, err4)
}
func TestIntsToFloat64s(t *testing.T) {
noInts := []int{}
noFloat64s := intsToFloat64s(noInts)
require.Equal(t, []float64{}, noFloat64s)
func TestCastSlice(t *testing.T) {
res1, err1 := castSlice([]interface{}{1, 2, 3})
require.NoError(t, err1)
require.Equal(t, []float64{1.0, 2.0, 3.0}, res1)
someInts := []int{1, 2, 3}
someFloat64s := intsToFloat64s(someInts)
require.Equal(t, []float64{1.0, 2.0, 3.0}, someFloat64s)
}
res2, err2 := castSlice([]interface{}{float32(1.0), float32(2.0), float32(3.0)})
require.NoError(t, err2)
require.Equal(t, []float64{1.0, 2.0, 3.0}, res2)
func TestFloat32sToFloat64s(t *testing.T) {
noFloat32s := []float32{}
noFloat64s := float32sToFloat64s(noFloat32s)
require.Equal(t, []float64{}, noFloat64s)
res3, err3 := castSlice([]interface{}{float64(1.0), float64(2.0), float64(3.0)})
require.NoError(t, err3)
require.Equal(t, []float64{1.0, 2.0, 3.0}, res3)
someFloat32s := []float32{1.0, 2.0, 3.0}
someFloat64s := float32sToFloat64s(someFloat32s)
require.Equal(t, []float64{1.0, 2.0, 3.0}, someFloat64s)
res4, err4 := castSlice([]interface{}{1, 2, "some string"})
require.Errorf(t, err4, "slice was returned by JSONPath, but value inside is unsupported: %T", "string")
require.Equal(t, []float64(nil), res4)
}
func TestReduce(t *testing.T) {