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 }