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  }