github.com/jonaz/heapster@v1.3.0-beta.0.0.20170208112634-cd3c15ca3d29/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  	historicalSource    core.HistoricalSource
    31  	gkeMetrics          map[string]core.MetricDescriptor
    32  	gkeLabels           map[string]core.LabelDescriptor
    33  }
    34  
    35  // Create a new Api to serve from the specified cache.
    36  func NewApi(runningInKubernetes bool, metricSink *metricsink.MetricSink, historicalSource core.HistoricalSource) *Api {
    37  	gkeMetrics := make(map[string]core.MetricDescriptor)
    38  	gkeLabels := make(map[string]core.LabelDescriptor)
    39  	for _, val := range core.StandardMetrics {
    40  		gkeMetrics[val.Name] = val.MetricDescriptor
    41  	}
    42  	for _, val := range core.LabeledMetrics {
    43  		gkeMetrics[val.Name] = val.MetricDescriptor
    44  	}
    45  	gkeMetrics[core.MetricCpuLimit.Name] = core.MetricCpuLimit.MetricDescriptor
    46  	gkeMetrics[core.MetricMemoryLimit.Name] = core.MetricMemoryLimit.MetricDescriptor
    47  
    48  	for _, val := range core.CommonLabels() {
    49  		gkeLabels[val.Key] = val
    50  	}
    51  	for _, val := range core.ContainerLabels() {
    52  		gkeLabels[val.Key] = val
    53  	}
    54  	for _, val := range core.PodLabels() {
    55  		gkeLabels[val.Key] = val
    56  	}
    57  
    58  	return &Api{
    59  		runningInKubernetes: runningInKubernetes,
    60  		metricSink:          metricSink,
    61  		historicalSource:    historicalSource,
    62  		gkeMetrics:          gkeMetrics,
    63  		gkeLabels:           gkeLabels,
    64  	}
    65  }
    66  
    67  // Register the mainApi on the specified endpoint.
    68  func (a *Api) Register(container *restful.Container) {
    69  	ws := new(restful.WebService)
    70  	ws.Path("/api/v1/metric-export").
    71  		Doc("Exports the latest point for all Heapster metrics").
    72  		Produces(restful.MIME_JSON)
    73  	ws.Route(ws.GET("").
    74  		To(a.exportMetrics).
    75  		Doc("export the latest data point for all metrics").
    76  		Operation("exportMetrics").
    77  		Writes([]*types.Timeseries{}))
    78  	container.Add(ws)
    79  	ws = new(restful.WebService)
    80  	ws.Path("/api/v1/metric-export-schema").
    81  		Doc("Schema for metrics exported by heapster").
    82  		Produces(restful.MIME_JSON)
    83  	ws.Route(ws.GET("").
    84  		To(a.exportMetricsSchema).
    85  		Doc("export the schema for all metrics").
    86  		Operation("exportmetricsSchema").
    87  		Writes(types.TimeseriesSchema{}))
    88  	container.Add(ws)
    89  
    90  	if a.metricSink != nil {
    91  		a.RegisterModel(container)
    92  	}
    93  
    94  	if a.historicalSource != nil {
    95  		a.RegisterHistorical(container)
    96  	}
    97  }
    98  
    99  func convertLabelDescriptor(ld core.LabelDescriptor) types.LabelDescriptor {
   100  	return types.LabelDescriptor{
   101  		Key:         ld.Key,
   102  		Description: ld.Description,
   103  	}
   104  }
   105  
   106  func convertMetricDescriptor(md core.MetricDescriptor) types.MetricDescriptor {
   107  	result := types.MetricDescriptor{
   108  		Name:        md.Name,
   109  		Description: md.Description,
   110  		Labels:      make([]types.LabelDescriptor, 0, len(md.Labels)),
   111  	}
   112  	for _, label := range md.Labels {
   113  		result.Labels = append(result.Labels, convertLabelDescriptor(label))
   114  	}
   115  
   116  	switch md.Type {
   117  	case core.MetricCumulative:
   118  		result.Type = "cumulative"
   119  	case core.MetricGauge:
   120  		result.Type = "gauge"
   121  	case core.MetricDelta:
   122  		result.Type = "delta"
   123  	}
   124  
   125  	switch md.ValueType {
   126  	case core.ValueInt64:
   127  		result.ValueType = "int64"
   128  	case core.ValueFloat:
   129  		result.ValueType = "double"
   130  	}
   131  
   132  	switch md.Units {
   133  	case core.UnitsBytes:
   134  		result.Units = "bytes"
   135  	case core.UnitsMilliseconds:
   136  		result.Units = "ms"
   137  	case core.UnitsNanoseconds:
   138  		result.Units = "ns"
   139  	case core.UnitsMillicores:
   140  		result.Units = "millicores"
   141  	}
   142  	return result
   143  }
   144  
   145  func (a *Api) exportMetricsSchema(_ *restful.Request, response *restful.Response) {
   146  	result := types.TimeseriesSchema{
   147  		Metrics:      make([]types.MetricDescriptor, 0),
   148  		CommonLabels: make([]types.LabelDescriptor, 0),
   149  		PodLabels:    make([]types.LabelDescriptor, 0),
   150  	}
   151  	for _, metric := range core.StandardMetrics {
   152  		if _, found := a.gkeMetrics[metric.Name]; found {
   153  			result.Metrics = append(result.Metrics, convertMetricDescriptor(metric.MetricDescriptor))
   154  		}
   155  	}
   156  	for _, metric := range core.AdditionalMetrics {
   157  		if _, found := a.gkeMetrics[metric.Name]; found {
   158  			result.Metrics = append(result.Metrics, convertMetricDescriptor(metric.MetricDescriptor))
   159  		}
   160  	}
   161  	for _, metric := range core.LabeledMetrics {
   162  		if _, found := a.gkeMetrics[metric.Name]; found {
   163  			result.Metrics = append(result.Metrics, convertMetricDescriptor(metric.MetricDescriptor))
   164  		}
   165  	}
   166  
   167  	for _, label := range core.CommonLabels() {
   168  		if _, found := a.gkeLabels[label.Key]; found {
   169  			result.CommonLabels = append(result.CommonLabels, convertLabelDescriptor(label))
   170  		}
   171  	}
   172  	for _, label := range core.ContainerLabels() {
   173  		if _, found := a.gkeLabels[label.Key]; found {
   174  			result.CommonLabels = append(result.CommonLabels, convertLabelDescriptor(label))
   175  		}
   176  	}
   177  	for _, label := range core.PodLabels() {
   178  		if _, found := a.gkeLabels[label.Key]; found {
   179  			result.PodLabels = append(result.PodLabels, convertLabelDescriptor(label))
   180  		}
   181  	}
   182  	response.WriteEntity(result)
   183  }
   184  
   185  func (a *Api) exportMetrics(_ *restful.Request, response *restful.Response) {
   186  	response.PrettyPrint(false)
   187  	response.WriteEntity(a.processMetricsRequest(a.metricSink.GetShortStore()))
   188  }
   189  
   190  func (a *Api) processMetricsRequest(shortStorage []*core.DataBatch) []*types.Timeseries {
   191  	tsmap := make(map[string]*types.Timeseries)
   192  
   193  	var newestBatch *core.DataBatch
   194  	for _, batch := range shortStorage {
   195  		if newestBatch == nil || newestBatch.Timestamp.Before(batch.Timestamp) {
   196  			newestBatch = batch
   197  		}
   198  	}
   199  
   200  	var timeseries []*types.Timeseries
   201  	if newestBatch == nil {
   202  		return timeseries
   203  	}
   204  	for key, ms := range newestBatch.MetricSets {
   205  		ts := tsmap[key]
   206  
   207  		msType := ms.Labels[core.LabelMetricSetType.Key]
   208  
   209  		switch msType {
   210  		case core.MetricSetTypeNode, core.MetricSetTypePod, core.MetricSetTypePodContainer, core.MetricSetTypeSystemContainer:
   211  		default:
   212  			continue
   213  		}
   214  
   215  		if ts == nil {
   216  			ts = &types.Timeseries{
   217  				Metrics: make(map[string][]types.Point),
   218  				Labels:  make(map[string]string),
   219  			}
   220  			for labelName, labelValue := range ms.Labels {
   221  				if _, ok := a.gkeLabels[labelName]; ok {
   222  					ts.Labels[labelName] = labelValue
   223  				}
   224  			}
   225  			if msType == core.MetricSetTypeNode {
   226  				ts.Labels[core.LabelContainerName.Key] = "machine"
   227  			}
   228  			if msType == core.MetricSetTypePod {
   229  				ts.Labels[core.LabelContainerName.Key] = "/pod"
   230  			}
   231  			tsmap[key] = ts
   232  		}
   233  		for metricName, metricVal := range ms.MetricValues {
   234  			if _, ok := a.gkeMetrics[metricName]; ok {
   235  				processPoint(ts, newestBatch, metricName, &metricVal, nil, ms.CreateTime)
   236  			}
   237  		}
   238  		for _, metric := range ms.LabeledMetrics {
   239  			if _, ok := a.gkeMetrics[metric.Name]; ok {
   240  				processPoint(ts, newestBatch, metric.Name, &metric.MetricValue, metric.Labels, ms.CreateTime)
   241  			}
   242  		}
   243  	}
   244  	timeseries = make([]*types.Timeseries, 0, len(tsmap))
   245  	for _, ts := range tsmap {
   246  		timeseries = append(timeseries, ts)
   247  	}
   248  	return timeseries
   249  }
   250  
   251  func processPoint(ts *types.Timeseries, db *core.DataBatch, metricName string, metricVal *core.MetricValue, labels map[string]string, creationTime time.Time) {
   252  	points := ts.Metrics[metricName]
   253  	if points == nil {
   254  		points = make([]types.Point, 0, 1)
   255  	}
   256  	point := types.Point{
   257  		Start: db.Timestamp,
   258  		End:   db.Timestamp,
   259  	}
   260  	// For cumulative metric use the provided start time.
   261  	if metricVal.MetricType == core.MetricCumulative {
   262  		point.Start = creationTime
   263  	}
   264  	var value interface{}
   265  	if metricVal.ValueType == core.ValueInt64 {
   266  		value = metricVal.IntValue
   267  	} else if metricVal.ValueType == core.ValueFloat {
   268  		value = metricVal.FloatValue
   269  	} else {
   270  		return
   271  	}
   272  	point.Value = value
   273  	if labels != nil {
   274  		point.Labels = make(map[string]string)
   275  		for key, value := range labels {
   276  			point.Labels[key] = value
   277  		}
   278  	}
   279  	points = append(points, point)
   280  	ts.Metrics[metricName] = points
   281  }