github.com/aclisp/heapster@v0.19.2-0.20160613100040-51756f899a96/metrics/api/v1/api.go (about)

     1  // Copyright 2015 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package v1
    16  
    17  import (
    18  	"time"
    19  
    20  	restful "github.com/emicklei/go-restful"
    21  
    22  	"k8s.io/heapster/metrics/api/v1/types"
    23  	"k8s.io/heapster/metrics/core"
    24  	metricsink "k8s.io/heapster/metrics/sinks/metric"
    25  )
    26  
    27  type Api struct {
    28  	runningInKubernetes bool
    29  	metricSink          *metricsink.MetricSink
    30  	gkeMetrics          map[string]core.MetricDescriptor
    31  	gkeLabels           map[string]core.LabelDescriptor
    32  }
    33  
    34  // Create a new Api to serve from the specified cache.
    35  func NewApi(runningInKubernetes bool, metricSink *metricsink.MetricSink) *Api {
    36  	gkeMetrics := make(map[string]core.MetricDescriptor)
    37  	gkeLabels := make(map[string]core.LabelDescriptor)
    38  	for _, val := range core.StandardMetrics {
    39  		gkeMetrics[val.Name] = val.MetricDescriptor
    40  	}
    41  	for _, val := range core.LabeledMetrics {
    42  		gkeMetrics[val.Name] = val.MetricDescriptor
    43  	}
    44  	gkeMetrics[core.MetricCpuLimit.Name] = core.MetricCpuLimit.MetricDescriptor
    45  	gkeMetrics[core.MetricMemoryLimit.Name] = core.MetricMemoryLimit.MetricDescriptor
    46  
    47  	for _, val := range core.CommonLabels() {
    48  		gkeLabels[val.Key] = val
    49  	}
    50  	for _, val := range core.ContainerLabels() {
    51  		gkeLabels[val.Key] = val
    52  	}
    53  	for _, val := range core.PodLabels() {
    54  		gkeLabels[val.Key] = val
    55  	}
    56  
    57  	return &Api{
    58  		runningInKubernetes: runningInKubernetes,
    59  		metricSink:          metricSink,
    60  		gkeMetrics:          gkeMetrics,
    61  		gkeLabels:           gkeLabels,
    62  	}
    63  }
    64  
    65  // Register the mainApi on the specified endpoint.
    66  func (a *Api) Register(container *restful.Container) {
    67  	ws := new(restful.WebService)
    68  	ws.Path("/api/v1/metric-export").
    69  		Doc("Exports the latest point for all Heapster metrics").
    70  		Produces(restful.MIME_JSON)
    71  	ws.Route(ws.GET("").
    72  		To(a.exportMetrics).
    73  		Doc("export the latest data point for all metrics").
    74  		Operation("exportMetrics").
    75  		Writes([]*types.Timeseries{}))
    76  	container.Add(ws)
    77  	ws = new(restful.WebService)
    78  	ws.Path("/api/v1/metric-export-schema").
    79  		Doc("Schema for metrics exported by heapster").
    80  		Produces(restful.MIME_JSON)
    81  	ws.Route(ws.GET("").
    82  		To(a.exportMetricsSchema).
    83  		Doc("export the schema for all metrics").
    84  		Operation("exportmetricsSchema").
    85  		Writes(types.TimeseriesSchema{}))
    86  	container.Add(ws)
    87  
    88  	if a.metricSink != nil {
    89  		a.RegisterModel(container)
    90  	}
    91  }
    92  
    93  func convertLabelDescriptor(ld core.LabelDescriptor) types.LabelDescriptor {
    94  	return types.LabelDescriptor{
    95  		Key:         ld.Key,
    96  		Description: ld.Description,
    97  	}
    98  }
    99  
   100  func convertMetricDescriptor(md core.MetricDescriptor) types.MetricDescriptor {
   101  	result := types.MetricDescriptor{
   102  		Name:        md.Name,
   103  		Description: md.Description,
   104  		Labels:      make([]types.LabelDescriptor, 0, len(md.Labels)),
   105  	}
   106  	for _, label := range md.Labels {
   107  		result.Labels = append(result.Labels, convertLabelDescriptor(label))
   108  	}
   109  
   110  	switch md.Type {
   111  	case core.MetricCumulative:
   112  		result.Type = "cumulative"
   113  	case core.MetricGauge:
   114  		result.Type = "gauge"
   115  	case core.MetricDelta:
   116  		result.Type = "delta"
   117  	}
   118  
   119  	switch md.ValueType {
   120  	case core.ValueInt64:
   121  		result.ValueType = "int64"
   122  	case core.ValueFloat:
   123  		result.ValueType = "double"
   124  	}
   125  
   126  	switch md.Units {
   127  	case core.UnitsBytes:
   128  		result.Units = "bytes"
   129  	case core.UnitsMilliseconds:
   130  		result.Units = "ms"
   131  	case core.UnitsNanoseconds:
   132  		result.Units = "ns"
   133  	case core.UnitsMillicores:
   134  		result.Units = "millicores"
   135  	}
   136  	return result
   137  }
   138  
   139  func (a *Api) exportMetricsSchema(_ *restful.Request, response *restful.Response) {
   140  	result := types.TimeseriesSchema{
   141  		Metrics:      make([]types.MetricDescriptor, 0),
   142  		CommonLabels: make([]types.LabelDescriptor, 0),
   143  		PodLabels:    make([]types.LabelDescriptor, 0),
   144  	}
   145  	for _, metric := range core.StandardMetrics {
   146  		if _, found := a.gkeMetrics[metric.Name]; found {
   147  			result.Metrics = append(result.Metrics, convertMetricDescriptor(metric.MetricDescriptor))
   148  		}
   149  	}
   150  	for _, metric := range core.AdditionalMetrics {
   151  		if _, found := a.gkeMetrics[metric.Name]; found {
   152  			result.Metrics = append(result.Metrics, convertMetricDescriptor(metric.MetricDescriptor))
   153  		}
   154  	}
   155  	for _, metric := range core.LabeledMetrics {
   156  		if _, found := a.gkeMetrics[metric.Name]; found {
   157  			result.Metrics = append(result.Metrics, convertMetricDescriptor(metric.MetricDescriptor))
   158  		}
   159  	}
   160  
   161  	for _, label := range core.CommonLabels() {
   162  		if _, found := a.gkeLabels[label.Key]; found {
   163  			result.CommonLabels = append(result.CommonLabels, convertLabelDescriptor(label))
   164  		}
   165  	}
   166  	for _, label := range core.ContainerLabels() {
   167  		if _, found := a.gkeLabels[label.Key]; found {
   168  			result.CommonLabels = append(result.CommonLabels, convertLabelDescriptor(label))
   169  		}
   170  	}
   171  	for _, label := range core.PodLabels() {
   172  		if _, found := a.gkeLabels[label.Key]; found {
   173  			result.PodLabels = append(result.PodLabels, convertLabelDescriptor(label))
   174  		}
   175  	}
   176  	response.WriteEntity(result)
   177  }
   178  
   179  func (a *Api) exportMetrics(_ *restful.Request, response *restful.Response) {
   180  	response.PrettyPrint(false)
   181  	response.WriteEntity(a.processMetricsRequest(a.metricSink.GetShortStore()))
   182  }
   183  
   184  func (a *Api) processMetricsRequest(shortStorage []*core.DataBatch) []*types.Timeseries {
   185  	tsmap := make(map[string]*types.Timeseries)
   186  
   187  	var newestBatch *core.DataBatch
   188  	for _, batch := range shortStorage {
   189  		if newestBatch == nil || newestBatch.Timestamp.Before(batch.Timestamp) {
   190  			newestBatch = batch
   191  		}
   192  	}
   193  
   194  	var timeseries []*types.Timeseries
   195  	if newestBatch == nil {
   196  		return timeseries
   197  	}
   198  	for key, ms := range newestBatch.MetricSets {
   199  		ts := tsmap[key]
   200  
   201  		msType := ms.Labels[core.LabelMetricSetType.Key]
   202  
   203  		switch msType {
   204  		case core.MetricSetTypeNode, core.MetricSetTypePod, core.MetricSetTypePodContainer, core.MetricSetTypeSystemContainer:
   205  		default:
   206  			continue
   207  		}
   208  
   209  		if ts == nil {
   210  			ts = &types.Timeseries{
   211  				Metrics: make(map[string][]types.Point),
   212  				Labels:  make(map[string]string),
   213  			}
   214  			for labelName, labelValue := range ms.Labels {
   215  				if _, ok := a.gkeLabels[labelName]; ok {
   216  					ts.Labels[labelName] = labelValue
   217  				}
   218  			}
   219  			if msType == core.MetricSetTypeNode {
   220  				ts.Labels[core.LabelContainerName.Key] = "machine"
   221  			}
   222  			if msType == core.MetricSetTypePod {
   223  				ts.Labels[core.LabelContainerName.Key] = "/pod"
   224  			}
   225  			tsmap[key] = ts
   226  		}
   227  		for metricName, metricVal := range ms.MetricValues {
   228  			if _, ok := a.gkeMetrics[metricName]; ok {
   229  				processPoint(ts, newestBatch, metricName, &metricVal, nil, ms.CreateTime)
   230  			}
   231  		}
   232  		for _, metric := range ms.LabeledMetrics {
   233  			if _, ok := a.gkeMetrics[metric.Name]; ok {
   234  				processPoint(ts, newestBatch, metric.Name, &metric.MetricValue, metric.Labels, ms.CreateTime)
   235  			}
   236  		}
   237  	}
   238  	timeseries = make([]*types.Timeseries, 0, len(tsmap))
   239  	for _, ts := range tsmap {
   240  		timeseries = append(timeseries, ts)
   241  	}
   242  	return timeseries
   243  }
   244  
   245  func processPoint(ts *types.Timeseries, db *core.DataBatch, metricName string, metricVal *core.MetricValue, labels map[string]string, creationTime time.Time) {
   246  	points := ts.Metrics[metricName]
   247  	if points == nil {
   248  		points = make([]types.Point, 0, 1)
   249  	}
   250  	point := types.Point{
   251  		Start: db.Timestamp,
   252  		End:   db.Timestamp,
   253  	}
   254  	// For cumulative metric use the provided start time.
   255  	if metricVal.MetricType == core.MetricCumulative {
   256  		point.Start = creationTime
   257  	}
   258  	var value interface{}
   259  	if metricVal.ValueType == core.ValueInt64 {
   260  		value = metricVal.IntValue
   261  	} else if metricVal.ValueType == core.ValueFloat {
   262  		value = metricVal.FloatValue
   263  	} else {
   264  		return
   265  	}
   266  	point.Value = value
   267  	if labels != nil {
   268  		point.Labels = make(map[string]string)
   269  		for key, value := range labels {
   270  			point.Labels[key] = value
   271  		}
   272  	}
   273  	points = append(points, point)
   274  	ts.Metrics[metricName] = points
   275  }