888e76b748
Signed-off-by: Arjun Naik <arjun.rn@gmail.com>
134 lines
2.8 KiB
Go
134 lines
2.8 KiB
Go
package collector
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/oliveagle/jsonpath"
|
|
corev1 "k8s.io/api/core/v1"
|
|
)
|
|
|
|
// JSONPathMetricsGetter is a metrics getter which looks up pod metrics by
|
|
// querying the pods metrics endpoint and lookup the metric value as defined by
|
|
// the json path query.
|
|
type JSONPathMetricsGetter struct {
|
|
jsonPath *jsonpath.Compiled
|
|
scheme string
|
|
path string
|
|
port int
|
|
}
|
|
|
|
// NewJSONPathMetricsGetter initializes a new JSONPathMetricsGetter.
|
|
func NewJSONPathMetricsGetter(config map[string]string) (*JSONPathMetricsGetter, error) {
|
|
getter := &JSONPathMetricsGetter{}
|
|
|
|
if v, ok := config["json-key"]; ok {
|
|
pat, err := jsonpath.Compile(v)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse json path definition: %v", err)
|
|
}
|
|
|
|
getter.jsonPath = pat
|
|
}
|
|
|
|
if v, ok := config["scheme"]; ok {
|
|
getter.scheme = v
|
|
}
|
|
|
|
if v, ok := config["path"]; ok {
|
|
getter.path = v
|
|
}
|
|
|
|
if v, ok := config["port"]; ok {
|
|
n, err := strconv.Atoi(v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
getter.port = n
|
|
}
|
|
|
|
return getter, nil
|
|
}
|
|
|
|
// GetMetric gets metric from pod by fetching json metrics from the pods metric
|
|
// endpoint and extracting the desired value using the specified json path
|
|
// query.
|
|
func (g *JSONPathMetricsGetter) GetMetric(pod *corev1.Pod) (float64, error) {
|
|
data, err := getPodMetrics(pod, g.scheme, g.path, g.port)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// parse data
|
|
var jsonData interface{}
|
|
err = json.Unmarshal(data, &jsonData)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
res, err := g.jsonPath.Lookup(jsonData)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
switch res := res.(type) {
|
|
case int:
|
|
return float64(res), nil
|
|
case float32:
|
|
return float64(res), nil
|
|
case float64:
|
|
return res, nil
|
|
default:
|
|
return 0, fmt.Errorf("unsupported type %T", res)
|
|
}
|
|
}
|
|
|
|
// 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 == "" {
|
|
return nil, fmt.Errorf("pod %s/%s does not have a pod IP", pod.Namespace, pod.Namespace)
|
|
}
|
|
|
|
httpClient := &http.Client{
|
|
Timeout: 15 * time.Second,
|
|
Transport: &http.Transport{},
|
|
}
|
|
|
|
if scheme == "" {
|
|
scheme = "http"
|
|
}
|
|
|
|
metricsURL := url.URL{
|
|
Scheme: scheme,
|
|
Host: fmt.Sprintf("%s:%d", pod.Status.PodIP, port),
|
|
Path: path,
|
|
}
|
|
|
|
request, err := http.NewRequest(http.MethodGet, metricsURL.String(), nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resp, err := httpClient.Do(request)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
data, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("unsuccessful response: %s", resp.Status)
|
|
}
|
|
|
|
return data, nil
|
|
}
|