github.com/galamsiva2020/kubernetes-heapster-monitoring@v0.0.0-20210823134957-3c1baa7c1e70/metrics/sinks/gcm/gcm.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 gcm 16 17 import ( 18 "fmt" 19 "net/url" 20 "sync" 21 "time" 22 23 gce_util "k8s.io/heapster/common/gce" 24 "k8s.io/heapster/metrics/core" 25 26 "github.com/golang/glog" 27 "golang.org/x/oauth2" 28 "golang.org/x/oauth2/google" 29 gcm "google.golang.org/api/monitoring/v3" 30 ) 31 32 const ( 33 metricDomain = "kubernetes.io" 34 customApiPrefix = "custom.googleapis.com" 35 maxNumLabels = 10 36 // The largest number of timeseries we can write to per request. 37 maxTimeseriesPerRequest = 200 38 ) 39 40 type MetricFilter int8 41 42 const ( 43 metricsAll MetricFilter = iota 44 metricsOnlyAutoscaling 45 ) 46 47 type gcmSink struct { 48 sync.RWMutex 49 registered bool 50 project string 51 metricFilter MetricFilter 52 gcmService *gcm.Service 53 } 54 55 func (sink *gcmSink) Name() string { 56 return "GCM Sink" 57 } 58 59 func getReq() *gcm.CreateTimeSeriesRequest { 60 return &gcm.CreateTimeSeriesRequest{TimeSeries: make([]*gcm.TimeSeries, 0)} 61 } 62 63 func fullMetricName(project string, name string) string { 64 return fmt.Sprintf("projects/%s/metricDescriptors/%s/%s/%s", project, customApiPrefix, metricDomain, name) 65 } 66 67 func fullMetricType(name string) string { 68 return fmt.Sprintf("%s/%s/%s", customApiPrefix, metricDomain, name) 69 } 70 71 func createTimeSeries(timestamp time.Time, labels map[string]string, metric string, val core.MetricValue, collectionStartTime time.Time) *gcm.TimeSeries { 72 point := &gcm.Point{ 73 Interval: &gcm.TimeInterval{ 74 StartTime: timestamp.Format(time.RFC3339), 75 EndTime: timestamp.Format(time.RFC3339), 76 }, 77 Value: &gcm.TypedValue{}, 78 } 79 80 var valueType string 81 82 switch val.ValueType { 83 case core.ValueInt64: 84 point.Value.Int64Value = &val.IntValue 85 point.Value.ForceSendFields = []string{"Int64Value"} 86 valueType = "INT64" 87 case core.ValueFloat: 88 v := float64(val.FloatValue) 89 point.Value.DoubleValue = &v 90 point.Value.ForceSendFields = []string{"DoubleValue"} 91 valueType = "DOUBLE" 92 default: 93 glog.Errorf("Type not supported %v in %v", val.ValueType, metric) 94 return nil 95 } 96 // For cumulative metric use the provided start time. 97 if val.MetricType == core.MetricCumulative { 98 point.Interval.StartTime = collectionStartTime.Format(time.RFC3339) 99 } 100 101 return &gcm.TimeSeries{ 102 Points: []*gcm.Point{point}, 103 Metric: &gcm.Metric{ 104 Type: fullMetricType(metric), 105 Labels: labels, 106 }, 107 ValueType: valueType, 108 } 109 } 110 111 func (sink *gcmSink) getTimeSeries(timestamp time.Time, labels map[string]string, metric string, val core.MetricValue, collectionStartTime time.Time) *gcm.TimeSeries { 112 finalLabels := make(map[string]string) 113 if core.IsNodeAutoscalingMetric(metric) { 114 // All and autoscaling. Do not populate for other filters. 115 if sink.metricFilter != metricsAll && 116 sink.metricFilter != metricsOnlyAutoscaling { 117 return nil 118 } 119 120 finalLabels[core.LabelHostname.Key] = labels[core.LabelHostname.Key] 121 finalLabels[core.LabelGCEResourceID.Key] = labels[core.LabelHostID.Key] 122 finalLabels[core.LabelGCEResourceType.Key] = "instance" 123 } else { 124 // Only all. 125 if sink.metricFilter != metricsAll { 126 return nil 127 } 128 supportedLables := core.GcmLabels() 129 for key, value := range labels { 130 if _, ok := supportedLables[key]; ok { 131 finalLabels[key] = value 132 } 133 } 134 } 135 136 return createTimeSeries(timestamp, finalLabels, metric, val, collectionStartTime) 137 } 138 139 func (sink *gcmSink) getTimeSeriesForLabeledMetrics(timestamp time.Time, labels map[string]string, metric core.LabeledMetric, collectionStartTime time.Time) *gcm.TimeSeries { 140 // Only all. There are no autoscaling labeled metrics. 141 if sink.metricFilter != metricsAll { 142 return nil 143 } 144 145 finalLabels := make(map[string]string) 146 supportedLables := core.GcmLabels() 147 for key, value := range labels { 148 if _, ok := supportedLables[key]; ok { 149 finalLabels[key] = value 150 } 151 } 152 for key, value := range metric.Labels { 153 if _, ok := supportedLables[key]; ok { 154 finalLabels[key] = value 155 } 156 } 157 158 return createTimeSeries(timestamp, finalLabels, metric.Name, metric.MetricValue, collectionStartTime) 159 } 160 161 func fullProjectName(name string) string { 162 return fmt.Sprintf("projects/%s", name) 163 } 164 165 func (sink *gcmSink) sendRequest(req *gcm.CreateTimeSeriesRequest) { 166 _, err := sink.gcmService.Projects.TimeSeries.Create(fullProjectName(sink.project), req).Do() 167 if err != nil { 168 glog.Errorf("Error while sending request to GCM %v", err) 169 } else { 170 glog.V(4).Infof("Successfully sent %v timeserieses to GCM", len(req.TimeSeries)) 171 } 172 } 173 174 func (sink *gcmSink) ExportData(dataBatch *core.DataBatch) { 175 if err := sink.registerAllMetrics(); err != nil { 176 glog.Warningf("Error during metrics registration: %v", err) 177 return 178 } 179 180 req := getReq() 181 for _, metricSet := range dataBatch.MetricSets { 182 for metric, val := range metricSet.MetricValues { 183 point := sink.getTimeSeries(dataBatch.Timestamp, metricSet.Labels, metric, val, metricSet.CollectionStartTime) 184 if point != nil { 185 req.TimeSeries = append(req.TimeSeries, point) 186 } 187 if len(req.TimeSeries) >= maxTimeseriesPerRequest { 188 sink.sendRequest(req) 189 req = getReq() 190 } 191 } 192 for _, metric := range metricSet.LabeledMetrics { 193 point := sink.getTimeSeriesForLabeledMetrics(dataBatch.Timestamp, metricSet.Labels, metric, metricSet.CollectionStartTime) 194 if point != nil { 195 req.TimeSeries = append(req.TimeSeries, point) 196 } 197 if len(req.TimeSeries) >= maxTimeseriesPerRequest { 198 sink.sendRequest(req) 199 req = getReq() 200 } 201 } 202 } 203 if len(req.TimeSeries) > 0 { 204 sink.sendRequest(req) 205 } 206 } 207 208 func (sink *gcmSink) Stop() { 209 // nothing needs to be done. 210 } 211 212 func (sink *gcmSink) registerAllMetrics() error { 213 return sink.register(core.AllMetrics) 214 } 215 216 // Adds the specified metrics or updates them if they already exist. 217 func (sink *gcmSink) register(metrics []core.Metric) error { 218 sink.Lock() 219 defer sink.Unlock() 220 if sink.registered { 221 return nil 222 } 223 224 for _, metric := range metrics { 225 metricName := fullMetricName(sink.project, metric.MetricDescriptor.Name) 226 metricType := fullMetricType(metric.MetricDescriptor.Name) 227 228 if _, err := sink.gcmService.Projects.MetricDescriptors.Delete(metricName).Do(); err != nil { 229 glog.Infof("[GCM] Deleting metric %v failed: %v", metricName, err) 230 } 231 labels := make([]*gcm.LabelDescriptor, 0) 232 233 // Node autoscaling metrics have special labels. 234 if core.IsNodeAutoscalingMetric(metric.MetricDescriptor.Name) { 235 // All and autoscaling. Do not populate for other filters. 236 if sink.metricFilter != metricsAll && 237 sink.metricFilter != metricsOnlyAutoscaling { 238 continue 239 } 240 241 for _, l := range core.GcmNodeAutoscalingLabels() { 242 labels = append(labels, &gcm.LabelDescriptor{ 243 Key: l.Key, 244 Description: l.Description, 245 }) 246 } 247 } else { 248 // Only all. 249 if sink.metricFilter != metricsAll { 250 continue 251 } 252 253 for _, l := range core.GcmLabels() { 254 labels = append(labels, &gcm.LabelDescriptor{ 255 Key: l.Key, 256 Description: l.Description, 257 }) 258 } 259 } 260 261 var metricKind string 262 263 switch metric.MetricDescriptor.Type { 264 case core.MetricCumulative: 265 metricKind = "CUMULATIVE" 266 case core.MetricGauge: 267 metricKind = "GAUGE" 268 case core.MetricDelta: 269 metricKind = "DELTA" 270 } 271 272 var valueType string 273 274 switch metric.MetricDescriptor.ValueType { 275 case core.ValueInt64: 276 valueType = "INT64" 277 case core.ValueFloat: 278 valueType = "DOUBLE" 279 } 280 281 desc := &gcm.MetricDescriptor{ 282 Name: metricName, 283 Description: metric.MetricDescriptor.Description, 284 Labels: labels, 285 MetricKind: metricKind, 286 ValueType: valueType, 287 Type: metricType, 288 } 289 290 if _, err := sink.gcmService.Projects.MetricDescriptors.Create(fullProjectName(sink.project), desc).Do(); err != nil { 291 glog.Errorf("Metric registration of %v failed: %v", desc.Name, err) 292 return err 293 } 294 } 295 sink.registered = true 296 return nil 297 } 298 299 func CreateGCMSink(uri *url.URL) (core.DataSink, error) { 300 if len(uri.Scheme) > 0 { 301 return nil, fmt.Errorf("scheme should not be set for GCM sink") 302 } 303 if len(uri.Host) > 0 { 304 return nil, fmt.Errorf("host should not be set for GCM sink") 305 } 306 307 opts, err := url.ParseQuery(uri.RawQuery) 308 if err != nil { 309 return nil, err 310 } 311 312 metrics := "all" 313 if len(opts["metrics"]) > 0 { 314 metrics = opts["metrics"][0] 315 } 316 var metricFilter MetricFilter = metricsAll 317 switch metrics { 318 case "all": 319 metricFilter = metricsAll 320 case "autoscaling": 321 metricFilter = metricsOnlyAutoscaling 322 default: 323 return nil, fmt.Errorf("invalid metrics parameter: %s", metrics) 324 } 325 326 client, err := google.DefaultClient(oauth2.NoContext, gcm.MonitoringScope) 327 if err != nil { 328 return nil, fmt.Errorf("error creating oauth2 client: %v", err) 329 } 330 331 // Create Google Cloud Monitoring service. 332 gcmService, err := gcm.New(client) 333 if err != nil { 334 return nil, fmt.Errorf("error creating GCM service: %v", err) 335 } 336 337 // Get the GCP Project ID. 338 projectId, err := gce_util.GetProjectId() 339 if err != nil { 340 return nil, fmt.Errorf("error getting GCP project ID: %v", err) 341 } 342 343 sink := &gcmSink{ 344 registered: false, 345 project: projectId, 346 gcmService: gcmService, 347 metricFilter: metricFilter, 348 } 349 glog.Infof("created GCM sink") 350 if err := sink.registerAllMetrics(); err != nil { 351 glog.Warningf("Error during metrics registration: %v", err) 352 } 353 return sink, nil 354 }