mirror of
https://github.com/zalando-incubator/kube-metrics-adapter.git
synced 2025-01-03 07:40:09 +00:00
Add support for passing URL query params to pod metric endpoints (#109)
Adds a new metric-config option named `rawQuery`. The value of this option will be appended to the metric `path` as URL query parameters to be used by the pod's application. E.g.,: ``` metric-config.pods.requests-per-second.json-path/rawQuery: "foo=bar&baz=bop" ``` will apppend `?foo=bar&baz=bop` to the URL. Signed-off-by: Abe Friesen <2319792+doyshinda@users.noreply.github.com>
This commit is contained in:
11
README.md
11
README.md
@ -155,6 +155,17 @@ values of JSONPath expressions that evaluate to arrays/slices of numbers.
|
||||
It's optional but when the expression evaluates to an array/slice, it's absence will
|
||||
produce an error. The supported aggregation functions are `avg`, `max`, `min` and `sum`.
|
||||
|
||||
The `raw-query` configuration option specifies the query params to send along to the endpoint:
|
||||
```yaml
|
||||
metric-config.pods.requests-per-second.json-path/path: /metrics
|
||||
metric-config.pods.requests-per-second.json-path/port: "9090"
|
||||
metric-config.pods.requests-per-second.json-path/raw-query: "foo=bar&baz=bop"
|
||||
```
|
||||
will create a URL like this:
|
||||
```
|
||||
http://<podIP>:9090/metrics?foo=bar&baz=bop
|
||||
```
|
||||
|
||||
## Prometheus collector
|
||||
|
||||
The Prometheus collector is a generic collector which can map Prometheus
|
||||
|
@ -25,6 +25,7 @@ type JSONPathMetricsGetter struct {
|
||||
port int
|
||||
aggregator string
|
||||
client *http.Client
|
||||
rawQuery string
|
||||
}
|
||||
|
||||
// NewJSONPathMetricsGetter initializes a new JSONPathMetricsGetter.
|
||||
@ -49,6 +50,10 @@ func NewJSONPathMetricsGetter(config map[string]string) (*JSONPathMetricsGetter,
|
||||
getter.path = v
|
||||
}
|
||||
|
||||
if v, ok := config["raw-query"]; ok {
|
||||
getter.rawQuery = v
|
||||
}
|
||||
|
||||
if v, ok := config["port"]; ok {
|
||||
n, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
@ -83,7 +88,7 @@ func defaultHTTPClient() *http.Client {
|
||||
// endpoint and extracting the desired value using the specified json path
|
||||
// query.
|
||||
func (g *JSONPathMetricsGetter) GetMetric(pod *corev1.Pod) (float64, error) {
|
||||
data, err := g.getPodMetrics(pod, g.scheme, g.path, g.port)
|
||||
data, err := g.getPodMetrics(pod)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -140,20 +145,12 @@ func castSlice(in []interface{}) ([]float64, error) {
|
||||
}
|
||||
|
||||
// getPodMetrics returns the content of the pods metrics endpoint.
|
||||
func (g *JSONPathMetricsGetter) getPodMetrics(pod *corev1.Pod, scheme, path string, port int) ([]byte, error) {
|
||||
func (g *JSONPathMetricsGetter) getPodMetrics(pod *corev1.Pod) ([]byte, error) {
|
||||
if pod.Status.PodIP == "" {
|
||||
return nil, fmt.Errorf("pod %s/%s does not have a pod IP", pod.Namespace, pod.Namespace)
|
||||
}
|
||||
|
||||
if scheme == "" {
|
||||
scheme = "http"
|
||||
}
|
||||
|
||||
metricsURL := url.URL{
|
||||
Scheme: scheme,
|
||||
Host: fmt.Sprintf("%s:%d", pod.Status.PodIP, port),
|
||||
Path: path,
|
||||
}
|
||||
metricsURL := g.buildMetricsURL(pod.Status.PodIP)
|
||||
|
||||
request, err := http.NewRequest(http.MethodGet, metricsURL.String(), nil)
|
||||
if err != nil {
|
||||
@ -178,6 +175,22 @@ func (g *JSONPathMetricsGetter) getPodMetrics(pod *corev1.Pod, scheme, path stri
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// buildMetricsURL will build the full URL needed to hit the pod metric endpoint.
|
||||
func (g *JSONPathMetricsGetter) buildMetricsURL(podIP string) url.URL {
|
||||
var scheme = g.scheme
|
||||
|
||||
if scheme == "" {
|
||||
scheme = "http"
|
||||
}
|
||||
|
||||
return url.URL{
|
||||
Scheme: scheme,
|
||||
Host: fmt.Sprintf("%s:%d", podIP, g.port),
|
||||
Path: g.path,
|
||||
RawQuery: g.rawQuery,
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/oliveagle/jsonpath"
|
||||
@ -70,6 +71,25 @@ func TestNewJSONPathMetricsGetter(t *testing.T) {
|
||||
|
||||
_, err4 := NewJSONPathMetricsGetter(configErrorPort)
|
||||
require.Error(t, err4)
|
||||
|
||||
configWithRawQuery := map[string]string{
|
||||
"json-key": "$.values",
|
||||
"scheme": "http",
|
||||
"path": "/metrics",
|
||||
"port": "9090",
|
||||
"raw-query": "foo=bar&baz=bop",
|
||||
}
|
||||
jpath5, _ := jsonpath.Compile(configWithRawQuery["json-key"])
|
||||
getterWithRawQuery, err5 := NewJSONPathMetricsGetter(configWithRawQuery)
|
||||
|
||||
require.NoError(t, err5)
|
||||
compareMetricsGetter(t, &JSONPathMetricsGetter{
|
||||
jsonPath: jpath5,
|
||||
scheme: "http",
|
||||
path: "/metrics",
|
||||
port: 9090,
|
||||
rawQuery: "foo=bar&baz=bop",
|
||||
}, getterWithRawQuery)
|
||||
}
|
||||
|
||||
func TestCastSlice(t *testing.T) {
|
||||
@ -110,3 +130,44 @@ func TestReduce(t *testing.T) {
|
||||
_, err5 := reduce([]float64{1, 2, 3}, "inexistent_function")
|
||||
require.Errorf(t, err5, "slice of numbers was returned by JSONPath, but no valid aggregator function was specified: %v", "inexistent_function")
|
||||
}
|
||||
|
||||
func TestBuildMetricsURL(t *testing.T) {
|
||||
scheme := "http"
|
||||
ip := "1.2.3.4"
|
||||
port := "9090"
|
||||
path := "/v1/test/"
|
||||
rawQuery := "foo=bar&baz=bop"
|
||||
|
||||
// Test building URL with rawQuery
|
||||
configWithRawQuery := map[string]string{
|
||||
"json-key": "$.value",
|
||||
"scheme": scheme,
|
||||
"path": path,
|
||||
"port": port,
|
||||
"raw-query": rawQuery,
|
||||
}
|
||||
_, err := jsonpath.Compile(configWithRawQuery["json-key"])
|
||||
require.NoError(t, err)
|
||||
getterWithRawQuery, err1 := NewJSONPathMetricsGetter(configWithRawQuery)
|
||||
require.NoError(t, err1)
|
||||
|
||||
expectedURLWithQuery := fmt.Sprintf("%s://%s:%s%s?%s", scheme, ip, port, path, rawQuery)
|
||||
receivedURLWithQuery := getterWithRawQuery.buildMetricsURL(ip)
|
||||
require.Equal(t, receivedURLWithQuery.String(), expectedURLWithQuery)
|
||||
|
||||
// Test building URL without rawQuery
|
||||
configWithNoQuery := map[string]string{
|
||||
"json-key": "$.value",
|
||||
"scheme": scheme,
|
||||
"path": path,
|
||||
"port": port,
|
||||
}
|
||||
_, err2 := jsonpath.Compile(configWithNoQuery["json-key"])
|
||||
require.NoError(t, err2)
|
||||
getterWithNoQuery, err3 := NewJSONPathMetricsGetter(configWithNoQuery)
|
||||
require.NoError(t, err3)
|
||||
|
||||
expectedURLNoQuery := fmt.Sprintf("%s://%s:%s%s", scheme, ip, port, path)
|
||||
receivedURLNoQuery := getterWithNoQuery.buildMetricsURL(ip)
|
||||
require.Equal(t, receivedURLNoQuery.String(), expectedURLNoQuery)
|
||||
}
|
||||
|
Reference in New Issue
Block a user