github.com/galamsiva2020/kubernetes-heapster-monitoring@v0.0.0-20210823134957-3c1baa7c1e70/metrics/sinks/hawkular/client.go (about) 1 // Copyright 2016 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 hawkular 16 17 import ( 18 "fmt" 19 "hash/fnv" 20 "math" 21 "regexp" 22 "strings" 23 "sync" 24 "time" 25 26 "github.com/golang/glog" 27 "github.com/hawkular/hawkular-client-go/metrics" 28 "k8s.io/heapster/metrics/core" 29 ) 30 31 // cacheDefinitions Fetches all known definitions from all tenants (all projects in Openshift) 32 func (h *hawkularSink) cacheDefinitions() error { 33 if !h.disablePreCaching { 34 mds, err := h.client.AllDefinitions(h.modifiers...) 35 if err != nil { 36 return err 37 } 38 err = h.updateDefinitions(mds) 39 if err != nil { 40 return err 41 } 42 } 43 44 glog.V(4).Infof("Hawkular definition pre-caching completed, cached %d definitions\n", len(h.expReg)) 45 46 return nil 47 } 48 49 // cache inserts the item to the cache 50 func (h *hawkularSink) cache(md *metrics.MetricDefinition) { 51 h.pushToCache(md.ID, hashDefinition(md)) 52 } 53 54 // toCache inserts the item and updates the TTL in the cache to current time 55 func (h *hawkularSink) pushToCache(key string, hash uint64) { 56 h.regLock.Lock() 57 h.expReg[key] = &expiringItem{ 58 hash: hash, 59 ttl: h.runId, 60 } 61 h.regLock.Unlock() 62 } 63 64 // checkCache returns false if the cached instance is not current. Updates the TTL in the cache 65 func (h *hawkularSink) checkCache(key string, hash uint64) bool { 66 h.regLock.Lock() 67 defer h.regLock.Unlock() 68 _, found := h.expReg[key] 69 if !found || h.expReg[key].hash != hash { 70 return false 71 } 72 // Update the TTL 73 h.expReg[key].ttl = h.runId 74 return true 75 } 76 77 // expireCache will process the map and check for any item that has been expired and release it 78 func (h *hawkularSink) expireCache(runId uint64) { 79 h.regLock.Lock() 80 defer h.regLock.Unlock() 81 82 for k, v := range h.expReg { 83 if (v.ttl + h.cacheAge) <= runId { 84 delete(h.expReg, k) 85 } 86 } 87 } 88 89 // Fetches definitions from the server and checks that they're matching the descriptors 90 func (h *hawkularSink) updateDefinitions(mds []*metrics.MetricDefinition) error { 91 for _, p := range mds { 92 if model, f := h.models[p.Tags[descriptorTag]]; f && !h.recent(p, model) { 93 if err := h.client.UpdateTags(p.Type, p.ID, p.Tags, h.modifiers...); err != nil { 94 return err 95 } 96 } 97 h.cache(p) 98 } 99 100 return nil 101 } 102 103 func hashDefinition(md *metrics.MetricDefinition) uint64 { 104 h := fnv.New64a() 105 106 h.Write([]byte(md.Type)) 107 h.Write([]byte(md.ID)) 108 109 helper := fnv.New64a() 110 111 var hashCode uint64 112 113 for k, v := range md.Tags { 114 helper.Reset() 115 helper.Write([]byte(k)) 116 helper.Write([]byte(v)) 117 vH := helper.Sum64() 118 hashCode = hashCode ^ vH 119 } 120 121 return hashCode 122 } 123 124 // Checks that stored definition is up to date with the model 125 func (h *hawkularSink) recent(live *metrics.MetricDefinition, model *metrics.MetricDefinition) bool { 126 recent := true 127 for k := range model.Tags { 128 if v, found := live.Tags[k]; !found { 129 // There's a label that wasn't in our stored definition 130 live.Tags[k] = v 131 recent = false 132 } 133 } 134 135 return recent 136 } 137 138 // Transform the MetricDescriptor to a format used by Hawkular-Metrics 139 func (h *hawkularSink) descriptorToDefinition(md *core.MetricDescriptor) metrics.MetricDefinition { 140 tags := make(map[string]string) 141 // Postfix description tags with _description 142 for _, l := range md.Labels { 143 if len(l.Description) > 0 { 144 tags[l.Key+descriptionTag] = l.Description 145 } 146 } 147 148 if len(md.Units.String()) > 0 { 149 tags[unitsTag] = md.Units.String() 150 } 151 152 tags[descriptorTag] = md.Name 153 154 hmd := metrics.MetricDefinition{ 155 ID: md.Name, 156 Tags: tags, 157 Type: heapsterTypeToHawkularType(md.Type), 158 } 159 160 return hmd 161 } 162 163 func (h *hawkularSink) groupName(ms *core.MetricSet, metricName string) string { 164 n := []string{ms.Labels[core.LabelContainerName.Key], metricName} 165 return strings.Join(n, separator) 166 } 167 168 func (h *hawkularSink) idName(ms *core.MetricSet, metricName string) string { 169 n := make([]string, 0, 3) 170 171 metricType := ms.Labels[core.LabelMetricSetType.Key] 172 switch metricType { 173 case core.MetricSetTypeNode: 174 n = append(n, "machine") 175 n = append(n, h.nodeName(ms)) 176 case core.MetricSetTypeSystemContainer: 177 n = append(n, core.MetricSetTypeSystemContainer) 178 n = append(n, ms.Labels[core.LabelContainerName.Key]) 179 n = append(n, ms.Labels[core.LabelPodId.Key]) 180 case core.MetricSetTypeCluster: 181 n = append(n, core.MetricSetTypeCluster) 182 case core.MetricSetTypeNamespace: 183 n = append(n, core.MetricSetTypeNamespace) 184 n = append(n, ms.Labels[core.LabelNamespaceName.Key]) 185 case core.MetricSetTypePod: 186 n = append(n, core.MetricSetTypePod) 187 n = append(n, ms.Labels[core.LabelPodId.Key]) 188 case core.MetricSetTypePodContainer: 189 n = append(n, ms.Labels[core.LabelContainerName.Key]) 190 n = append(n, ms.Labels[core.LabelPodId.Key]) 191 default: 192 n = append(n, ms.Labels[core.LabelContainerName.Key]) 193 if ms.Labels[core.LabelPodId.Key] != "" { 194 n = append(n, ms.Labels[core.LabelPodId.Key]) 195 } else { 196 n = append(n, h.nodeName(ms)) 197 } 198 } 199 200 n = append(n, metricName) 201 202 return strings.Join(n, separator) 203 } 204 205 func (h *hawkularSink) nodeName(ms *core.MetricSet) string { 206 if len(h.labelNodeId) > 0 { 207 if v, found := ms.Labels[h.labelNodeId]; found { 208 return v 209 } 210 glog.V(4).Infof("The labelNodeId was set to %s but there is no label with this value."+ 211 "Using the default 'nodename' label instead.", h.labelNodeId) 212 } 213 214 return ms.Labels[core.LabelNodename.Key] 215 } 216 217 func (h *hawkularSink) createDefinitionFromModel(ms *core.MetricSet, metric core.LabeledMetric) (*metrics.MetricDefinition, uint64) { 218 if md, f := h.models[metric.Name]; f { 219 hasher := fnv.New64a() 220 221 hasher.Write([]byte(md.Type)) 222 hasher.Write([]byte(md.ID)) 223 224 helper := fnv.New64a() 225 226 var hashCode uint64 227 228 helperFunc := func(k string, v string, hashCode uint64) uint64 { 229 helper.Reset() 230 helper.Write([]byte(k)) 231 helper.Write([]byte(v)) 232 vH := helper.Sum64() 233 hashCode = hashCode ^ vH 234 235 return hashCode 236 } 237 238 // Copy the original map 239 mdd := *md 240 tags := make(map[string]string, len(mdd.Tags)+len(ms.Labels)+len(metric.Labels)+2+8) // 8 is just arbitrary extra for potential splits 241 for k, v := range mdd.Tags { 242 tags[k] = v 243 hashCode = helperFunc(k, v, hashCode) 244 } 245 mdd.Tags = tags 246 247 // Set tag values 248 for k, v := range ms.Labels { 249 mdd.Tags[k] = v 250 if k == core.LabelLabels.Key { 251 labels := strings.Split(v, ",") 252 for _, label := range labels { 253 labelKeyValue := strings.Split(label, ":") 254 if len(labelKeyValue) != 2 { 255 glog.V(4).Infof("Could not split the label %v into its key and value pair. This label will not be added as a tag in Hawkular Metrics.", label) 256 } else { 257 labelKey := h.labelTagPrefix + labelKeyValue[0] 258 mdd.Tags[labelKey] = labelKeyValue[1] 259 hashCode = helperFunc(labelKey, labelKeyValue[1], hashCode) 260 } 261 } 262 } 263 } 264 265 // Set the labeled values 266 for k, v := range metric.Labels { 267 mdd.Tags[k] = v 268 hashCode = helperFunc(k, v, hashCode) 269 } 270 271 groupName := h.groupName(ms, metric.Name) 272 mdd.Tags[groupTag] = groupName 273 mdd.Tags[descriptorTag] = metric.Name 274 275 hashCode = helperFunc(groupTag, groupName, hashCode) 276 hashCode = helperFunc(descriptorTag, metric.Name, hashCode) 277 278 return &mdd, hashCode 279 } 280 return nil, 0 281 // return nil, fmt.Errorf("Could not find definition model with name %s", metric.Name) 282 } 283 284 func (h *hawkularSink) registerLabeledIfNecessaryInline(ms *core.MetricSet, metric core.LabeledMetric, wg *sync.WaitGroup, m ...metrics.Modifier) error { 285 var key string 286 if resourceID, found := metric.Labels[core.LabelResourceID.Key]; found { 287 key = h.idName(ms, metric.Name+separator+resourceID) 288 } else { 289 key = h.idName(ms, metric.Name) 290 } 291 292 mdd, mddHash := h.createDefinitionFromModel(ms, metric) 293 if mddHash != 0 && !h.checkCache(key, mddHash) { 294 295 wg.Add(1) 296 go func(ms *core.MetricSet, labeledMetric core.LabeledMetric, m ...metrics.Modifier) { 297 defer wg.Done() 298 m = append(m, h.modifiers...) 299 // Create metric, use updateTags instead of Create because we don't care about uniqueness 300 if err := h.client.UpdateTags(heapsterTypeToHawkularType(metric.MetricType), key, mdd.Tags, m...); err != nil { 301 // Log error and don't add this key to the lookup table 302 glog.Errorf("Could not update tags: %s", err) 303 return 304 // return err 305 } 306 h.pushToCache(key, mddHash) 307 }(ms, metric, m...) 308 } 309 return nil 310 } 311 312 func toBatches(m []metrics.MetricHeader, batchSize int) chan []metrics.MetricHeader { 313 if batchSize == 0 { 314 c := make(chan []metrics.MetricHeader, 1) 315 c <- m 316 return c 317 } 318 319 size := int(math.Ceil(float64(len(m)) / float64(batchSize))) 320 c := make(chan []metrics.MetricHeader, size) 321 322 for i := 0; i < len(m); i += batchSize { 323 n := i + batchSize 324 if len(m) < n { 325 n = len(m) 326 } 327 part := m[i:n] 328 c <- part 329 } 330 331 return c 332 } 333 334 func (h *hawkularSink) sendData(tmhs map[string][]metrics.MetricHeader, wg *sync.WaitGroup) { 335 for k, v := range tmhs { 336 parts := toBatches(v, h.batchSize) 337 close(parts) 338 339 for p := range parts { 340 wg.Add(1) 341 go func(batch []metrics.MetricHeader, tenant string) { 342 defer wg.Done() 343 344 m := make([]metrics.Modifier, len(h.modifiers), len(h.modifiers)+1) 345 copy(m, h.modifiers) 346 m = append(m, metrics.Tenant(tenant)) 347 if err := h.client.Write(batch, m...); err != nil { 348 glog.Errorf(err.Error()) 349 } 350 }(p, k) 351 } 352 } 353 } 354 355 // Converts Timeseries to metric structure used by the Hawkular 356 func (h *hawkularSink) pointToLabeledMetricHeader(ms *core.MetricSet, metric core.LabeledMetric, timestamp time.Time) (*metrics.MetricHeader, error) { 357 358 name := h.idName(ms, metric.Name) 359 if resourceID, found := metric.Labels[core.LabelResourceID.Key]; found { 360 name = h.idName(ms, metric.Name+separator+resourceID) 361 } 362 363 var value float64 364 if metric.ValueType == core.ValueInt64 { 365 value = float64(metric.IntValue) 366 } else { 367 value = float64(metric.FloatValue) 368 } 369 370 m := metrics.Datapoint{ 371 Value: value, 372 Timestamp: timestamp, 373 } 374 375 mh := &metrics.MetricHeader{ 376 ID: name, 377 Data: []metrics.Datapoint{m}, 378 Type: heapsterTypeToHawkularType(metric.MetricType), 379 } 380 381 return mh, nil 382 } 383 384 // If Heapster gets filters, remove these.. 385 func parseFilters(v []string) ([]Filter, error) { 386 fs := make([]Filter, 0, len(v)) 387 for _, s := range v { 388 p := strings.Index(s, "(") 389 if p < 0 { 390 return nil, fmt.Errorf("Incorrect syntax in filter parameters, missing (") 391 } 392 393 if strings.Index(s, ")") != len(s)-1 { 394 return nil, fmt.Errorf("Incorrect syntax in filter parameters, missing )") 395 } 396 397 t := Unknown.From(s[:p]) 398 if t == Unknown { 399 return nil, fmt.Errorf("Unknown filter type") 400 } 401 402 command := s[p+1 : len(s)-1] 403 404 switch t { 405 case Label: 406 proto := strings.SplitN(command, ":", 2) 407 if len(proto) < 2 { 408 return nil, fmt.Errorf("Missing : from label filter") 409 } 410 r, err := regexp.Compile(proto[1]) 411 if err != nil { 412 return nil, err 413 } 414 fs = append(fs, labelFilter(proto[0], r)) 415 break 416 case Name: 417 r, err := regexp.Compile(command) 418 if err != nil { 419 return nil, err 420 } 421 fs = append(fs, nameFilter(r)) 422 break 423 } 424 } 425 return fs, nil 426 } 427 428 func labelFilter(label string, r *regexp.Regexp) Filter { 429 return func(ms *core.MetricSet, metricName string) bool { 430 for k, v := range ms.Labels { 431 if k == label { 432 if r.MatchString(v) { 433 return false 434 } 435 } 436 } 437 return true 438 } 439 } 440 441 func nameFilter(r *regexp.Regexp) Filter { 442 return func(ms *core.MetricSet, metricName string) bool { 443 return !r.MatchString(metricName) 444 } 445 }