mirror of
https://github.com/zalando-incubator/kube-metrics-adapter.git
synced 2025-05-10 15:40:02 +00:00
Add support for ajson scripting engine in Pod collector through json-eval config key
Signed-off-by: Angel Alonso <angel.alonso@automattic.com>
This commit is contained in:
@ -62,7 +62,7 @@ func (p *HTTPCollectorPlugin) NewCollector(_ context.Context, hpa *autoscalingv2
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
jsonPathGetter, err := httpmetrics.NewJSONPathMetricsGetter(httpmetrics.DefaultMetricsHTTPClient(), aggFunc, jsonPath)
|
||||
jsonPathGetter, err := httpmetrics.NewJSONPathMetricsGetter(httpmetrics.DefaultMetricsHTTPClient(), aggFunc, jsonPath, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -16,18 +16,22 @@ import (
|
||||
// the json path query.
|
||||
type JSONPathMetricsGetter struct {
|
||||
jsonPath string
|
||||
jsonEval string
|
||||
aggregator AggregatorFunc
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// NewJSONPathMetricsGetter initializes a new JSONPathMetricsGetter.
|
||||
func NewJSONPathMetricsGetter(httpClient *http.Client, aggregatorFunc AggregatorFunc, jsonPath string) (*JSONPathMetricsGetter, error) {
|
||||
func NewJSONPathMetricsGetter(httpClient *http.Client, aggregatorFunc AggregatorFunc, jsonPath string, jsonEval string) (*JSONPathMetricsGetter, error) {
|
||||
// check that jsonPath parses
|
||||
_, err := ajson.ParseJSONPath(jsonPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if jsonPath != "" {
|
||||
_, err := ajson.ParseJSONPath(jsonPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &JSONPathMetricsGetter{client: httpClient, aggregator: aggregatorFunc, jsonPath: jsonPath}, nil
|
||||
|
||||
return &JSONPathMetricsGetter{client: httpClient, aggregator: aggregatorFunc, jsonPath: jsonPath, jsonEval: jsonEval}, nil
|
||||
}
|
||||
|
||||
var DefaultRequestTimeout = 15 * time.Second
|
||||
@ -67,9 +71,19 @@ func (g *JSONPathMetricsGetter) GetMetric(metricsURL url.URL) (float64, error) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
nodes, err := root.JSONPath(g.jsonPath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
var nodes []*ajson.Node
|
||||
if g.jsonPath != "" {
|
||||
nodes, err = root.JSONPath(g.jsonPath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
result, err := ajson.Eval(root, g.jsonEval)
|
||||
nodes = append(nodes, result)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(nodes) == 0 {
|
||||
|
@ -26,6 +26,7 @@ func TestJSONPathMetricsGetter(t *testing.T) {
|
||||
name string
|
||||
jsonResponse []byte
|
||||
jsonPath string
|
||||
jsonEval string
|
||||
result float64
|
||||
aggregator AggregatorFunc
|
||||
err error
|
||||
@ -58,6 +59,19 @@ func TestJSONPathMetricsGetter(t *testing.T) {
|
||||
result: 5,
|
||||
aggregator: Average,
|
||||
},
|
||||
{
|
||||
name: "evaluated script",
|
||||
jsonResponse: []byte(`{"active processes":1,"total processes":10}`),
|
||||
jsonEval: "ceil($['active processes'] / $['total processes'] * 100)",
|
||||
result: 10,
|
||||
aggregator: Average,
|
||||
},
|
||||
{
|
||||
name: "invalid script should error",
|
||||
jsonResponse: []byte(`{"active processes":1,"total processes":10}`),
|
||||
jsonEval: "ceil($['active processes'] ) $['total processes'] * 100)",
|
||||
err: errors.New("wrong request: formula has no left parentheses"),
|
||||
},
|
||||
{
|
||||
name: "json path not resulting in array or number should lead to error",
|
||||
jsonResponse: []byte(`{"metric.value":5}`),
|
||||
@ -74,7 +88,7 @@ func TestJSONPathMetricsGetter(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
server := makeTestHTTPServer(t, tc.jsonResponse)
|
||||
defer server.Close()
|
||||
getter, err := NewJSONPathMetricsGetter(DefaultMetricsHTTPClient(), tc.aggregator, tc.jsonPath)
|
||||
getter, err := NewJSONPathMetricsGetter(DefaultMetricsHTTPClient(), tc.aggregator, tc.jsonPath, tc.jsonEval)
|
||||
require.NoError(t, err)
|
||||
url, err := url.Parse(fmt.Sprintf("%s/metrics", server.URL))
|
||||
require.NoError(t, err)
|
||||
|
@ -33,6 +33,7 @@ func NewPodMetricsJSONPathGetter(config map[string]string) (*PodMetricsJSONPathG
|
||||
getter := PodMetricsJSONPathGetter{}
|
||||
var (
|
||||
jsonPath string
|
||||
jsonEval string
|
||||
aggregator AggregatorFunc
|
||||
err error
|
||||
)
|
||||
@ -41,6 +42,16 @@ func NewPodMetricsJSONPathGetter(config map[string]string) (*PodMetricsJSONPathG
|
||||
jsonPath = v
|
||||
}
|
||||
|
||||
if v, ok := config["json-eval"]; ok {
|
||||
jsonEval = v
|
||||
}
|
||||
|
||||
if jsonPath == "" && jsonEval == "" {
|
||||
return nil, fmt.Errorf("config value json-key or json-eval must be set")
|
||||
} else if jsonPath != "" && jsonEval != "" {
|
||||
return nil, fmt.Errorf("config value json-key and json-eval are mutually exclusive")
|
||||
}
|
||||
|
||||
if v, ok := config["scheme"]; ok {
|
||||
getter.scheme = v
|
||||
}
|
||||
@ -93,7 +104,7 @@ func NewPodMetricsJSONPathGetter(config map[string]string) (*PodMetricsJSONPathG
|
||||
connectTimeout = d
|
||||
}
|
||||
|
||||
jsonPathGetter, err := NewJSONPathMetricsGetter(CustomMetricsHTTPClient(requestTimeout, connectTimeout), aggregator, jsonPath)
|
||||
jsonPathGetter, err := NewJSONPathMetricsGetter(CustomMetricsHTTPClient(requestTimeout, connectTimeout), aggregator, jsonPath, jsonEval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -86,6 +86,17 @@ func TestNewPodJSONPathMetricsGetter(t *testing.T) {
|
||||
port: 9090,
|
||||
rawQuery: "foo=bar&baz=bop",
|
||||
}, getterWithRawQuery)
|
||||
|
||||
configErrorMixedPathEval := map[string]string{
|
||||
"json-key": "{}",
|
||||
"json-eval": "avg($.values)",
|
||||
"scheme": "http",
|
||||
"path": "/metrics",
|
||||
"port": "9090",
|
||||
}
|
||||
|
||||
_, err6 := NewPodMetricsJSONPathGetter(configErrorMixedPathEval)
|
||||
require.Error(t, err6)
|
||||
}
|
||||
|
||||
func TestBuildMetricsURL(t *testing.T) {
|
||||
|
Reference in New Issue
Block a user