update libcontainer,gocapability,cadvisor

This commit is contained in:
derekwaynecarr
2016-10-10 14:24:05 -04:00
parent 160adc3864
commit 07bd42a192
40 changed files with 585 additions and 451 deletions

View File

@@ -22,43 +22,43 @@ import (
)
type Config struct {
//the endpoint to hit to scrape metrics
// the endpoint to hit to scrape metrics
Endpoint EndpointConfig `json:"endpoint"`
//holds information about different metrics that can be collected
// holds information about different metrics that can be collected
MetricsConfig []MetricConfig `json:"metrics_config"`
}
// metricConfig holds information extracted from the config file about a metric
type MetricConfig struct {
//the name of the metric
// the name of the metric
Name string `json:"name"`
//enum type for the metric type
// enum type for the metric type
MetricType v1.MetricType `json:"metric_type"`
// metric units to display on UI and in storage (eg: MB, cores)
// this is only used for display.
Units string `json:"units"`
//data type of the metric (eg: int, float)
// data type of the metric (eg: int, float)
DataType v1.DataType `json:"data_type"`
//the frequency at which the metric should be collected
// the frequency at which the metric should be collected
PollingFrequency time.Duration `json:"polling_frequency"`
//the regular expression that can be used to extract the metric
// the regular expression that can be used to extract the metric
Regex string `json:"regex"`
}
type Prometheus struct {
//the endpoint to hit to scrape metrics
// the endpoint to hit to scrape metrics
Endpoint EndpointConfig `json:"endpoint"`
//the frequency at which metrics should be collected
// the frequency at which metrics should be collected
PollingFrequency time.Duration `json:"polling_frequency"`
//holds names of different metrics that can be collected
// holds names of different metrics that can be collected
MetricsConfig []string `json:"metrics_config"`
}

View File

@@ -29,13 +29,13 @@ import (
)
type GenericCollector struct {
//name of the collector
// name of the collector
name string
//holds information extracted from the config file for a collector
// holds information extracted from the config file for a collector
configFile Config
//holds information necessary to extract metrics
// holds information necessary to extract metrics
info *collectorInfo
// The Http client to use when connecting to metric endpoints
@@ -43,10 +43,10 @@ type GenericCollector struct {
}
type collectorInfo struct {
//minimum polling frequency among all metrics
// minimum polling frequency among all metrics
minPollingFrequency time.Duration
//regular expresssions for all metrics
// regular expresssions for all metrics
regexps []*regexp.Regexp
// Limit for the number of srcaped metrics. If the count is higher,
@@ -54,7 +54,7 @@ type collectorInfo struct {
metricCountLimit int
}
//Returns a new collector using the information extracted from the configfile
// Returns a new collector using the information extracted from the configfile
func NewCollector(collectorName string, configFile []byte, metricCountLimit int, containerHandler container.ContainerHandler, httpClient *http.Client) (*GenericCollector, error) {
var configInJSON Config
err := json.Unmarshal(configFile, &configInJSON)
@@ -64,7 +64,7 @@ func NewCollector(collectorName string, configFile []byte, metricCountLimit int,
configInJSON.Endpoint.configure(containerHandler)
//TODO : Add checks for validity of config file (eg : Accurate JSON fields)
// TODO : Add checks for validity of config file (eg : Accurate JSON fields)
if len(configInJSON.MetricsConfig) == 0 {
return nil, fmt.Errorf("No metrics provided in config")
@@ -109,7 +109,7 @@ func NewCollector(collectorName string, configFile []byte, metricCountLimit int,
}, nil
}
//Returns name of the collector
// Returns name of the collector
func (collector *GenericCollector) Name() string {
return collector.name
}
@@ -132,7 +132,7 @@ func (collector *GenericCollector) GetSpec() []v1.MetricSpec {
return specs
}
//Returns collected metrics and the next collection time of the collector
// Returns collected metrics and the next collection time of the collector
func (collector *GenericCollector) Collect(metrics map[string][]v1.MetricVal) (time.Time, map[string][]v1.MetricVal, error) {
currentTime := time.Now()
nextCollectionTime := currentTime.Add(time.Duration(collector.info.minPollingFrequency))

View File

@@ -15,27 +15,30 @@
package collector
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"math"
"io"
"net/http"
"strconv"
"strings"
"sort"
"time"
rawmodel "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
"github.com/prometheus/common/model"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/info/v1"
)
type PrometheusCollector struct {
//name of the collector
// name of the collector
name string
//rate at which metrics are collected
// rate at which metrics are collected
pollingFrequency time.Duration
//holds information extracted from the config file for a collector
// holds information extracted from the config file for a collector
configFile Prometheus
// the metrics to gather (uses a map as a set)
@@ -49,7 +52,7 @@ type PrometheusCollector struct {
httpClient *http.Client
}
//Returns a new collector using the information extracted from the configfile
// Returns a new collector using the information extracted from the configfile
func NewPrometheusCollector(collectorName string, configFile []byte, metricCountLimit int, containerHandler container.ContainerHandler, httpClient *http.Client) (*PrometheusCollector, error) {
var configInJSON Prometheus
err := json.Unmarshal(configFile, &configInJSON)
@@ -84,7 +87,7 @@ func NewPrometheusCollector(collectorName string, configFile []byte, metricCount
return nil, fmt.Errorf("Too many metrics defined: %d limit %d", len(configInJSON.MetricsConfig), metricCountLimit)
}
//TODO : Add checks for validity of config file (eg : Accurate JSON fields)
// TODO : Add checks for validity of config file (eg : Accurate JSON fields)
return &PrometheusCollector{
name: collectorName,
pollingFrequency: minPollingFrequency,
@@ -95,68 +98,110 @@ func NewPrometheusCollector(collectorName string, configFile []byte, metricCount
}, nil
}
//Returns name of the collector
// Returns name of the collector
func (collector *PrometheusCollector) Name() string {
return collector.name
}
func getMetricData(line string) string {
fields := strings.Fields(line)
data := fields[3]
if len(fields) > 4 {
for i := range fields {
if i > 3 {
data = data + "_" + fields[i]
}
}
}
return strings.TrimSpace(data)
}
func (collector *PrometheusCollector) GetSpec() []v1.MetricSpec {
specs := []v1.MetricSpec{}
response, err := collector.httpClient.Get(collector.configFile.Endpoint.URL)
if err != nil {
return specs
return nil
}
defer response.Body.Close()
pageContent, err := ioutil.ReadAll(response.Body)
if err != nil {
return specs
if response.StatusCode != http.StatusOK {
return nil
}
lines := strings.Split(string(pageContent), "\n")
lineCount := len(lines)
for i, line := range lines {
if strings.HasPrefix(line, "# HELP") {
if i+2 >= lineCount {
break
}
dec := expfmt.NewDecoder(response.Body, expfmt.ResponseFormat(response.Header))
stopIndex := strings.IndexAny(lines[i+2], "{ ")
if stopIndex == -1 {
continue
}
var specs []v1.MetricSpec
name := strings.TrimSpace(lines[i+2][0:stopIndex])
if _, ok := collector.metricsSet[name]; collector.metricsSet != nil && !ok {
continue
}
spec := v1.MetricSpec{
Name: name,
Type: v1.MetricType(getMetricData(lines[i+1])),
Format: "float",
Units: getMetricData(lines[i]),
}
specs = append(specs, spec)
for {
d := rawmodel.MetricFamily{}
if err = dec.Decode(&d); err != nil {
break
}
name := d.GetName()
if len(name) == 0 {
continue
}
// If metrics to collect is specified, skip any metrics not in the list to collect.
if _, ok := collector.metricsSet[name]; collector.metricsSet != nil && !ok {
continue
}
spec := v1.MetricSpec{
Name: name,
Type: metricType(d.GetType()),
Format: v1.FloatType,
}
specs = append(specs, spec)
}
if err != nil && err != io.EOF {
return nil
}
return specs
}
//Returns collected metrics and the next collection time of the collector
// metricType converts Prometheus metric type to cadvisor metric type.
// If there is no mapping then just return the name of the Prometheus metric type.
func metricType(t rawmodel.MetricType) v1.MetricType {
switch t {
case rawmodel.MetricType_COUNTER:
return v1.MetricCumulative
case rawmodel.MetricType_GAUGE:
return v1.MetricGauge
default:
return v1.MetricType(t.String())
}
}
type prometheusLabels []*rawmodel.LabelPair
func labelSetToLabelPairs(labels model.Metric) prometheusLabels {
var promLabels prometheusLabels
for k, v := range labels {
name := string(k)
value := string(v)
promLabels = append(promLabels, &rawmodel.LabelPair{Name: &name, Value: &value})
}
return promLabels
}
func (s prometheusLabels) Len() int { return len(s) }
func (s prometheusLabels) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// ByName implements sort.Interface by providing Less and using the Len and
// Swap methods of the embedded PrometheusLabels value.
type byName struct{ prometheusLabels }
func (s byName) Less(i, j int) bool {
return s.prometheusLabels[i].GetName() < s.prometheusLabels[j].GetName()
}
func prometheusLabelSetToCadvisorLabel(promLabels model.Metric) string {
labels := labelSetToLabelPairs(promLabels)
sort.Sort(byName{labels})
var b bytes.Buffer
for i, l := range labels {
if i > 0 {
b.WriteString("\xff")
}
b.WriteString(l.GetName())
b.WriteString("=")
b.WriteString(l.GetValue())
}
return string(b.Bytes())
}
// Returns collected metrics and the next collection time of the collector
func (collector *PrometheusCollector) Collect(metrics map[string][]v1.MetricVal) (time.Time, map[string][]v1.MetricVal, error) {
currentTime := time.Now()
nextCollectionTime := currentTime.Add(time.Duration(collector.pollingFrequency))
@@ -168,59 +213,61 @@ func (collector *PrometheusCollector) Collect(metrics map[string][]v1.MetricVal)
}
defer response.Body.Close()
pageContent, err := ioutil.ReadAll(response.Body)
if err != nil {
return nextCollectionTime, nil, err
if response.StatusCode != http.StatusOK {
return nextCollectionTime, nil, fmt.Errorf("server returned HTTP status %s", response.Status)
}
var errorSlice []error
lines := strings.Split(string(pageContent), "\n")
sdec := expfmt.SampleDecoder{
Dec: expfmt.NewDecoder(response.Body, expfmt.ResponseFormat(response.Header)),
Opts: &expfmt.DecodeOptions{
Timestamp: model.TimeFromUnixNano(currentTime.UnixNano()),
},
}
newMetrics := make(map[string][]v1.MetricVal)
for _, line := range lines {
if line == "" {
var (
// 50 is chosen as a reasonable guesstimate at a number of metrics we can
// expect from virtually any endpoint to try to save allocations.
decSamples = make(model.Vector, 0, 50)
newMetrics = make(map[string][]v1.MetricVal)
)
for {
if err = sdec.Decode(&decSamples); err != nil {
break
}
if !strings.HasPrefix(line, "# HELP") && !strings.HasPrefix(line, "# TYPE") {
var metLabel string
startLabelIndex := strings.Index(line, "{")
spaceIndex := strings.Index(line, " ")
if startLabelIndex == -1 {
startLabelIndex = spaceIndex
}
metName := strings.TrimSpace(line[0:startLabelIndex])
for _, sample := range decSamples {
metName := string(sample.Metric[model.MetricNameLabel])
if len(metName) == 0 {
continue
}
// If metrics to collect is specified, skip any metrics not in the list to collect.
if _, ok := collector.metricsSet[metName]; collector.metricsSet != nil && !ok {
continue
}
if startLabelIndex+1 <= spaceIndex-1 {
metLabel = strings.TrimSpace(line[(startLabelIndex + 1):(spaceIndex - 1)])
}
metVal, err := strconv.ParseFloat(line[spaceIndex+1:], 64)
if err != nil {
errorSlice = append(errorSlice, err)
}
if math.IsNaN(metVal) {
metVal = 0
}
// TODO Handle multiple labels nicer. Prometheus metrics can have multiple
// labels, cadvisor only accepts a single string for the metric label.
label := prometheusLabelSetToCadvisorLabel(sample.Metric)
metric := v1.MetricVal{
Label: metLabel,
FloatValue: metVal,
Timestamp: currentTime,
FloatValue: float64(sample.Value),
Timestamp: sample.Timestamp.Time(),
Label: label,
}
newMetrics[metName] = append(newMetrics[metName], metric)
if len(newMetrics) > collector.metricCountLimit {
return nextCollectionTime, nil, fmt.Errorf("too many metrics to collect")
}
}
decSamples = decSamples[:0]
}
if err != nil && err != io.EOF {
return nextCollectionTime, nil, err
}
for key, val := range newMetrics {
metrics[key] = append(metrics[key], val...)
}
return nextCollectionTime, metrics, compileErrors(errorSlice)
return nextCollectionTime, metrics, nil
}