github.com/timstclair/heapster@v0.20.0-alpha1/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  	restful "github.com/emicklei/go-restful"
    19  
    20  	"k8s.io/heapster/metrics/api/v1/types"
    21  	"k8s.io/heapster/metrics/core"
    22  	"k8s.io/heapster/metrics/sinks/metric"
    23  )
    24  
    25  type Api struct {
    26  	runningInKubernetes bool
    27  	metricSink          *metricsink.MetricSink
    28  	gkeMetrics          map[string]core.MetricDescriptor
    29  	gkeLabels           map[string]core.LabelDescriptor
    30  }
    31  
    32  // Create a new Api to serve from the specified cache.
    33  func NewApi(runningInKubernetes bool, metricSink *metricsink.MetricSink) *Api {
    34  	gkeMetrics := make(map[string]core.MetricDescriptor)
    35  	gkeLabels := make(map[string]core.LabelDescriptor)
    36  	for _, val := range core.StandardMetrics {
    37  		gkeMetrics[val.Name] = val.MetricDescriptor
    38  	}
    39  	for _, val := range core.CommonLabels() {
    40  		gkeLabels[val.Key] = val
    41  	}
    42  	for _, val := range core.ContainerLabels() {
    43  		gkeLabels[val.Key] = val
    44  	}
    45  	for _, val := range core.PodLabels() {
    46  		gkeLabels[val.Key] = val
    47  	}
    48  
    49  	return &Api{
    50  		runningInKubernetes: runningInKubernetes,
    51  		metricSink:          metricSink,
    52  		gkeMetrics:          gkeMetrics,
    53  		gkeLabels:           gkeLabels,
    54  	}
    55  }
    56  
    57  // Register the mainApi on the specified endpoint.
    58  func (a *Api) Register(container *restful.Container) {
    59  	ws := new(restful.WebService)
    60  	ws.Path("/api/v1/metric-export").
    61  		Doc("Exports the latest point for all Heapster metrics").
    62  		Produces(restful.MIME_JSON)
    63  	ws.Route(ws.GET("").
    64  		To(a.exportMetrics).
    65  		Doc("export the latest data point for all metrics").
    66  		Operation("exportMetrics").
    67  		Writes([]*types.Timeseries{}))
    68  	container.Add(ws)
    69  	ws = new(restful.WebService)
    70  	ws.Path("/api/v1/metric-export-schema").
    71  		Doc("Schema for metrics exported by heapster").
    72  		Produces(restful.MIME_JSON)
    73  	ws.Route(ws.GET("").
    74  		To(a.exportMetricsSchema).
    75  		Doc("export the schema for all metrics").
    76  		Operation("exportmetricsSchema").
    77  		Writes(types.TimeseriesSchema{}))
    78  	container.Add(ws)
    79  
    80  	if a.metricSink != nil {
    81  		a.RegisterModel(container)
    82  	}
    83  }
    84  
    85  func convertLabelDescriptor(ld core.LabelDescriptor) types.LabelDescriptor {
    86  	return types.LabelDescriptor{
    87  		Key:         ld.Key,
    88  		Description: ld.Description,
    89  	}
    90  }
    91  
    92  func convertMetricDescriptor(md core.MetricDescriptor) types.MetricDescriptor {
    93  	result := types.MetricDescriptor{
    94  		Name:        md.Name,
    95  		Description: md.Description,
    96  		Labels:      make([]types.LabelDescriptor, 0, len(md.Labels)),
    97  	}
    98  	for _, label := range md.Labels {
    99  		result.Labels = append(result.Labels, convertLabelDescriptor(label))
   100  	}
   101  
   102  	switch md.Type {
   103  	case core.MetricCumulative:
   104  		result.Type = "cumulative"
   105  	case core.MetricGauge:
   106  		result.Type = "gauge"
   107  	}
   108  
   109  	switch md.ValueType {
   110  	case core.ValueInt64:
   111  		result.ValueType = "int64"
   112  	case core.ValueFloat:
   113  		result.ValueType = "double"
   114  	}
   115  
   116  	switch md.Units {
   117  	case core.UnitsBytes:
   118  		result.Units = "bytes"
   119  	case core.UnitsMilliseconds:
   120  		result.Units = "ms"
   121  	case core.UnitsNanoseconds:
   122  		result.Units = "ns"
   123  	case core.UnitsMillicores:
   124  		result.Units = "millicores"
   125  	}
   126  	return result
   127  }
   128  
   129  func (a *Api) exportMetricsSchema(request *restful.Request, response *restful.Response) {
   130  	result := types.TimeseriesSchema{
   131  		Metrics:      make([]types.MetricDescriptor, 0),
   132  		CommonLabels: make([]types.LabelDescriptor, 0),
   133  		PodLabels:    make([]types.LabelDescriptor, 0),
   134  	}
   135  	for _, metric := range core.StandardMetrics {
   136  		if _, found := a.gkeMetrics[metric.Name]; found {
   137  			result.Metrics = append(result.Metrics, convertMetricDescriptor(metric.MetricDescriptor))
   138  		}
   139  	}
   140  	for _, label := range core.CommonLabels() {
   141  		if _, found := a.gkeLabels[label.Key]; found {
   142  			result.PodLabels = append(result.PodLabels, convertLabelDescriptor(label))
   143  		}
   144  	}
   145  	for _, label := range core.PodLabels() {
   146  		if _, found := a.gkeLabels[label.Key]; found {
   147  			result.PodLabels = append(result.PodLabels, convertLabelDescriptor(label))
   148  		}
   149  	}
   150  	response.WriteEntity(result)
   151  }
   152  
   153  func (a *Api) exportMetrics(request *restful.Request, response *restful.Response) {
   154  	shortStorage := a.metricSink.GetShortStore()
   155  	tsmap := make(map[string]*types.Timeseries)
   156  
   157  	for _, batch := range shortStorage {
   158  		for key, ms := range batch.MetricSets {
   159  			ts := tsmap[key]
   160  
   161  			msType := ms.Labels[core.LabelMetricSetType.Key]
   162  
   163  			if msType != core.MetricSetTypeNode &&
   164  				msType != core.MetricSetTypePod &&
   165  				msType != core.MetricSetTypePodContainer &&
   166  				msType != core.MetricSetTypeSystemContainer {
   167  				continue
   168  			}
   169  
   170  			if ts == nil {
   171  				ts = &types.Timeseries{
   172  					Metrics: make(map[string][]types.Point),
   173  					Labels:  make(map[string]string),
   174  				}
   175  				for labelName, labelValue := range ms.Labels {
   176  					if _, ok := a.gkeLabels[labelName]; ok {
   177  						ts.Labels[labelName] = labelValue
   178  					}
   179  				}
   180  				if msType == core.MetricSetTypeNode {
   181  					ts.Labels[core.LabelContainerName.Key] = "machine"
   182  				}
   183  				tsmap[key] = ts
   184  			}
   185  			for metricName, metricVal := range ms.MetricValues {
   186  				if _, ok := a.gkeMetrics[metricName]; ok {
   187  					points := ts.Metrics[metricName]
   188  					if points == nil {
   189  						points = make([]types.Point, 0, len(shortStorage))
   190  					}
   191  					point := types.Point{
   192  						Start: batch.Timestamp,
   193  						End:   batch.Timestamp,
   194  					}
   195  					if metricVal.ValueType == core.ValueInt64 {
   196  						point.Value = &metricVal.IntValue
   197  					} else if metricVal.ValueType == core.ValueFloat {
   198  						point.Value = &metricVal.FloatValue
   199  					} else {
   200  						continue
   201  					}
   202  					points = append(points, point)
   203  					ts.Metrics[metricName] = points
   204  				}
   205  			}
   206  		}
   207  	}
   208  	timeseries := make([]*types.Timeseries, 0, len(tsmap))
   209  	for _, ts := range tsmap {
   210  		timeseries = append(timeseries, ts)
   211  	}
   212  
   213  	response.WriteEntity(timeseries)
   214  }