1
// Copyright 2014 Canonical Ltd.
2
// Licensed under the LGPLv3, see LICENCE file for details.
13
goyaml "gopkg.in/yaml.v2"
16
// MetricType is used to identify metric types supported by juju.
17
type MetricType string
20
builtinMetricPrefix = "juju"
22
// Supported metric types.
23
MetricTypeGauge MetricType = "gauge"
24
MetricTypeAbsolute MetricType = "absolute"
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)
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.
36
return fmt.Errorf("metric value is too large")
38
fValue, err := strconv.ParseFloat(value, 64)
40
return fmt.Errorf("invalid value type: expected float, got %q", value)
43
return fmt.Errorf("invalid value: value must be greater or equal to zero, got %v", value)
48
// validateValue checks if the supplied metric value fits the requirements
49
// of its expected type.
50
func (m MetricType) validateValue(value string) error {
52
case MetricTypeGauge, MetricTypeAbsolute:
53
return validateValue(value)
55
return fmt.Errorf("unknown metric type %q", m)
59
// Metric represents a single metric definition
65
// Metrics contains the metrics declarations encoded in the metrics.yaml
68
Metrics map[string]Metric
71
// ReadMetrics reads a MetricsDeclaration in YAML format.
72
func ReadMetrics(r io.Reader) (*Metrics, error) {
73
data, err := ioutil.ReadAll(r)
78
if err := goyaml.Unmarshal(data, &metrics); err != nil {
81
if metrics.Metrics == nil {
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)
92
case MetricTypeGauge, MetricTypeAbsolute:
94
return nil, fmt.Errorf("invalid metrics declaration: metric %q has unknown type %q", name, metric.Type)
96
if metric.Description == "" {
97
return nil, fmt.Errorf("invalid metrics declaration: metric %q lacks description", name)
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]
108
return fmt.Errorf("metric %q not defined", name)
110
if IsBuiltinMetric(name) {
111
return validateValue(value)
113
return metric.Type.validateValue(value)