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 }