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 }