github.com/jonaz/heapster@v1.3.0-beta.0.0.20170208112634-cd3c15ca3d29/metrics/sinks/hawkular/driver.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 hawkular
    16  
    17  import (
    18  	"crypto/tls"
    19  	"crypto/x509"
    20  	"fmt"
    21  	"io/ioutil"
    22  	"net/http"
    23  	"net/url"
    24  	"strconv"
    25  	"sync"
    26  
    27  	"github.com/golang/glog"
    28  	"github.com/hawkular/hawkular-client-go/metrics"
    29  
    30  	"k8s.io/heapster/metrics/core"
    31  	kube_client "k8s.io/kubernetes/pkg/client/restclient"
    32  	kubeClientCmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
    33  )
    34  
    35  const (
    36  	unitsTag           = "units"
    37  	descriptionTag     = "_description"
    38  	descriptorTag      = "descriptor_name"
    39  	groupTag           = "group_id"
    40  	separator          = "/"
    41  	batchSizeDefault   = 1000
    42  	concurrencyDefault = 5
    43  
    44  	nodeId string = "labelNodeId"
    45  
    46  	defaultServiceAccountFile = "/var/run/secrets/kubernetes.io/serviceaccount/token"
    47  )
    48  
    49  // START: ExternalSink interface implementations
    50  
    51  func (h *hawkularSink) Register(mds []core.MetricDescriptor) error {
    52  	// Create model definitions based on the MetricDescriptors
    53  	for _, md := range mds {
    54  		hmd := h.descriptorToDefinition(&md)
    55  		h.models[md.Name] = &hmd
    56  	}
    57  
    58  	// Fetch currently known metrics from Hawkular-Metrics and cache them
    59  	types := []metrics.MetricType{metrics.Gauge, metrics.Counter}
    60  	for _, t := range types {
    61  		err := h.updateDefinitions(t)
    62  		if err != nil {
    63  			return err
    64  		}
    65  	}
    66  
    67  	return nil
    68  }
    69  
    70  func (h *hawkularSink) Stop() {
    71  	h.regLock.Lock()
    72  	defer h.regLock.Unlock()
    73  	h.init()
    74  }
    75  
    76  func (h *hawkularSink) ExportData(db *core.DataBatch) {
    77  	totalCount := 0
    78  	for _, ms := range db.MetricSets {
    79  		totalCount += len(ms.MetricValues)
    80  		totalCount += len(ms.LabeledMetrics)
    81  	}
    82  
    83  	if len(db.MetricSets) > 0 {
    84  		tmhs := make(map[string][]metrics.MetricHeader)
    85  
    86  		if len(h.labelTenant) == 0 {
    87  			tmhs[h.client.Tenant] = make([]metrics.MetricHeader, 0, totalCount)
    88  		}
    89  
    90  		wg := &sync.WaitGroup{}
    91  
    92  		for _, ms := range db.MetricSets {
    93  
    94  			// // Transform ms.MetricValues to LabeledMetrics first
    95  			lms := metricValueToLabeledMetric(ms.MetricValues)
    96  			ms.LabeledMetrics = append(ms.LabeledMetrics, lms...)
    97  
    98  		Store:
    99  			for _, labeledMetric := range ms.LabeledMetrics {
   100  
   101  				for _, filter := range h.filters {
   102  					if !filter(ms, labeledMetric.Name) {
   103  						continue Store
   104  					}
   105  				}
   106  
   107  				tenant := h.client.Tenant
   108  
   109  				if len(h.labelTenant) > 0 {
   110  					if v, found := ms.Labels[h.labelTenant]; found {
   111  						tenant = v
   112  					}
   113  				}
   114  
   115  				wg.Add(1)
   116  				go func(ms *core.MetricSet, labeledMetric core.LabeledMetric, tenant string) {
   117  					defer wg.Done()
   118  					h.registerLabeledIfNecessary(ms, labeledMetric, metrics.Tenant(tenant))
   119  				}(ms, labeledMetric, tenant)
   120  
   121  				mH, err := h.pointToLabeledMetricHeader(ms, labeledMetric, db.Timestamp)
   122  				if err != nil {
   123  					// One transformation error should not prevent the whole process
   124  					glog.Errorf(err.Error())
   125  					continue
   126  				}
   127  
   128  				if _, found := tmhs[tenant]; !found {
   129  					tmhs[tenant] = make([]metrics.MetricHeader, 0)
   130  				}
   131  
   132  				tmhs[tenant] = append(tmhs[tenant], *mH)
   133  			}
   134  		}
   135  		h.sendData(tmhs, wg) // Send to a limited channel? Only batches.. egg.
   136  		wg.Wait()
   137  	}
   138  }
   139  
   140  func metricValueToLabeledMetric(msValues map[string]core.MetricValue) []core.LabeledMetric {
   141  	lms := make([]core.LabeledMetric, 0, len(msValues))
   142  	for metricName, metricValue := range msValues {
   143  		lm := core.LabeledMetric{
   144  			Name:        metricName,
   145  			MetricValue: metricValue,
   146  			Labels:      make(map[string]string, 0),
   147  		}
   148  		lms = append(lms, lm)
   149  	}
   150  	return lms
   151  }
   152  
   153  func (h *hawkularSink) DebugInfo() string {
   154  	info := fmt.Sprintf("%s\n", h.Name())
   155  
   156  	h.regLock.Lock()
   157  	defer h.regLock.Unlock()
   158  	info += fmt.Sprintf("Known metrics: %d\n", len(h.reg))
   159  	if len(h.labelTenant) > 0 {
   160  		info += fmt.Sprintf("Using label '%s' as tenant information\n", h.labelTenant)
   161  	}
   162  	if len(h.labelNodeId) > 0 {
   163  		info += fmt.Sprintf("Using label '%s' as node identified in resourceid\n", h.labelNodeId)
   164  	}
   165  
   166  	// TODO Add here statistics from the Hawkular-Metrics client instance
   167  	return info
   168  }
   169  
   170  func (h *hawkularSink) Name() string {
   171  	return "Hawkular-Metrics Sink"
   172  }
   173  
   174  // NewHawkularSink Creates and returns a new hawkularSink instance
   175  func NewHawkularSink(u *url.URL) (core.DataSink, error) {
   176  	sink := &hawkularSink{
   177  		uri:       u,
   178  		batchSize: 1000,
   179  	}
   180  	if err := sink.init(); err != nil {
   181  		return nil, err
   182  	}
   183  
   184  	metrics := make([]core.MetricDescriptor, 0, len(core.AllMetrics))
   185  	for _, metric := range core.AllMetrics {
   186  		metrics = append(metrics, metric.MetricDescriptor)
   187  	}
   188  	sink.Register(metrics)
   189  	return sink, nil
   190  }
   191  
   192  func (h *hawkularSink) init() error {
   193  	h.reg = make(map[string]*metrics.MetricDefinition)
   194  	h.models = make(map[string]*metrics.MetricDefinition)
   195  	h.modifiers = make([]metrics.Modifier, 0)
   196  	h.filters = make([]Filter, 0)
   197  	h.batchSize = batchSizeDefault
   198  
   199  	p := metrics.Parameters{
   200  		Tenant:      "heapster",
   201  		Url:         h.uri.String(),
   202  		Concurrency: concurrencyDefault,
   203  	}
   204  
   205  	opts := h.uri.Query()
   206  
   207  	if v, found := opts["tenant"]; found {
   208  		p.Tenant = v[0]
   209  	}
   210  
   211  	if v, found := opts["labelToTenant"]; found {
   212  		h.labelTenant = v[0]
   213  	}
   214  
   215  	if v, found := opts[nodeId]; found {
   216  		h.labelNodeId = v[0]
   217  	}
   218  
   219  	if v, found := opts["useServiceAccount"]; found {
   220  		if b, _ := strconv.ParseBool(v[0]); b {
   221  			// If a readable service account token exists, then use it
   222  			if contents, err := ioutil.ReadFile(defaultServiceAccountFile); err == nil {
   223  				p.Token = string(contents)
   224  			}
   225  		}
   226  	}
   227  
   228  	// Authentication / Authorization parameters
   229  	tC := &tls.Config{}
   230  
   231  	if v, found := opts["auth"]; found {
   232  		if _, f := opts["caCert"]; f {
   233  			return fmt.Errorf("Both auth and caCert files provided, combination is not supported")
   234  		}
   235  		if len(v[0]) > 0 {
   236  			// Authfile
   237  			kubeConfig, err := kubeClientCmd.NewNonInteractiveDeferredLoadingClientConfig(&kubeClientCmd.ClientConfigLoadingRules{
   238  				ExplicitPath: v[0]},
   239  				&kubeClientCmd.ConfigOverrides{}).ClientConfig()
   240  			if err != nil {
   241  				return err
   242  			}
   243  			tC, err = kube_client.TLSConfigFor(kubeConfig)
   244  			if err != nil {
   245  				return err
   246  			}
   247  		}
   248  	}
   249  
   250  	if u, found := opts["user"]; found {
   251  		if _, wrong := opts["useServiceAccount"]; wrong {
   252  			return fmt.Errorf("If user and password are used, serviceAccount cannot be used")
   253  		}
   254  		if p, f := opts["pass"]; f {
   255  			h.modifiers = append(h.modifiers, func(req *http.Request) error {
   256  				req.SetBasicAuth(u[0], p[0])
   257  				return nil
   258  			})
   259  		}
   260  	}
   261  
   262  	if v, found := opts["caCert"]; found {
   263  		caCert, err := ioutil.ReadFile(v[0])
   264  		if err != nil {
   265  			return err
   266  		}
   267  
   268  		caCertPool := x509.NewCertPool()
   269  		caCertPool.AppendCertsFromPEM(caCert)
   270  
   271  		tC.RootCAs = caCertPool
   272  	}
   273  
   274  	if v, found := opts["insecure"]; found {
   275  		_, f := opts["caCert"]
   276  		_, f2 := opts["auth"]
   277  		if f || f2 {
   278  			return fmt.Errorf("Insecure can't be defined with auth or caCert")
   279  		}
   280  		insecure, err := strconv.ParseBool(v[0])
   281  		if err != nil {
   282  			return err
   283  		}
   284  		tC.InsecureSkipVerify = insecure
   285  	}
   286  
   287  	p.TLSConfig = tC
   288  
   289  	// Filters
   290  	if v, found := opts["filter"]; found {
   291  		filters, err := parseFilters(v)
   292  		if err != nil {
   293  			return err
   294  		}
   295  		h.filters = filters
   296  	}
   297  
   298  	// Concurrency limitations
   299  	if v, found := opts["concurrencyLimit"]; found {
   300  		cs, err := strconv.Atoi(v[0])
   301  		if err != nil || cs < 0 {
   302  			return fmt.Errorf("Supplied concurrency value of %s is invalid", v[0])
   303  		}
   304  		p.Concurrency = cs
   305  	}
   306  
   307  	if v, found := opts["batchSize"]; found {
   308  		bs, err := strconv.Atoi(v[0])
   309  		if err != nil || bs < 0 {
   310  			return fmt.Errorf("Supplied batchSize value of %s is invalid", v[0])
   311  		}
   312  		h.batchSize = bs
   313  	}
   314  
   315  	c, err := metrics.NewHawkularClient(p)
   316  	if err != nil {
   317  		return err
   318  	}
   319  
   320  	h.client = c
   321  
   322  	glog.Infof("Initialised Hawkular Sink with parameters %v", p)
   323  	return nil
   324  }