github.com/jonaz/heapster@v1.3.0-beta.0.0.20170208112634-cd3c15ca3d29/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  	"math"
    20  	"regexp"
    21  	"strings"
    22  	"sync"
    23  	"time"
    24  
    25  	"github.com/golang/glog"
    26  	"github.com/hawkular/hawkular-client-go/metrics"
    27  	"k8s.io/heapster/metrics/core"
    28  )
    29  
    30  // Fetches definitions from the server and checks that they're matching the descriptors
    31  func (h *hawkularSink) updateDefinitions(mt metrics.MetricType) error {
    32  	m := make([]metrics.Modifier, len(h.modifiers), len(h.modifiers)+1)
    33  	copy(m, h.modifiers)
    34  	m = append(m, metrics.Filters(metrics.TypeFilter(mt)))
    35  
    36  	mds, err := h.client.Definitions(m...)
    37  	if err != nil {
    38  		return err
    39  	}
    40  
    41  	h.regLock.Lock()
    42  	defer h.regLock.Unlock()
    43  
    44  	for _, p := range mds {
    45  		// If no descriptorTag is found, this metric does not belong to Heapster
    46  		if mk, found := p.Tags[descriptorTag]; found {
    47  			if model, f := h.models[mk]; f && !h.recent(p, model) {
    48  				if err := h.client.UpdateTags(mt, p.Id, p.Tags, h.modifiers...); err != nil {
    49  					return err
    50  				}
    51  			}
    52  			h.reg[p.Id] = p
    53  		}
    54  	}
    55  	return nil
    56  }
    57  
    58  // Checks that stored definition is up to date with the model
    59  func (h *hawkularSink) recent(live *metrics.MetricDefinition, model *metrics.MetricDefinition) bool {
    60  	recent := true
    61  	for k := range model.Tags {
    62  		if v, found := live.Tags[k]; !found {
    63  			// There's a label that wasn't in our stored definition
    64  			live.Tags[k] = v
    65  			recent = false
    66  		}
    67  	}
    68  
    69  	return recent
    70  }
    71  
    72  // Transform the MetricDescriptor to a format used by Hawkular-Metrics
    73  func (h *hawkularSink) descriptorToDefinition(md *core.MetricDescriptor) metrics.MetricDefinition {
    74  	tags := make(map[string]string)
    75  	// Postfix description tags with _description
    76  	for _, l := range md.Labels {
    77  		if len(l.Description) > 0 {
    78  			tags[l.Key+descriptionTag] = l.Description
    79  		}
    80  	}
    81  
    82  	if len(md.Units.String()) > 0 {
    83  		tags[unitsTag] = md.Units.String()
    84  	}
    85  
    86  	tags[descriptorTag] = md.Name
    87  
    88  	hmd := metrics.MetricDefinition{
    89  		Id:   md.Name,
    90  		Tags: tags,
    91  		Type: heapsterTypeToHawkularType(md.Type),
    92  	}
    93  
    94  	return hmd
    95  }
    96  
    97  func (h *hawkularSink) groupName(ms *core.MetricSet, metricName string) string {
    98  	n := []string{ms.Labels[core.LabelContainerName.Key], metricName}
    99  	return strings.Join(n, separator)
   100  }
   101  
   102  func (h *hawkularSink) idName(ms *core.MetricSet, metricName string) string {
   103  	n := make([]string, 0, 3)
   104  
   105  	metricType := ms.Labels[core.LabelMetricSetType.Key]
   106  	switch metricType {
   107  	case core.MetricSetTypeNode:
   108  		n = append(n, "machine")
   109  		n = append(n, h.nodeName(ms))
   110  	case core.MetricSetTypeSystemContainer:
   111  		n = append(n, core.MetricSetTypeSystemContainer)
   112  		n = append(n, ms.Labels[core.LabelContainerName.Key])
   113  		n = append(n, ms.Labels[core.LabelPodId.Key])
   114  	case core.MetricSetTypeCluster:
   115  		n = append(n, core.MetricSetTypeCluster)
   116  	case core.MetricSetTypeNamespace:
   117  		n = append(n, core.MetricSetTypeNamespace)
   118  		n = append(n, ms.Labels[core.LabelNamespaceName.Key])
   119  	case core.MetricSetTypePod:
   120  		n = append(n, core.MetricSetTypePod)
   121  		n = append(n, ms.Labels[core.LabelPodId.Key])
   122  	case core.MetricSetTypePodContainer:
   123  		n = append(n, ms.Labels[core.LabelContainerName.Key])
   124  		n = append(n, ms.Labels[core.LabelPodId.Key])
   125  	default:
   126  		n = append(n, ms.Labels[core.LabelContainerName.Key])
   127  		if ms.Labels[core.LabelPodId.Key] != "" {
   128  			n = append(n, ms.Labels[core.LabelPodId.Key])
   129  		} else {
   130  			n = append(n, h.nodeName(ms))
   131  		}
   132  	}
   133  
   134  	n = append(n, metricName)
   135  
   136  	return strings.Join(n, separator)
   137  }
   138  
   139  func (h *hawkularSink) nodeName(ms *core.MetricSet) string {
   140  	if len(h.labelNodeId) > 0 {
   141  		if v, found := ms.Labels[h.labelNodeId]; found {
   142  			return v
   143  		}
   144  		glog.V(4).Infof("The labelNodeId was set to %s but there is no label with this value."+
   145  			"Using the default 'nodename' label instead.", h.labelNodeId)
   146  	}
   147  
   148  	return ms.Labels[core.LabelNodename.Key]
   149  }
   150  
   151  func (h *hawkularSink) registerLabeledIfNecessary(ms *core.MetricSet, metric core.LabeledMetric, m ...metrics.Modifier) error {
   152  	key := h.idName(ms, metric.Name)
   153  
   154  	if resourceID, found := metric.Labels[core.LabelResourceID.Key]; found {
   155  		key = h.idName(ms, metric.Name+separator+resourceID)
   156  	}
   157  
   158  	h.regLock.Lock()
   159  	defer h.regLock.Unlock()
   160  
   161  	// If found, check it matches the current stored definition (could be old info from
   162  	// the stored metrics cache for example)
   163  	if _, found := h.reg[key]; !found {
   164  		// Register the metric descriptor here..
   165  		if md, f := h.models[metric.Name]; f {
   166  			// Copy the original map
   167  			mdd := *md
   168  			tags := make(map[string]string)
   169  			for k, v := range mdd.Tags {
   170  				tags[k] = v
   171  			}
   172  			mdd.Tags = tags
   173  
   174  			// Set tag values
   175  			for k, v := range ms.Labels {
   176  				mdd.Tags[k] = v
   177  			}
   178  
   179  			// Set the labeled values
   180  			for k, v := range metric.Labels {
   181  				mdd.Tags[k] = v
   182  			}
   183  
   184  			mdd.Tags[groupTag] = h.groupName(ms, metric.Name)
   185  			mdd.Tags[descriptorTag] = metric.Name
   186  
   187  			m = append(m, h.modifiers...)
   188  
   189  			// Create metric, use updateTags instead of Create because we know it is unique
   190  			if err := h.client.UpdateTags(heapsterTypeToHawkularType(metric.MetricType), key, mdd.Tags, m...); err != nil {
   191  				// Log error and don't add this key to the lookup table
   192  				glog.Errorf("Could not update tags: %s", err)
   193  				return err
   194  			}
   195  
   196  			// Add to the lookup table
   197  			h.reg[key] = &mdd
   198  		} else {
   199  			return fmt.Errorf("Could not find definition model with name %s", metric.Name)
   200  		}
   201  	}
   202  	// TODO Compare the definition tags and update if necessary? Quite expensive operation..
   203  
   204  	return nil
   205  }
   206  
   207  func toBatches(m []metrics.MetricHeader, batchSize int) chan []metrics.MetricHeader {
   208  	if batchSize == 0 {
   209  		c := make(chan []metrics.MetricHeader, 1)
   210  		c <- m
   211  		return c
   212  	}
   213  
   214  	size := int(math.Ceil(float64(len(m)) / float64(batchSize)))
   215  	c := make(chan []metrics.MetricHeader, size)
   216  
   217  	for i := 0; i < len(m); i += batchSize {
   218  		n := i + batchSize
   219  		if len(m) < n {
   220  			n = len(m)
   221  		}
   222  		part := m[i:n]
   223  		c <- part
   224  	}
   225  
   226  	return c
   227  }
   228  
   229  func (h *hawkularSink) sendData(tmhs map[string][]metrics.MetricHeader, wg *sync.WaitGroup) {
   230  	for k, v := range tmhs {
   231  		parts := toBatches(v, h.batchSize)
   232  		close(parts)
   233  
   234  		for p := range parts {
   235  			wg.Add(1)
   236  			go func(batch []metrics.MetricHeader, tenant string) {
   237  				defer wg.Done()
   238  
   239  				m := make([]metrics.Modifier, len(h.modifiers), len(h.modifiers)+1)
   240  				copy(m, h.modifiers)
   241  				m = append(m, metrics.Tenant(tenant))
   242  				if err := h.client.Write(batch, m...); err != nil {
   243  					glog.Errorf(err.Error())
   244  				}
   245  			}(p, k)
   246  		}
   247  	}
   248  }
   249  
   250  // Converts Timeseries to metric structure used by the Hawkular
   251  func (h *hawkularSink) pointToLabeledMetricHeader(ms *core.MetricSet, metric core.LabeledMetric, timestamp time.Time) (*metrics.MetricHeader, error) {
   252  
   253  	name := h.idName(ms, metric.Name)
   254  	if resourceID, found := metric.Labels[core.LabelResourceID.Key]; found {
   255  		name = h.idName(ms, metric.Name+separator+resourceID)
   256  	}
   257  
   258  	var value float64
   259  	if metric.ValueType == core.ValueInt64 {
   260  		value = float64(metric.IntValue)
   261  	} else {
   262  		value = float64(metric.FloatValue)
   263  	}
   264  
   265  	m := metrics.Datapoint{
   266  		Value:     value,
   267  		Timestamp: metrics.UnixMilli(timestamp),
   268  	}
   269  
   270  	mh := &metrics.MetricHeader{
   271  		Id:   name,
   272  		Data: []metrics.Datapoint{m},
   273  		Type: heapsterTypeToHawkularType(metric.MetricType),
   274  	}
   275  
   276  	return mh, nil
   277  }
   278  
   279  // If Heapster gets filters, remove these..
   280  func parseFilters(v []string) ([]Filter, error) {
   281  	fs := make([]Filter, 0, len(v))
   282  	for _, s := range v {
   283  		p := strings.Index(s, "(")
   284  		if p < 0 {
   285  			return nil, fmt.Errorf("Incorrect syntax in filter parameters, missing (")
   286  		}
   287  
   288  		if strings.Index(s, ")") != len(s)-1 {
   289  			return nil, fmt.Errorf("Incorrect syntax in filter parameters, missing )")
   290  		}
   291  
   292  		t := Unknown.From(s[:p])
   293  		if t == Unknown {
   294  			return nil, fmt.Errorf("Unknown filter type")
   295  		}
   296  
   297  		command := s[p+1 : len(s)-1]
   298  
   299  		switch t {
   300  		case Label:
   301  			proto := strings.SplitN(command, ":", 2)
   302  			if len(proto) < 2 {
   303  				return nil, fmt.Errorf("Missing : from label filter")
   304  			}
   305  			r, err := regexp.Compile(proto[1])
   306  			if err != nil {
   307  				return nil, err
   308  			}
   309  			fs = append(fs, labelFilter(proto[0], r))
   310  			break
   311  		case Name:
   312  			r, err := regexp.Compile(command)
   313  			if err != nil {
   314  				return nil, err
   315  			}
   316  			fs = append(fs, nameFilter(r))
   317  			break
   318  		}
   319  	}
   320  	return fs, nil
   321  }
   322  
   323  func labelFilter(label string, r *regexp.Regexp) Filter {
   324  	return func(ms *core.MetricSet, metricName string) bool {
   325  		for k, v := range ms.Labels {
   326  			if k == label {
   327  				if r.MatchString(v) {
   328  					return false
   329  				}
   330  			}
   331  		}
   332  		return true
   333  	}
   334  }
   335  
   336  func nameFilter(r *regexp.Regexp) Filter {
   337  	return func(ms *core.MetricSet, metricName string) bool {
   338  		return !r.MatchString(metricName)
   339  	}
   340  }