Add support for retrieving Droplet monitoring metrics. (#491)
* Add metrics package with minimal copy of github.com/prometheus/common/model Package metrics is a minimal copy of github.com/prometheus/common/model providing types to work with the Prometheus-style results in a DigitalOcean Monitoring metrics response. We have copied this here as Prometheus' common packages are considered internal to Prometheus, without any stability guarantees for external usage. * Add support for retrieving Droplet monitoring metrics. * Use pointer receivers.
This commit is contained in:
parent
35a70e8f3a
commit
33658a69d8
|
@ -0,0 +1,81 @@
|
||||||
|
// Copyright 2013 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package metrics is a minimal copy of github.com/prometheus/common/model
|
||||||
|
// providing types to work with the Prometheus-style results in a DigitalOcean
|
||||||
|
// Monitoring metrics response.
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MetricNameLabel is the label name indicating the metric name of a
|
||||||
|
// timeseries.
|
||||||
|
MetricNameLabel = "__name__"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A LabelSet is a collection of LabelName and LabelValue pairs. The LabelSet
|
||||||
|
// may be fully-qualified down to the point where it may resolve to a single
|
||||||
|
// Metric in the data store or not. All operations that occur within the realm
|
||||||
|
// of a LabelSet can emit a vector of Metric entities to which the LabelSet may
|
||||||
|
// match.
|
||||||
|
type LabelSet map[LabelName]LabelValue
|
||||||
|
|
||||||
|
func (l LabelSet) String() string {
|
||||||
|
lstrs := make([]string, 0, len(l))
|
||||||
|
for l, v := range l {
|
||||||
|
lstrs = append(lstrs, fmt.Sprintf("%s=%q", l, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(lstrs)
|
||||||
|
return fmt.Sprintf("{%s}", strings.Join(lstrs, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
// A LabelValue is an associated value for a MetricLabelName.
|
||||||
|
type LabelValue string
|
||||||
|
|
||||||
|
// A LabelName is a key for a Metric.
|
||||||
|
type LabelName string
|
||||||
|
|
||||||
|
// A Metric is similar to a LabelSet, but the key difference is that a Metric is
|
||||||
|
// a singleton and refers to one and only one stream of samples.
|
||||||
|
type Metric LabelSet
|
||||||
|
|
||||||
|
func (m Metric) String() string {
|
||||||
|
metricName, hasName := m[MetricNameLabel]
|
||||||
|
numLabels := len(m) - 1
|
||||||
|
if !hasName {
|
||||||
|
numLabels = len(m)
|
||||||
|
}
|
||||||
|
labelStrings := make([]string, 0, numLabels)
|
||||||
|
for label, value := range m {
|
||||||
|
if label != MetricNameLabel {
|
||||||
|
labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch numLabels {
|
||||||
|
case 0:
|
||||||
|
if hasName {
|
||||||
|
return string(metricName)
|
||||||
|
}
|
||||||
|
return "{}"
|
||||||
|
default:
|
||||||
|
sort.Strings(labelStrings)
|
||||||
|
return fmt.Sprintf("%s{%s}", metricName, strings.Join(labelStrings, ", "))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
// Copyright 2013 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMetricToString(t *testing.T) {
|
||||||
|
scenarios := []struct {
|
||||||
|
name string
|
||||||
|
input Metric
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid metric without __name__ label",
|
||||||
|
input: Metric{
|
||||||
|
"first_name": "electro",
|
||||||
|
"occupation": "robot",
|
||||||
|
"manufacturer": "westinghouse",
|
||||||
|
},
|
||||||
|
expected: `{first_name="electro", manufacturer="westinghouse", occupation="robot"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid metric with __name__ label",
|
||||||
|
input: Metric{
|
||||||
|
"__name__": "electro",
|
||||||
|
"occupation": "robot",
|
||||||
|
"manufacturer": "westinghouse",
|
||||||
|
},
|
||||||
|
expected: `electro{manufacturer="westinghouse", occupation="robot"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty metric with __name__ label",
|
||||||
|
input: Metric{
|
||||||
|
"__name__": "fooname",
|
||||||
|
},
|
||||||
|
expected: "fooname",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty metric",
|
||||||
|
input: Metric{},
|
||||||
|
expected: "{}",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, scenario := range scenarios {
|
||||||
|
t.Run(scenario.name, func(t *testing.T) {
|
||||||
|
actual := scenario.input.String()
|
||||||
|
if actual != scenario.expected {
|
||||||
|
t.Errorf("expected string output %s but got %s", actual, scenario.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,164 @@
|
||||||
|
// Copyright 2013 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MinimumTick is the minimum supported time resolution. This has to be
|
||||||
|
// at least time.Second in order for the code below to work.
|
||||||
|
minimumTick = time.Millisecond
|
||||||
|
// second is the Time duration equivalent to one second.
|
||||||
|
second = int64(time.Second / minimumTick)
|
||||||
|
// The number of nanoseconds per minimum tick.
|
||||||
|
nanosPerTick = int64(minimumTick / time.Nanosecond)
|
||||||
|
|
||||||
|
// Earliest is the earliest Time representable. Handy for
|
||||||
|
// initializing a high watermark.
|
||||||
|
Earliest = Time(math.MinInt64)
|
||||||
|
// Latest is the latest Time representable. Handy for initializing
|
||||||
|
// a low watermark.
|
||||||
|
Latest = Time(math.MaxInt64)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Time is the number of milliseconds since the epoch
|
||||||
|
// (1970-01-01 00:00 UTC) excluding leap seconds.
|
||||||
|
type Time int64
|
||||||
|
|
||||||
|
// Interval describes an interval between two timestamps.
|
||||||
|
type Interval struct {
|
||||||
|
Start, End Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now returns the current time as a Time.
|
||||||
|
func Now() Time {
|
||||||
|
return TimeFromUnixNano(time.Now().UnixNano())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeFromUnix returns the Time equivalent to the Unix Time t
|
||||||
|
// provided in seconds.
|
||||||
|
func TimeFromUnix(t int64) Time {
|
||||||
|
return Time(t * second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeFromUnixNano returns the Time equivalent to the Unix Time
|
||||||
|
// t provided in nanoseconds.
|
||||||
|
func TimeFromUnixNano(t int64) Time {
|
||||||
|
return Time(t / nanosPerTick)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal reports whether two Times represent the same instant.
|
||||||
|
func (t Time) Equal(o Time) bool {
|
||||||
|
return t == o
|
||||||
|
}
|
||||||
|
|
||||||
|
// Before reports whether the Time t is before o.
|
||||||
|
func (t Time) Before(o Time) bool {
|
||||||
|
return t < o
|
||||||
|
}
|
||||||
|
|
||||||
|
// After reports whether the Time t is after o.
|
||||||
|
func (t Time) After(o Time) bool {
|
||||||
|
return t > o
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add returns the Time t + d.
|
||||||
|
func (t Time) Add(d time.Duration) Time {
|
||||||
|
return t + Time(d/minimumTick)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub returns the Duration t - o.
|
||||||
|
func (t Time) Sub(o Time) time.Duration {
|
||||||
|
return time.Duration(t-o) * minimumTick
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time returns the time.Time representation of t.
|
||||||
|
func (t Time) Time() time.Time {
|
||||||
|
return time.Unix(int64(t)/second, (int64(t)%second)*nanosPerTick)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unix returns t as a Unix time, the number of seconds elapsed
|
||||||
|
// since January 1, 1970 UTC.
|
||||||
|
func (t Time) Unix() int64 {
|
||||||
|
return int64(t) / second
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnixNano returns t as a Unix time, the number of nanoseconds elapsed
|
||||||
|
// since January 1, 1970 UTC.
|
||||||
|
func (t Time) UnixNano() int64 {
|
||||||
|
return int64(t) * nanosPerTick
|
||||||
|
}
|
||||||
|
|
||||||
|
// The number of digits after the dot.
|
||||||
|
var dotPrecision = int(math.Log10(float64(second)))
|
||||||
|
|
||||||
|
// String returns a string representation of the Time.
|
||||||
|
func (t Time) String() string {
|
||||||
|
return strconv.FormatFloat(float64(t)/float64(second), 'f', -1, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements the json.Marshaler interface.
|
||||||
|
func (t Time) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(t.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||||
|
func (t *Time) UnmarshalJSON(b []byte) error {
|
||||||
|
p := strings.Split(string(b), ".")
|
||||||
|
switch len(p) {
|
||||||
|
case 1:
|
||||||
|
v, err := strconv.ParseInt(string(p[0]), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*t = Time(v * second)
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
v, err := strconv.ParseInt(string(p[0]), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v *= second
|
||||||
|
|
||||||
|
prec := dotPrecision - len(p[1])
|
||||||
|
if prec < 0 {
|
||||||
|
p[1] = p[1][:dotPrecision]
|
||||||
|
} else if prec > 0 {
|
||||||
|
p[1] = p[1] + strings.Repeat("0", prec)
|
||||||
|
}
|
||||||
|
|
||||||
|
va, err := strconv.ParseInt(p[1], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the value was something like -0.1 the negative is lost in the
|
||||||
|
// parsing because of the leading zero, this ensures that we capture it.
|
||||||
|
if len(p[0]) > 0 && p[0][0] == '-' && v+va > 0 {
|
||||||
|
*t = Time(v+va) * -1
|
||||||
|
} else {
|
||||||
|
*t = Time(v + va)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid time %q", string(b))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
// Copyright 2013 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestComparators(t *testing.T) {
|
||||||
|
t1a := TimeFromUnix(0)
|
||||||
|
t1b := TimeFromUnix(0)
|
||||||
|
t2 := TimeFromUnix(2*second - 1)
|
||||||
|
|
||||||
|
if !t1a.Equal(t1b) {
|
||||||
|
t.Fatalf("Expected %s to be equal to %s", t1a, t1b)
|
||||||
|
}
|
||||||
|
if t1a.Equal(t2) {
|
||||||
|
t.Fatalf("Expected %s to not be equal to %s", t1a, t2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !t1a.Before(t2) {
|
||||||
|
t.Fatalf("Expected %s to be before %s", t1a, t2)
|
||||||
|
}
|
||||||
|
if t1a.Before(t1b) {
|
||||||
|
t.Fatalf("Expected %s to not be before %s", t1a, t1b)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !t2.After(t1a) {
|
||||||
|
t.Fatalf("Expected %s to be after %s", t2, t1a)
|
||||||
|
}
|
||||||
|
if t1b.After(t1a) {
|
||||||
|
t.Fatalf("Expected %s to not be after %s", t1b, t1a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimeConversions(t *testing.T) {
|
||||||
|
unixSecs := int64(1136239445)
|
||||||
|
unixNsecs := int64(123456789)
|
||||||
|
unixNano := unixSecs*1e9 + unixNsecs
|
||||||
|
|
||||||
|
t1 := time.Unix(unixSecs, unixNsecs-unixNsecs%nanosPerTick)
|
||||||
|
t2 := time.Unix(unixSecs, unixNsecs)
|
||||||
|
|
||||||
|
ts := TimeFromUnixNano(unixNano)
|
||||||
|
if !ts.Time().Equal(t1) {
|
||||||
|
t.Fatalf("Expected %s, got %s", t1, ts.Time())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test available precision.
|
||||||
|
ts = TimeFromUnixNano(t2.UnixNano())
|
||||||
|
if !ts.Time().Equal(t1) {
|
||||||
|
t.Fatalf("Expected %s, got %s", t1, ts.Time())
|
||||||
|
}
|
||||||
|
|
||||||
|
if ts.UnixNano() != unixNano-unixNano%nanosPerTick {
|
||||||
|
t.Fatalf("Expected %d, got %d", unixNano, ts.UnixNano())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDuration(t *testing.T) {
|
||||||
|
duration := time.Second + time.Minute + time.Hour
|
||||||
|
goTime := time.Unix(1136239445, 0)
|
||||||
|
|
||||||
|
ts := TimeFromUnix(goTime.Unix())
|
||||||
|
if !goTime.Add(duration).Equal(ts.Add(duration).Time()) {
|
||||||
|
t.Fatalf("Expected %s to be equal to %s", goTime.Add(duration), ts.Add(duration))
|
||||||
|
}
|
||||||
|
|
||||||
|
earlier := ts.Add(-duration)
|
||||||
|
delta := ts.Sub(earlier)
|
||||||
|
if delta != duration {
|
||||||
|
t.Fatalf("Expected %s to be equal to %s", delta, duration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimeJSON(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
in Time
|
||||||
|
out string
|
||||||
|
}{
|
||||||
|
{Time(1), `0.001`},
|
||||||
|
{Time(-1), `-0.001`},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||||
|
b, err := test.in.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error marshaling time: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(b) != test.out {
|
||||||
|
t.Errorf("Mismatch in marshal expected=%s actual=%s", test.out, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tm Time
|
||||||
|
if err := tm.UnmarshalJSON(b); err != nil {
|
||||||
|
t.Fatalf("Error Unmarshaling time: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !test.in.Equal(tm) {
|
||||||
|
t.Fatalf("Mismatch after Unmarshal expected=%v actual=%v", test.in, tm)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
// Copyright 2013 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A SampleValue is a representation of a value for a given sample at a given time.
|
||||||
|
type SampleValue float64
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
|
func (v *SampleValue) UnmarshalJSON(b []byte) error {
|
||||||
|
if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' {
|
||||||
|
return fmt.Errorf("sample value must be a quoted string")
|
||||||
|
}
|
||||||
|
f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*v = SampleValue(f)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler.
|
||||||
|
func (v SampleValue) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(v.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v SampleValue) String() string {
|
||||||
|
return strconv.FormatFloat(float64(v), 'f', -1, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns true if the value of v and o is equal or if both are NaN. Note
|
||||||
|
// that v==o is false if both are NaN. If you want the conventional float
|
||||||
|
// behavior, use == to compare two SampleValues.
|
||||||
|
func (v SampleValue) Equal(o SampleValue) bool {
|
||||||
|
if v == o {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return math.IsNaN(float64(v)) && math.IsNaN(float64(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SamplePair pairs a SampleValue with a Timestamp.
|
||||||
|
type SamplePair struct {
|
||||||
|
Timestamp Time
|
||||||
|
Value SampleValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SamplePair) String() string {
|
||||||
|
return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
|
func (s *SamplePair) UnmarshalJSON(b []byte) error {
|
||||||
|
v := [...]json.Unmarshaler{&s.Timestamp, &s.Value}
|
||||||
|
return json.Unmarshal(b, &v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler.
|
||||||
|
func (s SamplePair) MarshalJSON() ([]byte, error) {
|
||||||
|
t, err := json.Marshal(s.Timestamp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
v, err := json.Marshal(s.Value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SampleStream is a stream of Values belonging to an attached COWMetric.
|
||||||
|
type SampleStream struct {
|
||||||
|
Metric Metric `json:"metric"`
|
||||||
|
Values []SamplePair `json:"values"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss SampleStream) String() string {
|
||||||
|
vals := make([]string, len(ss.Values))
|
||||||
|
for i, v := range ss.Values {
|
||||||
|
vals[i] = v.String()
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s =>\n%s", ss.Metric, strings.Join(vals, "\n"))
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
// Copyright 2013 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEqualValues(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
in1, in2 SampleValue
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
"equal floats": {
|
||||||
|
in1: 3.14,
|
||||||
|
in2: 3.14,
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
"unequal floats": {
|
||||||
|
in1: 3.14,
|
||||||
|
in2: 3.1415,
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
"positive inifinities": {
|
||||||
|
in1: SampleValue(math.Inf(+1)),
|
||||||
|
in2: SampleValue(math.Inf(+1)),
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
"negative inifinities": {
|
||||||
|
in1: SampleValue(math.Inf(-1)),
|
||||||
|
in2: SampleValue(math.Inf(-1)),
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
"different inifinities": {
|
||||||
|
in1: SampleValue(math.Inf(+1)),
|
||||||
|
in2: SampleValue(math.Inf(-1)),
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
"number and infinity": {
|
||||||
|
in1: 42,
|
||||||
|
in2: SampleValue(math.Inf(+1)),
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
"number and NaN": {
|
||||||
|
in1: 42,
|
||||||
|
in2: SampleValue(math.NaN()),
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
"NaNs": {
|
||||||
|
in1: SampleValue(math.NaN()),
|
||||||
|
in2: SampleValue(math.NaN()),
|
||||||
|
want: true, // !!!
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, test := range tests {
|
||||||
|
got := test.in1.Equal(test.in2)
|
||||||
|
if got != test.want {
|
||||||
|
t.Errorf("Comparing %s, %f and %f: got %t, want %t", name, test.in1, test.in2, got, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSamplePairJSON(t *testing.T) {
|
||||||
|
input := []struct {
|
||||||
|
plain string
|
||||||
|
value SamplePair
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
plain: `[1234.567,"123.1"]`,
|
||||||
|
value: SamplePair{
|
||||||
|
Value: 123.1,
|
||||||
|
Timestamp: 1234567,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range input {
|
||||||
|
b, err := json.Marshal(test.value)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(b) != test.plain {
|
||||||
|
t.Errorf("encoding error: expected %q, got %q", test.plain, b)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var sp SamplePair
|
||||||
|
err = json.Unmarshal(b, &sp)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if sp != test.value {
|
||||||
|
t.Errorf("decoding error: expected %v, got %v", test.value, sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
137
monitoring.go
137
monitoring.go
|
@ -4,11 +4,15 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/digitalocean/godo/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
monitoringBasePath = "v2/monitoring"
|
monitoringBasePath = "v2/monitoring"
|
||||||
alertPolicyBasePath = monitoringBasePath + "/alerts"
|
alertPolicyBasePath = monitoringBasePath + "/alerts"
|
||||||
|
dropletMetricsBasePath = monitoringBasePath + "/metrics/droplet"
|
||||||
|
|
||||||
DropletCPUUtilizationPercent = "v1/insights/droplet/cpu"
|
DropletCPUUtilizationPercent = "v1/insights/droplet/cpu"
|
||||||
DropletMemoryUtilizationPercent = "v1/insights/droplet/memory_utilization_percent"
|
DropletMemoryUtilizationPercent = "v1/insights/droplet/memory_utilization_percent"
|
||||||
|
@ -33,6 +37,18 @@ type MonitoringService interface {
|
||||||
CreateAlertPolicy(context.Context, *AlertPolicyCreateRequest) (*AlertPolicy, *Response, error)
|
CreateAlertPolicy(context.Context, *AlertPolicyCreateRequest) (*AlertPolicy, *Response, error)
|
||||||
UpdateAlertPolicy(context.Context, string, *AlertPolicyUpdateRequest) (*AlertPolicy, *Response, error)
|
UpdateAlertPolicy(context.Context, string, *AlertPolicyUpdateRequest) (*AlertPolicy, *Response, error)
|
||||||
DeleteAlertPolicy(context.Context, string) (*Response, error)
|
DeleteAlertPolicy(context.Context, string) (*Response, error)
|
||||||
|
|
||||||
|
GetDropletBandwidth(context.Context, *DropletBandwidthMetricsRequest) (*MetricsResponse, *Response, error)
|
||||||
|
GetDropletAvailableMemory(context.Context, *DropletMetricsRequest) (*MetricsResponse, *Response, error)
|
||||||
|
GetDropletCPU(context.Context, *DropletMetricsRequest) (*MetricsResponse, *Response, error)
|
||||||
|
GetDropletFilesystemFree(context.Context, *DropletMetricsRequest) (*MetricsResponse, *Response, error)
|
||||||
|
GetDropletFilesystemSize(context.Context, *DropletMetricsRequest) (*MetricsResponse, *Response, error)
|
||||||
|
GetDropletLoad1(context.Context, *DropletMetricsRequest) (*MetricsResponse, *Response, error)
|
||||||
|
GetDropletLoad5(context.Context, *DropletMetricsRequest) (*MetricsResponse, *Response, error)
|
||||||
|
GetDropletLoad15(context.Context, *DropletMetricsRequest) (*MetricsResponse, *Response, error)
|
||||||
|
GetDropletCachedMemory(context.Context, *DropletMetricsRequest) (*MetricsResponse, *Response, error)
|
||||||
|
GetDropletFreeMemory(context.Context, *DropletMetricsRequest) (*MetricsResponse, *Response, error)
|
||||||
|
GetDropletTotalMemory(context.Context, *DropletMetricsRequest) (*MetricsResponse, *Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonitoringServiceOp handles communication with monitoring related methods of the
|
// MonitoringServiceOp handles communication with monitoring related methods of the
|
||||||
|
@ -115,6 +131,32 @@ type alertPolicyRoot struct {
|
||||||
AlertPolicy *AlertPolicy `json:"policy,omitempty"`
|
AlertPolicy *AlertPolicy `json:"policy,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DropletMetricsRequest holds the information needed to retrieve Droplet various metrics.
|
||||||
|
type DropletMetricsRequest struct {
|
||||||
|
HostID string
|
||||||
|
Start time.Time
|
||||||
|
End time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropletBandwidthMetricsRequest holds the information needed to retrieve Droplet bandwidth metrics.
|
||||||
|
type DropletBandwidthMetricsRequest struct {
|
||||||
|
DropletMetricsRequest
|
||||||
|
Interface string
|
||||||
|
Direction string
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetricsResponse holds a Metrics query response.
|
||||||
|
type MetricsResponse struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Data MetricsData `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetricsData holds the data portion of a Metrics response.
|
||||||
|
type MetricsData struct {
|
||||||
|
ResultType string `json:"resultType"`
|
||||||
|
Result []metrics.SampleStream `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
// ListAlertPolicies all alert policies
|
// ListAlertPolicies all alert policies
|
||||||
func (s *MonitoringServiceOp) ListAlertPolicies(ctx context.Context, opt *ListOptions) ([]AlertPolicy, *Response, error) {
|
func (s *MonitoringServiceOp) ListAlertPolicies(ctx context.Context, opt *ListOptions) ([]AlertPolicy, *Response, error) {
|
||||||
path := alertPolicyBasePath
|
path := alertPolicyBasePath
|
||||||
|
@ -221,3 +263,94 @@ func (s *MonitoringServiceOp) DeleteAlertPolicy(ctx context.Context, uuid string
|
||||||
|
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDropletBandwidth retrieves Droplet bandwidth metrics.
|
||||||
|
func (s *MonitoringServiceOp) GetDropletBandwidth(ctx context.Context, args *DropletBandwidthMetricsRequest) (*MetricsResponse, *Response, error) {
|
||||||
|
path := dropletMetricsBasePath + "/bandwidth"
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Add("host_id", args.HostID)
|
||||||
|
q.Add("interface", args.Interface)
|
||||||
|
q.Add("direction", args.Direction)
|
||||||
|
q.Add("start", fmt.Sprintf("%d", args.Start.Unix()))
|
||||||
|
q.Add("end", fmt.Sprintf("%d", args.End.Unix()))
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
root := new(MetricsResponse)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
|
||||||
|
return root, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDropletCPU retrieves Droplet CPU metrics.
|
||||||
|
func (s *MonitoringServiceOp) GetDropletCPU(ctx context.Context, args *DropletMetricsRequest) (*MetricsResponse, *Response, error) {
|
||||||
|
return s.getDropletMetrics(ctx, "/cpu", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDropletFilesystemFree retrieves Droplet filesystem free metrics.
|
||||||
|
func (s *MonitoringServiceOp) GetDropletFilesystemFree(ctx context.Context, args *DropletMetricsRequest) (*MetricsResponse, *Response, error) {
|
||||||
|
return s.getDropletMetrics(ctx, "/filesystem_free", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDropletFilesystemSize retrieves Droplet filesystem size metrics.
|
||||||
|
func (s *MonitoringServiceOp) GetDropletFilesystemSize(ctx context.Context, args *DropletMetricsRequest) (*MetricsResponse, *Response, error) {
|
||||||
|
return s.getDropletMetrics(ctx, "/filesystem_size", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDropletLoad1 retrieves Droplet load 1 metrics.
|
||||||
|
func (s *MonitoringServiceOp) GetDropletLoad1(ctx context.Context, args *DropletMetricsRequest) (*MetricsResponse, *Response, error) {
|
||||||
|
return s.getDropletMetrics(ctx, "/load_1", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDropletLoad5 retrieves Droplet load 5 metrics.
|
||||||
|
func (s *MonitoringServiceOp) GetDropletLoad5(ctx context.Context, args *DropletMetricsRequest) (*MetricsResponse, *Response, error) {
|
||||||
|
return s.getDropletMetrics(ctx, "/load_5", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDropletLoad15 retrieves Droplet load 15 metrics.
|
||||||
|
func (s *MonitoringServiceOp) GetDropletLoad15(ctx context.Context, args *DropletMetricsRequest) (*MetricsResponse, *Response, error) {
|
||||||
|
return s.getDropletMetrics(ctx, "/load_15", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDropletCachedMemory retrieves Droplet cached memory metrics.
|
||||||
|
func (s *MonitoringServiceOp) GetDropletCachedMemory(ctx context.Context, args *DropletMetricsRequest) (*MetricsResponse, *Response, error) {
|
||||||
|
return s.getDropletMetrics(ctx, "/memory_cached", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDropletFreeMemory retrieves Droplet free memory metrics.
|
||||||
|
func (s *MonitoringServiceOp) GetDropletFreeMemory(ctx context.Context, args *DropletMetricsRequest) (*MetricsResponse, *Response, error) {
|
||||||
|
return s.getDropletMetrics(ctx, "/memory_free", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDropletTotalMemory retrieves Droplet total memory metrics.
|
||||||
|
func (s *MonitoringServiceOp) GetDropletTotalMemory(ctx context.Context, args *DropletMetricsRequest) (*MetricsResponse, *Response, error) {
|
||||||
|
return s.getDropletMetrics(ctx, "/memory_total", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDropletAvailableMemory retrieves Droplet available memory metrics.
|
||||||
|
func (s *MonitoringServiceOp) GetDropletAvailableMemory(ctx context.Context, args *DropletMetricsRequest) (*MetricsResponse, *Response, error) {
|
||||||
|
return s.getDropletMetrics(ctx, "/memory_available", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MonitoringServiceOp) getDropletMetrics(ctx context.Context, path string, args *DropletMetricsRequest) (*MetricsResponse, *Response, error) {
|
||||||
|
fullPath := dropletMetricsBasePath + path
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, fullPath, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Add("host_id", args.HostID)
|
||||||
|
q.Add("start", fmt.Sprintf("%d", args.Start.Unix()))
|
||||||
|
q.Add("end", fmt.Sprintf("%d", args.End.Unix()))
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
root := new(MetricsResponse)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
|
||||||
|
return root, resp, err
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,10 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/digitalocean/godo/metrics"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -170,6 +174,557 @@ var (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
bandwidthRespJSON = `
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"data": {
|
||||||
|
"resultType": "matrix",
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"metric": {
|
||||||
|
"direction": "inbound",
|
||||||
|
"host_id": "222651441",
|
||||||
|
"interface": "private"
|
||||||
|
},
|
||||||
|
"values": [
|
||||||
|
[
|
||||||
|
1634052360,
|
||||||
|
"0.016600450090265357"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1634052480,
|
||||||
|
"0.015085955677299055"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1634052600,
|
||||||
|
"0.014941163855322308"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1634052720,
|
||||||
|
"0.016214285714285712"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
memoryRespJSON = `
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"data": {
|
||||||
|
"resultType": "matrix",
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"metric": {
|
||||||
|
"host_id": "123"
|
||||||
|
},
|
||||||
|
"values": [
|
||||||
|
[
|
||||||
|
1635386880,
|
||||||
|
"1028956160"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387000,
|
||||||
|
"1028956160"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387120,
|
||||||
|
"1028956160"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
filesystemRespJSON = `
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"data": {
|
||||||
|
"resultType": "matrix",
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"metric": {
|
||||||
|
"device": "/dev/vda1",
|
||||||
|
"fstype": "ext4",
|
||||||
|
"host_id": "123",
|
||||||
|
"mountpoint": "/"
|
||||||
|
},
|
||||||
|
"values": [
|
||||||
|
[
|
||||||
|
1635386880,
|
||||||
|
"25832407040"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387000,
|
||||||
|
"25832407040"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387120,
|
||||||
|
"25832407040"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
loadRespJSON = `
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"data": {
|
||||||
|
"resultType": "matrix",
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"metric": {
|
||||||
|
"host_id": "123"
|
||||||
|
},
|
||||||
|
"values": [
|
||||||
|
[
|
||||||
|
1635386880,
|
||||||
|
"0.04"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387000,
|
||||||
|
"0.03"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387120,
|
||||||
|
"0.01"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
cpuRespJSON = `
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"data": {
|
||||||
|
"resultType": "matrix",
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"metric": {
|
||||||
|
"host_id": "123",
|
||||||
|
"mode": "idle"
|
||||||
|
},
|
||||||
|
"values": [
|
||||||
|
[
|
||||||
|
1635386880,
|
||||||
|
"122901.18"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387000,
|
||||||
|
"123020.92"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387120,
|
||||||
|
"123140.8"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metric": {
|
||||||
|
"host_id": "123",
|
||||||
|
"mode": "iowait"
|
||||||
|
},
|
||||||
|
"values": [
|
||||||
|
[
|
||||||
|
1635386880,
|
||||||
|
"14.99"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387000,
|
||||||
|
"15.01"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387120,
|
||||||
|
"15.01"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metric": {
|
||||||
|
"host_id": "123",
|
||||||
|
"mode": "irq"
|
||||||
|
},
|
||||||
|
"values": [
|
||||||
|
[
|
||||||
|
1635386880,
|
||||||
|
"0"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387000,
|
||||||
|
"0"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387120,
|
||||||
|
"0"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metric": {
|
||||||
|
"host_id": "123",
|
||||||
|
"mode": "nice"
|
||||||
|
},
|
||||||
|
"values": [
|
||||||
|
[
|
||||||
|
1635386880,
|
||||||
|
"66.35"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387000,
|
||||||
|
"66.35"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387120,
|
||||||
|
"66.35"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metric": {
|
||||||
|
"host_id": "123",
|
||||||
|
"mode": "softirq"
|
||||||
|
},
|
||||||
|
"values": [
|
||||||
|
[
|
||||||
|
1635386880,
|
||||||
|
"2.13"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387000,
|
||||||
|
"2.13"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387120,
|
||||||
|
"2.13"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metric": {
|
||||||
|
"host_id": "123",
|
||||||
|
"mode": "steal"
|
||||||
|
},
|
||||||
|
"values": [
|
||||||
|
[
|
||||||
|
1635386880,
|
||||||
|
"7.89"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387000,
|
||||||
|
"7.9"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387120,
|
||||||
|
"7.91"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metric": {
|
||||||
|
"host_id": "123",
|
||||||
|
"mode": "system"
|
||||||
|
},
|
||||||
|
"values": [
|
||||||
|
[
|
||||||
|
1635386880,
|
||||||
|
"140.09"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387000,
|
||||||
|
"140.2"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387120,
|
||||||
|
"140.23"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metric": {
|
||||||
|
"host_id": "123",
|
||||||
|
"mode": "user"
|
||||||
|
},
|
||||||
|
"values": [
|
||||||
|
[
|
||||||
|
1635386880,
|
||||||
|
"278.57"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387000,
|
||||||
|
"278.65"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1635387120,
|
||||||
|
"278.69"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
testCPUResponse = &MetricsResponse{
|
||||||
|
Status: "success",
|
||||||
|
Data: MetricsData{
|
||||||
|
ResultType: "matrix",
|
||||||
|
Result: []metrics.SampleStream{
|
||||||
|
{
|
||||||
|
Metric: metrics.Metric{
|
||||||
|
"host_id": "123",
|
||||||
|
"mode": "idle",
|
||||||
|
},
|
||||||
|
Values: []metrics.SamplePair{
|
||||||
|
{
|
||||||
|
Timestamp: 1635386880000,
|
||||||
|
Value: 122901.18,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387000000,
|
||||||
|
Value: 123020.92,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387120000,
|
||||||
|
Value: 123140.8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Metric: metrics.Metric{
|
||||||
|
"host_id": "123",
|
||||||
|
"mode": "iowait",
|
||||||
|
},
|
||||||
|
Values: []metrics.SamplePair{
|
||||||
|
{
|
||||||
|
Timestamp: 1635386880000,
|
||||||
|
Value: 14.99,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387000000,
|
||||||
|
Value: 15.01,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387120000,
|
||||||
|
Value: 15.01,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Metric: metrics.Metric{
|
||||||
|
"host_id": "123",
|
||||||
|
"mode": "irq",
|
||||||
|
},
|
||||||
|
Values: []metrics.SamplePair{
|
||||||
|
{
|
||||||
|
Timestamp: 1635386880000,
|
||||||
|
Value: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387000000,
|
||||||
|
Value: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387120000,
|
||||||
|
Value: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Metric: metrics.Metric{
|
||||||
|
"host_id": "123",
|
||||||
|
"mode": "nice",
|
||||||
|
},
|
||||||
|
Values: []metrics.SamplePair{
|
||||||
|
{
|
||||||
|
Timestamp: 1635386880000,
|
||||||
|
Value: 66.35,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387000000,
|
||||||
|
Value: 66.35,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387120000,
|
||||||
|
Value: 66.35,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Metric: metrics.Metric{
|
||||||
|
"host_id": "123",
|
||||||
|
"mode": "softirq",
|
||||||
|
},
|
||||||
|
Values: []metrics.SamplePair{
|
||||||
|
{
|
||||||
|
Timestamp: 1635386880000,
|
||||||
|
Value: 2.13,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387000000,
|
||||||
|
Value: 2.13,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387120000,
|
||||||
|
Value: 2.13,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Metric: metrics.Metric{
|
||||||
|
"host_id": "123",
|
||||||
|
"mode": "steal",
|
||||||
|
},
|
||||||
|
Values: []metrics.SamplePair{
|
||||||
|
{
|
||||||
|
Timestamp: 1635386880000,
|
||||||
|
Value: 7.89,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387000000,
|
||||||
|
Value: 7.9,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387120000,
|
||||||
|
Value: 7.91,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Metric: metrics.Metric{
|
||||||
|
"host_id": "123",
|
||||||
|
"mode": "system",
|
||||||
|
},
|
||||||
|
Values: []metrics.SamplePair{
|
||||||
|
{
|
||||||
|
Timestamp: 1635386880000,
|
||||||
|
Value: 140.09,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387000000,
|
||||||
|
Value: 140.2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387120000,
|
||||||
|
Value: 140.23,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Metric: metrics.Metric{
|
||||||
|
"host_id": "123",
|
||||||
|
"mode": "user",
|
||||||
|
},
|
||||||
|
Values: []metrics.SamplePair{
|
||||||
|
{
|
||||||
|
Timestamp: 1635386880000,
|
||||||
|
Value: 278.57,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387000000,
|
||||||
|
Value: 278.65,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387120000,
|
||||||
|
Value: 278.69,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testLoadResponse = &MetricsResponse{
|
||||||
|
Status: "success",
|
||||||
|
Data: MetricsData{
|
||||||
|
ResultType: "matrix",
|
||||||
|
Result: []metrics.SampleStream{
|
||||||
|
{
|
||||||
|
Metric: metrics.Metric{
|
||||||
|
"host_id": "123",
|
||||||
|
},
|
||||||
|
Values: []metrics.SamplePair{
|
||||||
|
{
|
||||||
|
Timestamp: 1635386880000,
|
||||||
|
Value: 0.04,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387000000,
|
||||||
|
Value: 0.03,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387120000,
|
||||||
|
Value: 0.01,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testFilesystemResponse = &MetricsResponse{
|
||||||
|
Status: "success",
|
||||||
|
Data: MetricsData{
|
||||||
|
ResultType: "matrix",
|
||||||
|
Result: []metrics.SampleStream{
|
||||||
|
{
|
||||||
|
Metric: metrics.Metric{
|
||||||
|
"device": "/dev/vda1",
|
||||||
|
"fstype": "ext4",
|
||||||
|
"host_id": "123",
|
||||||
|
"mountpoint": "/",
|
||||||
|
},
|
||||||
|
Values: []metrics.SamplePair{
|
||||||
|
{
|
||||||
|
Timestamp: 1635386880000,
|
||||||
|
Value: 25832407040,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387000000,
|
||||||
|
Value: 25832407040,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387120000,
|
||||||
|
Value: 25832407040,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testMemoryResponse = &MetricsResponse{
|
||||||
|
Status: "success",
|
||||||
|
Data: MetricsData{
|
||||||
|
ResultType: "matrix",
|
||||||
|
Result: []metrics.SampleStream{
|
||||||
|
{
|
||||||
|
Metric: metrics.Metric{
|
||||||
|
"host_id": "123",
|
||||||
|
},
|
||||||
|
Values: []metrics.SamplePair{
|
||||||
|
{
|
||||||
|
Timestamp: 1635386880000,
|
||||||
|
Value: 1.02895616e+09,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387000000,
|
||||||
|
Value: 1.02895616e+09,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1635387120000,
|
||||||
|
Value: 1.02895616e+09,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAlertPolicies_List(t *testing.T) {
|
func TestAlertPolicies_List(t *testing.T) {
|
||||||
|
@ -368,3 +923,385 @@ func TestAlertPolicy_Update(t *testing.T) {
|
||||||
t.Errorf("Monitoring.UpdateAlertPolicy returned %+v, expected %+v", policy, expected)
|
t.Errorf("Monitoring.UpdateAlertPolicy returned %+v, expected %+v", policy, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetDropletBandwidth(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
defer teardown()
|
||||||
|
now := time.Now()
|
||||||
|
metricReq := &DropletBandwidthMetricsRequest{
|
||||||
|
DropletMetricsRequest: DropletMetricsRequest{HostID: "123",
|
||||||
|
Start: now.Add(-300 * time.Second),
|
||||||
|
End: now,
|
||||||
|
},
|
||||||
|
Interface: "private",
|
||||||
|
Direction: "inbound",
|
||||||
|
}
|
||||||
|
|
||||||
|
mux.HandleFunc("/v2/monitoring/metrics/droplet/bandwidth", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hostID := r.URL.Query().Get("host_id")
|
||||||
|
inter := r.URL.Query().Get("interface")
|
||||||
|
direction := r.URL.Query().Get("direction")
|
||||||
|
start := r.URL.Query().Get("start")
|
||||||
|
end := r.URL.Query().Get("end")
|
||||||
|
|
||||||
|
assert.Equal(t, metricReq.HostID, hostID)
|
||||||
|
assert.Equal(t, metricReq.Interface, inter)
|
||||||
|
assert.Equal(t, metricReq.Direction, direction)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.Start.Unix()), start)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.End.Unix()), end)
|
||||||
|
testMethod(t, r, http.MethodGet)
|
||||||
|
|
||||||
|
fmt.Fprintf(w, bandwidthRespJSON)
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsResp, _, err := client.Monitoring.GetDropletBandwidth(ctx, metricReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Monitoring.GetDropletBandwidthMetrics returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := &MetricsResponse{
|
||||||
|
Status: "success",
|
||||||
|
Data: MetricsData{
|
||||||
|
ResultType: "matrix",
|
||||||
|
Result: []metrics.SampleStream{
|
||||||
|
{
|
||||||
|
Metric: metrics.Metric{
|
||||||
|
"host_id": "222651441",
|
||||||
|
"direction": "inbound",
|
||||||
|
"interface": "private",
|
||||||
|
},
|
||||||
|
Values: []metrics.SamplePair{
|
||||||
|
{
|
||||||
|
Timestamp: 1634052360000,
|
||||||
|
Value: 0.016600450090265357,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1634052480000,
|
||||||
|
Value: 0.015085955677299055,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1634052600000,
|
||||||
|
Value: 0.014941163855322308,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timestamp: 1634052720000,
|
||||||
|
Value: 0.016214285714285712,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, expected, metricsResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDropletTotalMemory(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
defer teardown()
|
||||||
|
now := time.Now()
|
||||||
|
metricReq := &DropletMetricsRequest{
|
||||||
|
HostID: "123",
|
||||||
|
Start: now.Add(-300 * time.Second),
|
||||||
|
End: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
mux.HandleFunc("/v2/monitoring/metrics/droplet/memory_total", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hostID := r.URL.Query().Get("host_id")
|
||||||
|
start := r.URL.Query().Get("start")
|
||||||
|
end := r.URL.Query().Get("end")
|
||||||
|
|
||||||
|
assert.Equal(t, metricReq.HostID, hostID)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.Start.Unix()), start)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.End.Unix()), end)
|
||||||
|
testMethod(t, r, http.MethodGet)
|
||||||
|
|
||||||
|
fmt.Fprintf(w, memoryRespJSON)
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsResp, _, err := client.Monitoring.GetDropletTotalMemory(ctx, metricReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Monitoring.GetDropletTotalMemory returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, testMemoryResponse, metricsResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDropletFreeMemory(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
defer teardown()
|
||||||
|
now := time.Now()
|
||||||
|
metricReq := &DropletMetricsRequest{
|
||||||
|
HostID: "123",
|
||||||
|
Start: now.Add(-300 * time.Second),
|
||||||
|
End: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
mux.HandleFunc("/v2/monitoring/metrics/droplet/memory_free", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hostID := r.URL.Query().Get("host_id")
|
||||||
|
start := r.URL.Query().Get("start")
|
||||||
|
end := r.URL.Query().Get("end")
|
||||||
|
|
||||||
|
assert.Equal(t, metricReq.HostID, hostID)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.Start.Unix()), start)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.End.Unix()), end)
|
||||||
|
testMethod(t, r, http.MethodGet)
|
||||||
|
|
||||||
|
fmt.Fprintf(w, memoryRespJSON)
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsResp, _, err := client.Monitoring.GetDropletFreeMemory(ctx, metricReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Monitoring.GetDropletFreeMemory returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, testMemoryResponse, metricsResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDropletAvailableMemory(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
defer teardown()
|
||||||
|
now := time.Now()
|
||||||
|
metricReq := &DropletMetricsRequest{
|
||||||
|
HostID: "123",
|
||||||
|
Start: now.Add(-300 * time.Second),
|
||||||
|
End: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
mux.HandleFunc("/v2/monitoring/metrics/droplet/memory_available", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hostID := r.URL.Query().Get("host_id")
|
||||||
|
start := r.URL.Query().Get("start")
|
||||||
|
end := r.URL.Query().Get("end")
|
||||||
|
|
||||||
|
assert.Equal(t, metricReq.HostID, hostID)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.Start.Unix()), start)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.End.Unix()), end)
|
||||||
|
testMethod(t, r, http.MethodGet)
|
||||||
|
|
||||||
|
fmt.Fprintf(w, memoryRespJSON)
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsResp, _, err := client.Monitoring.GetDropletAvailableMemory(ctx, metricReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Monitoring.GetDropletAvailableMemory returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, testMemoryResponse, metricsResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDropletCachedMemory(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
defer teardown()
|
||||||
|
now := time.Now()
|
||||||
|
metricReq := &DropletMetricsRequest{
|
||||||
|
HostID: "123",
|
||||||
|
Start: now.Add(-300 * time.Second),
|
||||||
|
End: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
mux.HandleFunc("/v2/monitoring/metrics/droplet/memory_cached", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hostID := r.URL.Query().Get("host_id")
|
||||||
|
start := r.URL.Query().Get("start")
|
||||||
|
end := r.URL.Query().Get("end")
|
||||||
|
|
||||||
|
assert.Equal(t, metricReq.HostID, hostID)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.Start.Unix()), start)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.End.Unix()), end)
|
||||||
|
testMethod(t, r, http.MethodGet)
|
||||||
|
|
||||||
|
fmt.Fprintf(w, memoryRespJSON)
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsResp, _, err := client.Monitoring.GetDropletCachedMemory(ctx, metricReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Monitoring.GetDropletCachedMemory returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, testMemoryResponse, metricsResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDropletFilesystemFree(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
defer teardown()
|
||||||
|
now := time.Now()
|
||||||
|
metricReq := &DropletMetricsRequest{
|
||||||
|
HostID: "123",
|
||||||
|
Start: now.Add(-300 * time.Second),
|
||||||
|
End: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
mux.HandleFunc("/v2/monitoring/metrics/droplet/filesystem_free", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hostID := r.URL.Query().Get("host_id")
|
||||||
|
start := r.URL.Query().Get("start")
|
||||||
|
end := r.URL.Query().Get("end")
|
||||||
|
|
||||||
|
assert.Equal(t, metricReq.HostID, hostID)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.Start.Unix()), start)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.End.Unix()), end)
|
||||||
|
testMethod(t, r, http.MethodGet)
|
||||||
|
|
||||||
|
fmt.Fprintf(w, filesystemRespJSON)
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsResp, _, err := client.Monitoring.GetDropletFilesystemFree(ctx, metricReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Monitoring.GetDropletFilesystemFree returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, testFilesystemResponse, metricsResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDropletFilesystemSize(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
defer teardown()
|
||||||
|
now := time.Now()
|
||||||
|
metricReq := &DropletMetricsRequest{
|
||||||
|
HostID: "123",
|
||||||
|
Start: now.Add(-300 * time.Second),
|
||||||
|
End: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
mux.HandleFunc("/v2/monitoring/metrics/droplet/filesystem_size", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hostID := r.URL.Query().Get("host_id")
|
||||||
|
start := r.URL.Query().Get("start")
|
||||||
|
end := r.URL.Query().Get("end")
|
||||||
|
|
||||||
|
assert.Equal(t, metricReq.HostID, hostID)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.Start.Unix()), start)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.End.Unix()), end)
|
||||||
|
testMethod(t, r, http.MethodGet)
|
||||||
|
|
||||||
|
fmt.Fprintf(w, filesystemRespJSON)
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsResp, _, err := client.Monitoring.GetDropletFilesystemSize(ctx, metricReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Monitoring.GetDropletFilesystemSize returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, testFilesystemResponse, metricsResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDropletLoad1(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
defer teardown()
|
||||||
|
now := time.Now()
|
||||||
|
metricReq := &DropletMetricsRequest{
|
||||||
|
HostID: "123",
|
||||||
|
Start: now.Add(-300 * time.Second),
|
||||||
|
End: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
mux.HandleFunc("/v2/monitoring/metrics/droplet/load_1", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hostID := r.URL.Query().Get("host_id")
|
||||||
|
start := r.URL.Query().Get("start")
|
||||||
|
end := r.URL.Query().Get("end")
|
||||||
|
|
||||||
|
assert.Equal(t, metricReq.HostID, hostID)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.Start.Unix()), start)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.End.Unix()), end)
|
||||||
|
testMethod(t, r, http.MethodGet)
|
||||||
|
|
||||||
|
fmt.Fprintf(w, loadRespJSON)
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsResp, _, err := client.Monitoring.GetDropletLoad1(ctx, metricReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Monitoring.GetDropletLoad1 returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, testLoadResponse, metricsResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDropletLoad5(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
defer teardown()
|
||||||
|
now := time.Now()
|
||||||
|
metricReq := &DropletMetricsRequest{
|
||||||
|
HostID: "123",
|
||||||
|
Start: now.Add(-300 * time.Second),
|
||||||
|
End: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
mux.HandleFunc("/v2/monitoring/metrics/droplet/load_5", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hostID := r.URL.Query().Get("host_id")
|
||||||
|
start := r.URL.Query().Get("start")
|
||||||
|
end := r.URL.Query().Get("end")
|
||||||
|
|
||||||
|
assert.Equal(t, metricReq.HostID, hostID)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.Start.Unix()), start)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.End.Unix()), end)
|
||||||
|
testMethod(t, r, http.MethodGet)
|
||||||
|
|
||||||
|
fmt.Fprintf(w, loadRespJSON)
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsResp, _, err := client.Monitoring.GetDropletLoad5(ctx, metricReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Monitoring.GetDropletLoad5 returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, testLoadResponse, metricsResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDropletLoad15(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
defer teardown()
|
||||||
|
now := time.Now()
|
||||||
|
metricReq := &DropletMetricsRequest{
|
||||||
|
HostID: "123",
|
||||||
|
Start: now.Add(-300 * time.Second),
|
||||||
|
End: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
mux.HandleFunc("/v2/monitoring/metrics/droplet/load_15", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hostID := r.URL.Query().Get("host_id")
|
||||||
|
start := r.URL.Query().Get("start")
|
||||||
|
end := r.URL.Query().Get("end")
|
||||||
|
|
||||||
|
assert.Equal(t, metricReq.HostID, hostID)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.Start.Unix()), start)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.End.Unix()), end)
|
||||||
|
testMethod(t, r, http.MethodGet)
|
||||||
|
|
||||||
|
fmt.Fprintf(w, loadRespJSON)
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsResp, _, err := client.Monitoring.GetDropletLoad15(ctx, metricReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Monitoring.GetDropletLoad15 returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, testLoadResponse, metricsResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDropletCPU(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
defer teardown()
|
||||||
|
now := time.Now()
|
||||||
|
metricReq := &DropletMetricsRequest{
|
||||||
|
HostID: "123",
|
||||||
|
Start: now.Add(-300 * time.Second),
|
||||||
|
End: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
mux.HandleFunc("/v2/monitoring/metrics/droplet/cpu", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hostID := r.URL.Query().Get("host_id")
|
||||||
|
start := r.URL.Query().Get("start")
|
||||||
|
end := r.URL.Query().Get("end")
|
||||||
|
|
||||||
|
assert.Equal(t, metricReq.HostID, hostID)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.Start.Unix()), start)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%d", metricReq.End.Unix()), end)
|
||||||
|
testMethod(t, r, http.MethodGet)
|
||||||
|
|
||||||
|
fmt.Fprintf(w, cpuRespJSON)
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsResp, _, err := client.Monitoring.GetDropletCPU(ctx, metricReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Monitoring.GetDropletCPU returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, testCPUResponse, metricsResp)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue