Skip to content

Commit

Permalink
Merge pull request #35 from circonus-labs/CIRC-4388
Browse files Browse the repository at this point in the history
CIRC-4388: Support new IRONdb find parameters
  • Loading branch information
maier authored Jan 10, 2020
2 parents ed0cdc7 + fcdbb5f commit 8378ff1
Show file tree
Hide file tree
Showing 6 changed files with 283 additions and 20 deletions.
13 changes: 13 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ to [Semantic Versioning](http://semver.org/) rules.

## [Next Release]

### Added

- Added a new FindTagsLatest type and a new 'Latest' field to the `FindTagsItem`
type to support `SnowthClient.FindTags()` returning the latest data values when
requested from the IRONdb find call.

### Changed

- Changed the signature of the `SnowthClient.FindTags()` and
`SnowthClient.FindTagsContext()` methods to accept a `*FindTagsOptions`
argument. This argument contains the values for the supported IRONdb find
operation query parameters.

## [v1.4.4] - 2019-11-21

### Added
Expand Down
10 changes: 8 additions & 2 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ func TestSnowthClientRequest(t *testing.T) {
}

node := &SnowthNode{url: u}
res, err := sc.FindTags(node, 1, "test", "1", "1")
res, err := sc.FindTags(node, 1, "test", &FindTagsOptions{
Start: time.Unix(1, 0),
End: time.Unix(2, 0),
})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -169,7 +172,10 @@ func TestSnowthClientDiscoverNodesWatch(t *testing.T) {
currentTopology: "294cbd39999c2270964029691e8bc5e231a867d525ccba62181dc8988ff218dc",
}

res, err := sc.FindTags(node, 1, "test", "1", "1")
res, err := sc.FindTags(node, 1, "test", &FindTagsOptions{
Start: time.Unix(1, 0),
End: time.Unix(2, 0),
})
if err != nil {
t.Fatal(err)
}
Expand Down
16 changes: 16 additions & 0 deletions common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,22 @@ import (
"time"
)

func int64Ptr(i int64) *int64 {
return &i
}

func float64Ptr(f float64) *float64 {
return &f
}

func stringPtr(s string) *string {
return &s
}

func boolPtr(b bool) *bool {
return &b
}

type noOpReadCloser struct {
*bytes.Buffer
WasClosed bool
Expand Down
5 changes: 2 additions & 3 deletions examples/retrieval.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,8 @@ func ExampleReadText() {
log.Fatalf("failed to write text data: %v", err)
}

data, err := client.ReadTextValues(node,
time.Now().Add(-60*time.Second), time.Now().Add(60*time.Second),
id, "test-text-metric2")
data, err := client.ReadTextValues(node, id, "test-text-metric2",
time.Now().Add(-60*time.Second), time.Now().Add(60*time.Second))
if err != nil {
log.Fatalf("failed to read text data: %v", err)
}
Expand Down
191 changes: 178 additions & 13 deletions tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,26 @@ package gosnowth

import (
"context"
"encoding/json"
"fmt"
"net/url"
"strconv"
"time"

"github.com/pkg/errors"
)

// FindTagsItem values represent results returned from IRONdb tag queries.
type FindTagsItem struct {
UUID string `json:"uuid"`
CheckName string `json:"check_name"`
CheckTags []string `json:"check_tags,omitempty"`
MetricName string `json:"metric_name"`
Category string `json:"category"`
Type string `type:"type"`
AccountID int64 `json:"account_id"`
Activity [][]int64 `json:"activity,omitempty"`
UUID string `json:"uuid"`
CheckName string `json:"check_name"`
CheckTags []string `json:"check_tags,omitempty"`
MetricName string `json:"metric_name"`
Category string `json:"category"`
Type string `type:"type"`
AccountID int64 `json:"account_id"`
Activity [][]int64 `json:"activity,omitempty"`
Latest *FindTagsLatest `json:"latest,omitempty"`
}

// FindTagsResult values contain the results of a find tags request.
Expand All @@ -27,22 +30,184 @@ type FindTagsResult struct {
Count int64
}

// FindTagsOptions values contain optional parameters to be passed to the
// IRONdb find tags call by a FindTags operation.
type FindTagsOptions struct {
Start time.Time `json:"activity_start_secs"`
End time.Time `json:"activity_end_secs"`
Activity int64 `json:"activity"`
Latest int64 `json:"latest"`
CountOnly int64 `json:"count_only"`
}

// FindTagsLatest values contain the most recent data values for a metric.
type FindTagsLatest struct {
Numeric []FindTagsLatestNumeric `json:"numeric,omitempty"`
Text []FindTagsLatestText `json:"text,omitempty"`
Histogram []FindTagsLatestHistogram `json:"histogram,omitempty"`
}

// FindTagsLatestNumeric values contain recent metric numeric data.
type FindTagsLatestNumeric struct {
Time int64
Value *float64
}

// MarshalJSON encodes a FindTagsLatestNumeric value into a JSON format byte
// slice.
func (ftl *FindTagsLatestNumeric) MarshalJSON() ([]byte, error) {
v := []interface{}{ftl.Time, ftl.Value}
return json.Marshal(v)
}

// UnmarshalJSON decodes a JSON format byte slice into a FindTagsLatestNumeric
// value.
func (ftl *FindTagsLatestNumeric) UnmarshalJSON(b []byte) error {
v := []interface{}{}
err := json.Unmarshal(b, &v)
if err != nil {
return err
}

if len(v) != 2 {
return fmt.Errorf("unable to decode latest numeric value, "+
"invalid length: %v: %v", string(b), err)
}

if fv, ok := v[0].(float64); ok {
ftl.Time = int64(fv)
} else {
return fmt.Errorf("unable to decode latest numeric value, "+
"invalid timestamp: %v", string(b))
}

if v[1] != nil {
if fv, ok := v[1].(float64); ok {
ftl.Value = &fv
} else {
return fmt.Errorf("unable to decode latest numeric value, "+
"invalid value: %v", string(b))
}
}

return nil
}

// FindTagsLatestText values contain recent metric text data.
type FindTagsLatestText struct {
Time int64
Value *string
}

// MarshalJSON encodes a FindTagsLatestText value into a JSON format byte slice.
func (ftl *FindTagsLatestText) MarshalJSON() ([]byte, error) {
v := []interface{}{ftl.Time, ftl.Value}
return json.Marshal(v)
}

// UnmarshalJSON decodes a JSON format byte slice into a FindTagsLatestText
// value.
func (ftl *FindTagsLatestText) UnmarshalJSON(b []byte) error {
v := []interface{}{}
err := json.Unmarshal(b, &v)
if err != nil {
return err
}

if len(v) != 2 {
return fmt.Errorf("unable to decode latest text value, "+
"invalid length: %v: %v", string(b), err)
}

if fv, ok := v[0].(float64); ok {
ftl.Time = int64(fv)
} else {
return fmt.Errorf("unable to decode latest text value, "+
"invalid timestamp: %v", string(b))
}

if v[1] != nil {
if sv, ok := v[1].(string); ok {
ftl.Value = &sv
} else {
return fmt.Errorf("unable to decode latest text value, "+
"invalid value: %v", string(b))
}
}

return nil
}

// FindTagsLatestHistogram values contain recent metric histogram data.
type FindTagsLatestHistogram struct {
Time int64
Value *string
}

// MarshalJSON encodes a FindTagsLatestHistogram value into a JSON format byte
// slice.
func (ftl *FindTagsLatestHistogram) MarshalJSON() ([]byte, error) {
v := []interface{}{ftl.Time, ftl.Value}
return json.Marshal(v)
}

// UnmarshalJSON decodes a JSON format byte slice into a
// FindTagsLatestHistogram value.
func (ftl *FindTagsLatestHistogram) UnmarshalJSON(b []byte) error {
v := []interface{}{}
err := json.Unmarshal(b, &v)
if err != nil {
return err
}

if len(v) != 2 {
return fmt.Errorf("unable to decode latest histogram value, "+
"invalid length: %v: %v", string(b), err)
}

if fv, ok := v[0].(float64); ok {
ftl.Time = int64(fv)
} else {
return fmt.Errorf("unable to decode latest histogram value, "+
"invalid timestamp: %v", string(b))
}

if v[1] != nil {
if sv, ok := v[1].(string); ok {
ftl.Value = &sv
} else {
return fmt.Errorf("unable to decode latest histogram value, "+
"invalid value: %v", string(b))
}
}

return nil
}

// FindTags retrieves metrics that are associated with the provided tag query.
func (sc *SnowthClient) FindTags(node *SnowthNode, accountID int64,
query string, start, end string) (*FindTagsResult, error) {
query string, options *FindTagsOptions) (*FindTagsResult, error) {
return sc.FindTagsContext(context.Background(), node, accountID, query,
start, end)
options)
}

// FindTagsContext is the context aware version of FindTags.
func (sc *SnowthClient) FindTagsContext(ctx context.Context, node *SnowthNode,
accountID int64, query string, start, end string) (*FindTagsResult, error) {
accountID int64, query string,
options *FindTagsOptions) (*FindTagsResult, error) {
u := fmt.Sprintf("%s?query=%s",
sc.getURL(node, fmt.Sprintf("/find/%d/tags", accountID)),
url.QueryEscape(query))
if start != "" && end != "" {
if !options.Start.IsZero() && !options.End.IsZero() &&
options.Start.Unix() != 0 && options.End.Unix() != 0 {
u += fmt.Sprintf("&activity_start_secs=%s&activity_end_secs=%s",
url.QueryEscape(start), url.QueryEscape(end))
formatTimestamp(options.Start), formatTimestamp(options.End))
}

u += fmt.Sprintf("&activity=%d", options.Activity)
u += fmt.Sprintf("&latest=%d", options.Latest)
if options.CountOnly != 0 {
u += fmt.Sprintf("&count_only=%d", options.CountOnly)
}

r := &FindTagsResult{}
Expand Down
Loading

0 comments on commit 8378ff1

Please sign in to comment.