~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/gopkg.in/juju/charm.v6-unstable/metrics.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2014 Canonical Ltd.
 
2
// Licensed under the LGPLv3, see LICENCE file for details.
 
3
 
 
4
package charm
 
5
 
 
6
import (
 
7
        "fmt"
 
8
        "io"
 
9
        "io/ioutil"
 
10
        "strconv"
 
11
        "strings"
 
12
 
 
13
        goyaml "gopkg.in/yaml.v2"
 
14
)
 
15
 
 
16
// MetricType is used to identify metric types supported by juju.
 
17
type MetricType string
 
18
 
 
19
const (
 
20
        builtinMetricPrefix = "juju"
 
21
 
 
22
        // Supported metric types.
 
23
        MetricTypeGauge    MetricType = "gauge"
 
24
        MetricTypeAbsolute MetricType = "absolute"
 
25
)
 
26
 
 
27
// IsBuiltinMetric reports whether the given metric key is in the builtin metric namespace
 
28
func IsBuiltinMetric(key string) bool {
 
29
        return strings.HasPrefix(key, builtinMetricPrefix)
 
30
}
 
31
 
 
32
func validateValue(value string) error {
 
33
        // The largest number of digits that can be returned by strconv.FormatFloat is 24, so
 
34
        // choose an arbitrary limit somewhat higher than that.
 
35
        if len(value) > 30 {
 
36
                return fmt.Errorf("metric value is too large")
 
37
        }
 
38
        fValue, err := strconv.ParseFloat(value, 64)
 
39
        if err != nil {
 
40
                return fmt.Errorf("invalid value type: expected float, got %q", value)
 
41
        }
 
42
        if fValue < 0 {
 
43
                return fmt.Errorf("invalid value: value must be greater or equal to zero, got %v", value)
 
44
        }
 
45
        return nil
 
46
}
 
47
 
 
48
// validateValue checks if the supplied metric value fits the requirements
 
49
// of its expected type.
 
50
func (m MetricType) validateValue(value string) error {
 
51
        switch m {
 
52
        case MetricTypeGauge, MetricTypeAbsolute:
 
53
                return validateValue(value)
 
54
        default:
 
55
                return fmt.Errorf("unknown metric type %q", m)
 
56
        }
 
57
}
 
58
 
 
59
// Metric represents a single metric definition
 
60
type Metric struct {
 
61
        Type        MetricType
 
62
        Description string
 
63
}
 
64
 
 
65
// Metrics contains the metrics declarations encoded in the metrics.yaml
 
66
// file.
 
67
type Metrics struct {
 
68
        Metrics map[string]Metric
 
69
}
 
70
 
 
71
// ReadMetrics reads a MetricsDeclaration in YAML format.
 
72
func ReadMetrics(r io.Reader) (*Metrics, error) {
 
73
        data, err := ioutil.ReadAll(r)
 
74
        if err != nil {
 
75
                return nil, err
 
76
        }
 
77
        var metrics Metrics
 
78
        if err := goyaml.Unmarshal(data, &metrics); err != nil {
 
79
                return nil, err
 
80
        }
 
81
        if metrics.Metrics == nil {
 
82
                return &metrics, nil
 
83
        }
 
84
        for name, metric := range metrics.Metrics {
 
85
                if IsBuiltinMetric(name) {
 
86
                        if metric.Type != MetricType("") || metric.Description != "" {
 
87
                                return nil, fmt.Errorf("metric %q is using a prefix reserved for built-in metrics: it should not have type or description specification", name)
 
88
                        }
 
89
                        continue
 
90
                }
 
91
                switch metric.Type {
 
92
                case MetricTypeGauge, MetricTypeAbsolute:
 
93
                default:
 
94
                        return nil, fmt.Errorf("invalid metrics declaration: metric %q has unknown type %q", name, metric.Type)
 
95
                }
 
96
                if metric.Description == "" {
 
97
                        return nil, fmt.Errorf("invalid metrics declaration: metric %q lacks description", name)
 
98
                }
 
99
        }
 
100
        return &metrics, nil
 
101
}
 
102
 
 
103
// ValidateMetric validates the supplied metric name and value against the loaded
 
104
// metric definitions.
 
105
func (m Metrics) ValidateMetric(name, value string) error {
 
106
        metric, exists := m.Metrics[name]
 
107
        if !exists {
 
108
                return fmt.Errorf("metric %q not defined", name)
 
109
        }
 
110
        if IsBuiltinMetric(name) {
 
111
                return validateValue(value)
 
112
        }
 
113
        return metric.Type.validateValue(value)
 
114
}