remove err struct
This commit is contained in:
parent
e7ff7d506b
commit
3457c98eb1
|
@ -82,52 +82,10 @@ const (
|
||||||
measurementJob = "jenkins_job"
|
measurementJob = "jenkins_job"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error base type of error.
|
func badFormatErr(url string, field interface{}, want string, fieldName string) error {
|
||||||
type Error struct {
|
return fmt.Errorf("error bad format[%s]: fieldName: %s, want %s, got %T", url, fieldName, want, field)
|
||||||
err error
|
|
||||||
reference string
|
|
||||||
url string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newError(err error, ref, url string) *Error {
|
|
||||||
return &Error{
|
|
||||||
err: err,
|
|
||||||
reference: ref,
|
|
||||||
url: url,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Error) Error() string {
|
|
||||||
if e == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("error %s[%s]: %v", e.reference, e.url, e.err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func badFormatErr(url string, field interface{}, want string, fieldName string) *Error {
|
|
||||||
return &Error{
|
|
||||||
err: fmt.Errorf("fieldName: %s, want %s, got %T", fieldName, want, field),
|
|
||||||
reference: errBadFormat,
|
|
||||||
url: url,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// err references
|
|
||||||
const (
|
|
||||||
errParseConfig = "parse jenkins config"
|
|
||||||
errJobFilterCompile = "compile job filters"
|
|
||||||
errNodeFilterCompile = "compile node filters"
|
|
||||||
errConnectJenkins = "connect jenkins instance"
|
|
||||||
errInitJenkins = "init jenkins instance"
|
|
||||||
errRetrieveNode = "retrieving nodes"
|
|
||||||
errRetrieveJobs = "retrieving jobs"
|
|
||||||
errEmptyNodeName = "empty node name"
|
|
||||||
errEmptyMonitorData = "empty monitor data"
|
|
||||||
errBadFormat = "bad format"
|
|
||||||
errRetrieveInnerJobs = "retrieving inner jobs"
|
|
||||||
errRetrieveLatestBuild = "retrieving latest build"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SampleConfig implements telegraf.Input interface
|
// SampleConfig implements telegraf.Input interface
|
||||||
func (j *Jenkins) SampleConfig() string {
|
func (j *Jenkins) SampleConfig() string {
|
||||||
return sampleConfig
|
return sampleConfig
|
||||||
|
@ -156,38 +114,39 @@ func (j *Jenkins) Gather(acc telegraf.Accumulator) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *Jenkins) initClient() (*http.Client, *Error) {
|
func (j *Jenkins) initClient() (*http.Client, error) {
|
||||||
tlsCfg, err := j.ClientConfig.TLSConfig()
|
tlsCfg, err := j.ClientConfig.TLSConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, newError(err, errParseConfig, j.URL)
|
return nil, fmt.Errorf("error parse jenkins config[%s]: %v", j.URL, err)
|
||||||
}
|
}
|
||||||
return &http.Client{
|
return &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
TLSClientConfig: tlsCfg,
|
TLSClientConfig: tlsCfg,
|
||||||
|
MaxIdleConns: j.MaxConnections,
|
||||||
},
|
},
|
||||||
Timeout: j.ResponseTimeout.Duration,
|
Timeout: j.ResponseTimeout.Duration,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// seperate the client as dependency to use httptest Client for mocking
|
// seperate the client as dependency to use httptest Client for mocking
|
||||||
func (j *Jenkins) newInstance(client *http.Client) *Error {
|
func (j *Jenkins) newInstance(client *http.Client) error {
|
||||||
// create instance
|
// create instance
|
||||||
var err error
|
var err error
|
||||||
j.instance, err = gojenkins.CreateJenkins(client, j.URL, j.Username, j.Password).Init()
|
j.instance, err = gojenkins.CreateJenkins(client, j.URL, j.Username, j.Password).Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newError(err, errConnectJenkins, j.URL)
|
return fmt.Errorf("error connect jenkins instance[%s]: %v", j.URL, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// init job filter
|
// init job filter
|
||||||
j.jobFilter, err = filter.Compile(j.JobExclude)
|
j.jobFilter, err = filter.Compile(j.JobExclude)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newError(err, errJobFilterCompile, j.URL)
|
return fmt.Errorf("error compile job filters[%s]: %v", j.URL, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// init node filter
|
// init node filter
|
||||||
j.nodeFilter, err = filter.Compile(j.NodeExclude)
|
j.nodeFilter, err = filter.Compile(j.NodeExclude)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newError(err, errNodeFilterCompile, j.URL)
|
return fmt.Errorf("error compile node filters[%s]: %v", j.URL, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// init tcp pool with default value
|
// init tcp pool with default value
|
||||||
|
@ -203,7 +162,7 @@ func (j *Jenkins) newInstance(client *http.Client) *Error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *Jenkins) gatherNodeData(node *gojenkins.Node, url string, acc telegraf.Accumulator) *Error {
|
func (j *Jenkins) gatherNodeData(node *gojenkins.Node, url string, acc telegraf.Accumulator) error {
|
||||||
tags := map[string]string{}
|
tags := map[string]string{}
|
||||||
fields := make(map[string]interface{})
|
fields := make(map[string]interface{})
|
||||||
|
|
||||||
|
@ -211,7 +170,7 @@ func (j *Jenkins) gatherNodeData(node *gojenkins.Node, url string, acc telegraf.
|
||||||
|
|
||||||
// detect the parsing error, since gojenkins lib won't do it
|
// detect the parsing error, since gojenkins lib won't do it
|
||||||
if info == nil || info.DisplayName == "" {
|
if info == nil || info.DisplayName == "" {
|
||||||
return newError(nil, errEmptyNodeName, url)
|
return fmt.Errorf("error empty node name[%s]: ", j.URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
tags["node_name"] = info.DisplayName
|
tags["node_name"] = info.DisplayName
|
||||||
|
@ -222,7 +181,7 @@ func (j *Jenkins) gatherNodeData(node *gojenkins.Node, url string, acc telegraf.
|
||||||
}
|
}
|
||||||
|
|
||||||
if info.MonitorData.Hudson_NodeMonitors_ArchitectureMonitor == nil {
|
if info.MonitorData.Hudson_NodeMonitors_ArchitectureMonitor == nil {
|
||||||
return newError(fmt.Errorf("maybe check your permission"), errEmptyMonitorData, url)
|
return fmt.Errorf("error empty monitor data[%s]: ", j.URL)
|
||||||
}
|
}
|
||||||
tags["arch"], ok = info.MonitorData.Hudson_NodeMonitors_ArchitectureMonitor.(string)
|
tags["arch"], ok = info.MonitorData.Hudson_NodeMonitors_ArchitectureMonitor.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -292,7 +251,7 @@ func (j *Jenkins) gatherNodesData(acc telegraf.Accumulator) {
|
||||||
// since gojenkins lib will never return error
|
// since gojenkins lib will never return error
|
||||||
// returns error for len(nodes) is 0
|
// returns error for len(nodes) is 0
|
||||||
if err != nil || len(nodes) == 0 {
|
if err != nil || len(nodes) == 0 {
|
||||||
acc.AddError(newError(err, errRetrieveNode, url))
|
acc.AddError(fmt.Errorf("error retrieving nodes[%s]: %v", j.URL, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// get node data
|
// get node data
|
||||||
|
@ -308,7 +267,7 @@ func (j *Jenkins) gatherNodesData(acc telegraf.Accumulator) {
|
||||||
func (j *Jenkins) gatherJobs(acc telegraf.Accumulator) {
|
func (j *Jenkins) gatherJobs(acc telegraf.Accumulator) {
|
||||||
jobs, err := j.instance.GetAllJobNames()
|
jobs, err := j.instance.GetAllJobNames()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
acc.AddError(newError(err, errRetrieveJobs, j.URL))
|
acc.AddError(fmt.Errorf("error retrieving jobs[%s]: %v", j.URL, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
jobsC := make(chan jobRequest, j.MaxConnections)
|
jobsC := make(chan jobRequest, j.MaxConnections)
|
||||||
|
@ -336,7 +295,7 @@ func (j *Jenkins) gatherJobs(acc telegraf.Accumulator) {
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *Jenkins) getJobDetail(sj jobRequest, jobsC chan<- jobRequest, wg *sync.WaitGroup, acc telegraf.Accumulator) *Error {
|
func (j *Jenkins) getJobDetail(sj jobRequest, jobsC chan<- jobRequest, wg *sync.WaitGroup, acc telegraf.Accumulator) error {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
if j.MaxSubJobDepth > 0 && sj.layer == j.MaxSubJobDepth {
|
if j.MaxSubJobDepth > 0 && sj.layer == j.MaxSubJobDepth {
|
||||||
return nil
|
return nil
|
||||||
|
@ -348,7 +307,7 @@ func (j *Jenkins) getJobDetail(sj jobRequest, jobsC chan<- jobRequest, wg *sync.
|
||||||
url := j.URL + "/job/" + strings.Join(sj.combined(), "/job/") + "/api/json"
|
url := j.URL + "/job/" + strings.Join(sj.combined(), "/job/") + "/api/json"
|
||||||
jobDetail, err := j.instance.GetJob(sj.name, sj.parents...)
|
jobDetail, err := j.instance.GetJob(sj.name, sj.parents...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newError(err, errRetrieveInnerJobs, url)
|
return fmt.Errorf("error retrieving inner jobs[%s]: ", url)
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, innerJob := range jobDetail.Raw.Jobs {
|
for k, innerJob := range jobDetail.Raw.Jobs {
|
||||||
|
@ -385,7 +344,7 @@ func (j *Jenkins) getJobDetail(sj jobRequest, jobsC chan<- jobRequest, wg *sync.
|
||||||
if err == nil && status != 200 {
|
if err == nil && status != 200 {
|
||||||
err = fmt.Errorf("status code %d", status)
|
err = fmt.Errorf("status code %d", status)
|
||||||
}
|
}
|
||||||
return newError(err, errRetrieveLatestBuild, j.URL+baseURL+"/api/json")
|
return fmt.Errorf("error retrieving inner jobs[%s]: %v", j.URL+baseURL+"/api/json", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if build.Raw.Building {
|
if build.Raw.Building {
|
||||||
|
|
|
@ -2,7 +2,6 @@ package jenkins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -15,40 +14,6 @@ import (
|
||||||
"github.com/kelwang/gojenkins"
|
"github.com/kelwang/gojenkins"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestErr(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
err *Error
|
|
||||||
output string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
nil,
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
&Error{
|
|
||||||
reference: errConnectJenkins,
|
|
||||||
url: "http://badurl.com",
|
|
||||||
err: errors.New("unknown error"),
|
|
||||||
},
|
|
||||||
"error connect jenkins instance[http://badurl.com]: unknown error",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
newError(errors.New("2"), errEmptyMonitorData, "http://badurl.com"),
|
|
||||||
"error empty monitor data[http://badurl.com]: 2",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
badFormatErr("http://badurl.com", 20.12, "string", "arch"),
|
|
||||||
"error bad format[http://badurl.com]: fieldName: arch, want string, got float64",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
output := test.err.Error()
|
|
||||||
if output != test.output {
|
|
||||||
t.Errorf("Expected %s, got %s\n", test.output, output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestJobRequest(t *testing.T) {
|
func TestJobRequest(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input jobRequest
|
input jobRequest
|
||||||
|
@ -129,10 +94,10 @@ type monitorData struct {
|
||||||
|
|
||||||
func TestGatherNodeData(t *testing.T) {
|
func TestGatherNodeData(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
input mockHandler
|
input mockHandler
|
||||||
output *testutil.Accumulator
|
output *testutil.Accumulator
|
||||||
oe *Error
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "bad endpoint",
|
name: "bad endpoint",
|
||||||
|
@ -142,9 +107,7 @@ func TestGatherNodeData(t *testing.T) {
|
||||||
"/computer/api/json": nil,
|
"/computer/api/json": nil,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
oe: &Error{
|
wantErr: true,
|
||||||
reference: errRetrieveNode,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "bad node data",
|
name: "bad node data",
|
||||||
|
@ -160,9 +123,7 @@ func TestGatherNodeData(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
oe: &Error{
|
wantErr: true,
|
||||||
reference: errEmptyNodeName,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "bad empty monitor data",
|
name: "bad empty monitor data",
|
||||||
|
@ -177,9 +138,7 @@ func TestGatherNodeData(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
oe: &Error{
|
wantErr: true,
|
||||||
reference: errEmptyMonitorData,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "bad monitor data format",
|
name: "bad monitor data format",
|
||||||
|
@ -195,9 +154,7 @@ func TestGatherNodeData(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
oe: &Error{
|
wantErr: true,
|
||||||
reference: errBadFormat,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "filtered nodes",
|
name: "filtered nodes",
|
||||||
|
@ -286,22 +243,13 @@ func TestGatherNodeData(t *testing.T) {
|
||||||
acc := new(testutil.Accumulator)
|
acc := new(testutil.Accumulator)
|
||||||
j.gatherNodesData(acc)
|
j.gatherNodesData(acc)
|
||||||
if err := acc.FirstError(); err != nil {
|
if err := acc.FirstError(); err != nil {
|
||||||
te = err.(*Error)
|
te = err
|
||||||
}
|
}
|
||||||
|
|
||||||
if test.oe == nil && te != nil {
|
if !test.wantErr && te != nil {
|
||||||
t.Fatalf("%s: failed %s, expected to be nil", test.name, te.Error())
|
t.Fatalf("%s: failed %s, expected to be nil", test.name, te.Error())
|
||||||
} else if test.oe != nil {
|
} else if test.wantErr && te == nil {
|
||||||
test.oe.url = ts.URL + "/computer/api/json"
|
t.Fatalf("%s: expected err, got nil", test.name)
|
||||||
if te == nil {
|
|
||||||
t.Fatalf("%s: want err: %s, got nil", test.name, test.oe.Error())
|
|
||||||
}
|
|
||||||
if test.oe.reference != te.reference {
|
|
||||||
t.Fatalf("%s: bad error msg Expected %s, got %s\n", test.name, test.oe.reference, te.reference)
|
|
||||||
}
|
|
||||||
if test.oe.url != te.url {
|
|
||||||
t.Fatalf("%s: bad error url Expected %s, got %s\n", test.name, test.oe.url, te.url)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if test.output == nil && len(acc.Metrics) > 0 {
|
if test.output == nil && len(acc.Metrics) > 0 {
|
||||||
t.Fatalf("%s: collected extra data", test.name)
|
t.Fatalf("%s: collected extra data", test.name)
|
||||||
|
@ -331,10 +279,10 @@ func TestNewInstance(t *testing.T) {
|
||||||
mockClient := &http.Client{Transport: &http.Transport{}}
|
mockClient := &http.Client{Transport: &http.Transport{}}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
// name of the test
|
// name of the test
|
||||||
name string
|
name string
|
||||||
input *Jenkins
|
input *Jenkins
|
||||||
output *Jenkins
|
output *Jenkins
|
||||||
oe *Error
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "bad jenkins config",
|
name: "bad jenkins config",
|
||||||
|
@ -342,10 +290,7 @@ func TestNewInstance(t *testing.T) {
|
||||||
URL: "http://a bad url",
|
URL: "http://a bad url",
|
||||||
ResponseTimeout: internal.Duration{Duration: time.Microsecond},
|
ResponseTimeout: internal.Duration{Duration: time.Microsecond},
|
||||||
},
|
},
|
||||||
oe: &Error{
|
wantErr: true,
|
||||||
url: "http://a bad url",
|
|
||||||
reference: errConnectJenkins,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "has filter",
|
name: "has filter",
|
||||||
|
@ -370,15 +315,10 @@ func TestNewInstance(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
te := test.input.newInstance(mockClient)
|
te := test.input.newInstance(mockClient)
|
||||||
if test.oe == nil && te != nil {
|
if !test.wantErr && te != nil {
|
||||||
t.Fatalf("%s: failed %s, expected to be nil", test.name, te.Error())
|
t.Fatalf("%s: failed %s, expected to be nil", test.name, te.Error())
|
||||||
} else if test.oe != nil {
|
} else if test.wantErr && te == nil {
|
||||||
if test.oe.reference != te.reference {
|
t.Fatalf("%s: expected err, got nil", test.name)
|
||||||
t.Fatalf("%s: bad error msg Expected %s, got %s\n", test.name, test.oe.reference, te.reference)
|
|
||||||
}
|
|
||||||
if test.oe.url != te.url {
|
|
||||||
t.Fatalf("%s: bad error url Expected %s, got %s\n", test.name, test.oe.url, te.url)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if test.output != nil {
|
if test.output != nil {
|
||||||
if test.input.instance == nil {
|
if test.input.instance == nil {
|
||||||
|
@ -394,10 +334,10 @@ func TestNewInstance(t *testing.T) {
|
||||||
|
|
||||||
func TestGatherJobs(t *testing.T) {
|
func TestGatherJobs(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
input mockHandler
|
input mockHandler
|
||||||
output *testutil.Accumulator
|
output *testutil.Accumulator
|
||||||
oe *Error
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "empty job",
|
name: "empty job",
|
||||||
|
@ -414,10 +354,7 @@ func TestGatherJobs(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
oe: &Error{
|
wantErr: true,
|
||||||
reference: errRetrieveInnerJobs,
|
|
||||||
url: "/job/job1/api/json",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "jobs has no build",
|
name: "jobs has no build",
|
||||||
|
@ -448,10 +385,7 @@ func TestGatherJobs(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
oe: &Error{
|
wantErr: true,
|
||||||
url: "/job/job1/1/api/json",
|
|
||||||
reference: errRetrieveLatestBuild,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ignore building job",
|
name: "ignore building job",
|
||||||
|
@ -671,22 +605,14 @@ func TestGatherJobs(t *testing.T) {
|
||||||
acc := new(testutil.Accumulator)
|
acc := new(testutil.Accumulator)
|
||||||
j.gatherJobs(acc)
|
j.gatherJobs(acc)
|
||||||
if err := acc.FirstError(); err != nil {
|
if err := acc.FirstError(); err != nil {
|
||||||
te = err.(*Error)
|
te = err
|
||||||
}
|
}
|
||||||
if test.oe == nil && te != nil {
|
if !test.wantErr && te != nil {
|
||||||
t.Fatalf("%s: failed %s, expected to be nil", test.name, te.Error())
|
t.Fatalf("%s: failed %s, expected to be nil", test.name, te.Error())
|
||||||
} else if test.oe != nil {
|
} else if test.wantErr && te == nil {
|
||||||
test.oe.url = ts.URL + test.oe.url
|
t.Fatalf("%s: expected err, got nil", test.name)
|
||||||
if te == nil {
|
|
||||||
t.Fatalf("%s: want err: %s, got nil", test.name, test.oe.Error())
|
|
||||||
}
|
|
||||||
if test.oe.reference != te.reference {
|
|
||||||
t.Fatalf("%s: bad error msg Expected %s, got %s\n", test.name, test.oe.reference, te.reference)
|
|
||||||
}
|
|
||||||
if test.oe.url != te.url {
|
|
||||||
t.Fatalf("%s: bad error url Expected %s, got %s\n", test.name, test.oe.url, te.url)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if test.output != nil && len(test.output.Metrics) > 0 {
|
if test.output != nil && len(test.output.Metrics) > 0 {
|
||||||
// sort metrics
|
// sort metrics
|
||||||
sort.Slice(acc.Metrics, func(i, j int) bool {
|
sort.Slice(acc.Metrics, func(i, j int) bool {
|
||||||
|
|
Loading…
Reference in New Issue