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 }