From 03a2e213dac5354213add5fa8bca262a6afa6b15 Mon Sep 17 00:00:00 2001 From: Chris Grindstaff Date: Mon, 27 Mar 2023 10:39:02 -0400 Subject: [PATCH] feat: bin/rest should be able to query all clusters (#1866) * feat: bin/rest should be able to query all clusters feat: bin/rest should print the equivalent curl commands feat: bin/rest should collect cluster name, version, and poll time feat: bin/rest should have a cli option to set timeout docs: reformat bin/rest examples and include jq example * feat: bin/rest should be able to query all clusters feat: bin/rest should print the equivalent curl commands feat: bin/rest should collect cluster name, version, and poll time feat: bin/rest should have a cli option to set timeout docs: reformat bin/rest examples and include jq example --- .../rest/plugins/certificate/certificate.go | 2 +- cmd/collectors/rest/plugins/qtree/qtree.go | 2 +- .../securityaccount/securityaccount.go | 2 +- .../rest/plugins/snapmirror/snapmirror.go | 3 +- cmd/collectors/rest/plugins/volume/volume.go | 2 +- .../volumeanalytics/volumeanalytics.go | 2 +- cmd/collectors/rest/rest.go | 7 +- cmd/collectors/restperf/plugins/disk/disk.go | 2 +- .../restperf/plugins/volumetag/volumetag.go | 2 +- .../storagegrid/plugins/bucket/bucket.go | 2 +- .../storagegrid/plugins/joinrest/joinrest.go | 2 +- cmd/collectors/storagegrid/rest/client.go | 15 +- cmd/collectors/storagegrid/storagegrid.go | 2 +- cmd/collectors/zapi/collector/zapi.go | 2 +- .../zapi/plugins/certificate/certificate.go | 2 +- cmd/collectors/zapi/plugins/qtree/qtree.go | 2 +- .../zapi/plugins/security/security.go | 2 +- .../zapi/plugins/sensor/sensor_test.go | 2 +- cmd/collectors/zapi/plugins/shelf/shelf.go | 2 +- .../zapi/plugins/snapmirror/snapmirror.go | 2 +- cmd/collectors/zapi/plugins/svm/svm.go | 2 +- cmd/collectors/zapi/plugins/volume/volume.go | 2 +- cmd/collectors/zapiperf/plugins/disk/disk.go | 2 +- .../zapiperf/plugins/volumetag/volumetag.go | 2 +- cmd/poller/collector/collector.go | 19 +- .../plugin/aggregator/aggregator_test.go | 2 +- .../plugin/labelagent/label_agent_test.go | 2 +- .../plugin/metricagent/metric_agent_test.go | 2 +- cmd/poller/plugin/plugin.go | 35 +-- cmd/poller/plugin/test/plugin_test.go | 4 +- cmd/poller/poller.go | 15 +- cmd/tools/generate/generate.go | 7 +- cmd/tools/rest/client.go | 21 +- cmd/tools/rest/rest.go | 205 +++++++++++++----- cmd/tools/zapi/zapi.go | 5 +- pkg/api/ontapi/zapi/client.go | 10 +- pkg/api/ontapi/zapi/client_test.go | 3 +- pkg/auth/auth.go | 33 +-- 38 files changed, 260 insertions(+), 170 deletions(-) diff --git a/cmd/collectors/rest/plugins/certificate/certificate.go b/cmd/collectors/rest/plugins/certificate/certificate.go index 04404436d..fd6b9acd6 100644 --- a/cmd/collectors/rest/plugins/certificate/certificate.go +++ b/cmd/collectors/rest/plugins/certificate/certificate.go @@ -36,7 +36,7 @@ func (my *Certificate) Init() error { } timeout, _ := time.ParseDuration(rest.DefaultTimeout) - if my.client, err = rest.New(conf.ZapiPoller(my.ParentParams), timeout); err != nil { + if my.client, err = rest.New(conf.ZapiPoller(my.ParentParams), timeout, my.Auth); err != nil { my.Logger.Error().Stack().Err(err).Msg("connecting") return err } diff --git a/cmd/collectors/rest/plugins/qtree/qtree.go b/cmd/collectors/rest/plugins/qtree/qtree.go index e9a27b51f..7d19d7086 100644 --- a/cmd/collectors/rest/plugins/qtree/qtree.go +++ b/cmd/collectors/rest/plugins/qtree/qtree.go @@ -65,7 +65,7 @@ func (my *Qtree) Init() error { } else { my.Logger.Info().Str("timeout", timeout.String()).Msg("Using default timeout") } - if my.client, err = rest.New(conf.ZapiPoller(my.ParentParams), timeout); err != nil { + if my.client, err = rest.New(conf.ZapiPoller(my.ParentParams), timeout, my.Auth); err != nil { my.Logger.Error().Stack().Err(err).Msg("connecting") return err } diff --git a/cmd/collectors/rest/plugins/securityaccount/securityaccount.go b/cmd/collectors/rest/plugins/securityaccount/securityaccount.go index 345e0f6ba..63882bbc3 100644 --- a/cmd/collectors/rest/plugins/securityaccount/securityaccount.go +++ b/cmd/collectors/rest/plugins/securityaccount/securityaccount.go @@ -40,7 +40,7 @@ func (s *SecurityAccount) Init() error { } else { s.Logger.Info().Str("timeout", timeout.String()).Msg("Using default timeout") } - if s.client, err = rest.New(conf.ZapiPoller(s.ParentParams), timeout); err != nil { + if s.client, err = rest.New(conf.ZapiPoller(s.ParentParams), timeout, s.Auth); err != nil { s.Logger.Error().Stack().Err(err).Msg("connecting") return err } diff --git a/cmd/collectors/rest/plugins/snapmirror/snapmirror.go b/cmd/collectors/rest/plugins/snapmirror/snapmirror.go index 717dbfdd5..245096975 100644 --- a/cmd/collectors/rest/plugins/snapmirror/snapmirror.go +++ b/cmd/collectors/rest/plugins/snapmirror/snapmirror.go @@ -1,6 +1,7 @@ /* * Copyright NetApp Inc, 2022 All rights reserved */ + package snapmirror import ( @@ -42,7 +43,7 @@ func (my *SnapMirror) Init() error { } timeout, _ := time.ParseDuration(rest.DefaultTimeout) - if my.client, err = rest.New(conf.ZapiPoller(my.ParentParams), timeout); err != nil { + if my.client, err = rest.New(conf.ZapiPoller(my.ParentParams), timeout, my.Auth); err != nil { my.Logger.Error().Stack().Err(err).Msg("connecting") return err } diff --git a/cmd/collectors/rest/plugins/volume/volume.go b/cmd/collectors/rest/plugins/volume/volume.go index fc513c25d..a8e7ebd8b 100644 --- a/cmd/collectors/rest/plugins/volume/volume.go +++ b/cmd/collectors/rest/plugins/volume/volume.go @@ -37,7 +37,7 @@ func (my *Volume) Init() error { } timeout, _ := time.ParseDuration(rest.DefaultTimeout) - if my.client, err = rest.New(conf.ZapiPoller(my.ParentParams), timeout); err != nil { + if my.client, err = rest.New(conf.ZapiPoller(my.ParentParams), timeout, my.Auth); err != nil { my.Logger.Error().Stack().Err(err).Msg("connecting") return err } diff --git a/cmd/collectors/rest/plugins/volumeanalytics/volumeanalytics.go b/cmd/collectors/rest/plugins/volumeanalytics/volumeanalytics.go index d589a0f1c..c23549f08 100644 --- a/cmd/collectors/rest/plugins/volumeanalytics/volumeanalytics.go +++ b/cmd/collectors/rest/plugins/volumeanalytics/volumeanalytics.go @@ -54,7 +54,7 @@ func (v *VolumeAnalytics) Init() error { } timeout, _ := time.ParseDuration(rest.DefaultTimeout) - if v.client, err = rest.New(conf.ZapiPoller(v.ParentParams), timeout); err != nil { + if v.client, err = rest.New(conf.ZapiPoller(v.ParentParams), timeout, v.Auth); err != nil { v.Logger.Error().Stack().Err(err).Msg("connecting") return err } diff --git a/cmd/collectors/rest/rest.go b/cmd/collectors/rest/rest.go index 9cb177613..056784a3d 100644 --- a/cmd/collectors/rest/rest.go +++ b/cmd/collectors/rest/rest.go @@ -17,6 +17,7 @@ import ( "github.com/netapp/harvest/v2/cmd/poller/collector" "github.com/netapp/harvest/v2/cmd/poller/plugin" "github.com/netapp/harvest/v2/cmd/tools/rest" + "github.com/netapp/harvest/v2/pkg/auth" "github.com/netapp/harvest/v2/pkg/conf" "github.com/netapp/harvest/v2/pkg/errs" "github.com/netapp/harvest/v2/pkg/matrix" @@ -148,7 +149,7 @@ func (r *Rest) InitClient() error { var err error a := r.AbstractCollector - if r.Client, err = r.getClient(a); err != nil { + if r.Client, err = r.getClient(a, r.Auth); err != nil { return err } @@ -176,7 +177,7 @@ func (r *Rest) InitMatrix() error { return nil } -func (r *Rest) getClient(a *collector.AbstractCollector) (*rest.Client, error) { +func (r *Rest) getClient(a *collector.AbstractCollector, c *auth.Credentials) (*rest.Client, error) { var ( poller *conf.Poller err error @@ -193,7 +194,7 @@ func (r *Rest) getClient(a *collector.AbstractCollector) (*rest.Client, error) { return nil, errs.New(errs.ErrMissingParam, "addr") } timeout, _ := time.ParseDuration(rest.DefaultTimeout) - if client, err = rest.New(poller, timeout); err != nil { + if client, err = rest.New(poller, timeout, c); err != nil { r.Logger.Error().Err(err).Str("poller", opt.Poller).Msg("error creating new client") os.Exit(1) } diff --git a/cmd/collectors/restperf/plugins/disk/disk.go b/cmd/collectors/restperf/plugins/disk/disk.go index 71d42fb9a..1eac7a8fd 100644 --- a/cmd/collectors/restperf/plugins/disk/disk.go +++ b/cmd/collectors/restperf/plugins/disk/disk.go @@ -155,7 +155,7 @@ func (d *Disk) Init() error { } timeout, _ := time.ParseDuration(rest.DefaultTimeout) - if d.client, err = rest.New(conf.ZapiPoller(d.ParentParams), timeout); err != nil { + if d.client, err = rest.New(conf.ZapiPoller(d.ParentParams), timeout, d.Auth); err != nil { d.Logger.Error().Stack().Err(err).Msg("connecting") return err } diff --git a/cmd/collectors/restperf/plugins/volumetag/volumetag.go b/cmd/collectors/restperf/plugins/volumetag/volumetag.go index a4ba72d70..e91b7f58a 100644 --- a/cmd/collectors/restperf/plugins/volumetag/volumetag.go +++ b/cmd/collectors/restperf/plugins/volumetag/volumetag.go @@ -24,7 +24,7 @@ func (v *VolumeTag) Init() error { } timeout, _ := time.ParseDuration(rest.DefaultTimeout) - if v.client, err = rest.New(conf.ZapiPoller(v.ParentParams), timeout); err != nil { + if v.client, err = rest.New(conf.ZapiPoller(v.ParentParams), timeout, v.Auth); err != nil { v.Logger.Error().Stack().Err(err).Msg("connecting") return err } diff --git a/cmd/collectors/storagegrid/plugins/bucket/bucket.go b/cmd/collectors/storagegrid/plugins/bucket/bucket.go index 21dc9c0f0..8ef4dc2ab 100644 --- a/cmd/collectors/storagegrid/plugins/bucket/bucket.go +++ b/cmd/collectors/storagegrid/plugins/bucket/bucket.go @@ -26,7 +26,7 @@ func (b *Bucket) Init() error { } clientTimeout := b.ParentParams.GetChildContentS("client_timeout") - if b.client, err = rest.NewClient(b.Options.Poller, clientTimeout); err != nil { + if b.client, err = rest.NewClient(b.Options.Poller, clientTimeout, b.Auth); err != nil { b.Logger.Error().Stack().Err(err).Msg("connecting") return err } diff --git a/cmd/collectors/storagegrid/plugins/joinrest/joinrest.go b/cmd/collectors/storagegrid/plugins/joinrest/joinrest.go index 47c7ca616..cebe8340c 100644 --- a/cmd/collectors/storagegrid/plugins/joinrest/joinrest.go +++ b/cmd/collectors/storagegrid/plugins/joinrest/joinrest.go @@ -155,7 +155,7 @@ func (t *JoinRest) updateCache(model join, bytes *[]byte) { func (t *JoinRest) initClient() error { var err error - if t.client, err = rest.NewClient(t.Options.Poller, t.Params.GetChildContentS("client_timeout")); err != nil { + if t.client, err = rest.NewClient(t.Options.Poller, t.Params.GetChildContentS("client_timeout"), t.Auth); err != nil { return err } diff --git a/cmd/collectors/storagegrid/rest/client.go b/cmd/collectors/storagegrid/rest/client.go index 5436a2c8a..5ce0ec232 100644 --- a/cmd/collectors/storagegrid/rest/client.go +++ b/cmd/collectors/storagegrid/rest/client.go @@ -37,6 +37,7 @@ type Client struct { Timeout time.Duration logRest bool // used to log Rest request/response APIPath string + auth *auth.Credentials } type Cluster struct { @@ -46,7 +47,7 @@ type Cluster struct { Version [3]int } -func NewClient(pollerName string, clientTimeout string) (*Client, error) { +func NewClient(pollerName string, clientTimeout string, c *auth.Credentials) (*Client, error) { var ( poller *conf.Poller err error @@ -65,14 +66,14 @@ func NewClient(pollerName string, clientTimeout string) (*Client, error) { if err != nil { timeout, _ = time.ParseDuration(DefaultTimeout) } - if client, err = New(poller, timeout); err != nil { + if client, err = New(poller, timeout, c); err != nil { return nil, fmt.Errorf("uanble to create poller [%s]. err: %w", pollerName, err) } return client, err } -func New(poller *conf.Poller, timeout time.Duration) (*Client, error) { +func New(poller *conf.Poller, timeout time.Duration, c *auth.Credentials) (*Client, error) { var ( client Client httpclient *http.Client @@ -84,7 +85,9 @@ func New(poller *conf.Poller, timeout time.Duration) (*Client, error) { err error ) - client = Client{} + client = Client{ + auth: c, + } client.Logger = logging.Get().SubLogger("StorageGrid", "Client") if addr = poller.Addr; addr == "" { @@ -135,7 +138,7 @@ func New(poller *conf.Poller, timeout time.Duration) (*Client, error) { } } else { username := poller.Username - password := auth.Get().Password() + password := c.Password() client.username = username if username == "" { return nil, errs.New(errs.ErrMissingParam, "username") @@ -386,7 +389,7 @@ func (c *Client) fetchToken() error { } authB := authBody{ Username: c.username, - Password: auth.Get().Password(), + Password: c.auth.Password(), } postBody, err := json.Marshal(authB) if err != nil { diff --git a/cmd/collectors/storagegrid/storagegrid.go b/cmd/collectors/storagegrid/storagegrid.go index 8e40b34cc..56c9cfbec 100644 --- a/cmd/collectors/storagegrid/storagegrid.go +++ b/cmd/collectors/storagegrid/storagegrid.go @@ -398,7 +398,7 @@ func (s *StorageGrid) handleResults(result []gjson.Result) uint64 { func (s *StorageGrid) initClient() error { var err error - if s.client, err = srest.NewClient(s.Options.Poller, s.Params.GetChildContentS("client_timeout")); err != nil { + if s.client, err = srest.NewClient(s.Options.Poller, s.Params.GetChildContentS("client_timeout"), s.Auth); err != nil { return err } diff --git a/cmd/collectors/zapi/collector/zapi.go b/cmd/collectors/zapi/collector/zapi.go index cd770e95a..693ed7d55 100644 --- a/cmd/collectors/zapi/collector/zapi.go +++ b/cmd/collectors/zapi/collector/zapi.go @@ -87,7 +87,7 @@ func (z *Zapi) InitVars() error { var err error - if z.Client, err = client.New(conf.ZapiPoller(z.Params)); err != nil { // convert to connection error, so poller aborts + if z.Client, err = client.New(conf.ZapiPoller(z.Params), z.Auth); err != nil { // convert to connection error, so poller aborts return errs.New(errs.ErrConnection, err.Error()) } z.Client.TraceLogSet(z.Name, z.Params) diff --git a/cmd/collectors/zapi/plugins/certificate/certificate.go b/cmd/collectors/zapi/plugins/certificate/certificate.go index b848d82cb..1c819f984 100644 --- a/cmd/collectors/zapi/plugins/certificate/certificate.go +++ b/cmd/collectors/zapi/plugins/certificate/certificate.go @@ -39,7 +39,7 @@ func (my *Certificate) Init() error { return err } - if my.client, err = zapi.New(conf.ZapiPoller(my.ParentParams)); err != nil { + if my.client, err = zapi.New(conf.ZapiPoller(my.ParentParams), my.Auth); err != nil { my.Logger.Error().Stack().Err(err).Msg("connecting") return err } diff --git a/cmd/collectors/zapi/plugins/qtree/qtree.go b/cmd/collectors/zapi/plugins/qtree/qtree.go index f1c86b235..9e8fe3752 100644 --- a/cmd/collectors/zapi/plugins/qtree/qtree.go +++ b/cmd/collectors/zapi/plugins/qtree/qtree.go @@ -43,7 +43,7 @@ func (my *Qtree) Init() error { return err } - if my.client, err = zapi.New(conf.ZapiPoller(my.ParentParams)); err != nil { + if my.client, err = zapi.New(conf.ZapiPoller(my.ParentParams), my.Auth); err != nil { my.Logger.Error().Stack().Err(err).Msg("connecting") return err } diff --git a/cmd/collectors/zapi/plugins/security/security.go b/cmd/collectors/zapi/plugins/security/security.go index 535faa51e..eb19fc921 100644 --- a/cmd/collectors/zapi/plugins/security/security.go +++ b/cmd/collectors/zapi/plugins/security/security.go @@ -34,7 +34,7 @@ func (my *Security) Init() error { return err } - if my.client, err = zapi.New(conf.ZapiPoller(my.ParentParams)); err != nil { + if my.client, err = zapi.New(conf.ZapiPoller(my.ParentParams), my.Auth); err != nil { my.Logger.Error().Stack().Err(err).Msg("connecting") return err } diff --git a/cmd/collectors/zapi/plugins/sensor/sensor_test.go b/cmd/collectors/zapi/plugins/sensor/sensor_test.go index 28995553c..bf3f0c327 100644 --- a/cmd/collectors/zapi/plugins/sensor/sensor_test.go +++ b/cmd/collectors/zapi/plugins/sensor/sensor_test.go @@ -14,7 +14,7 @@ import ( var testxml = "../../../../../cmd/collectors/zapi/plugins/sensor/testdata/sensor.xml" var mat *matrix.Matrix -var sensor = &Sensor{AbstractPlugin: plugin.New("sensor", nil, nil, nil, "sensor")} +var sensor = &Sensor{AbstractPlugin: plugin.New("sensor", nil, nil, nil, "sensor", nil)} func init() { //setup matrix data diff --git a/cmd/collectors/zapi/plugins/shelf/shelf.go b/cmd/collectors/zapi/plugins/shelf/shelf.go index 139794da3..aebb697ca 100644 --- a/cmd/collectors/zapi/plugins/shelf/shelf.go +++ b/cmd/collectors/zapi/plugins/shelf/shelf.go @@ -37,7 +37,7 @@ func (my *Shelf) Init() error { return err } - if my.client, err = zapi.New(conf.ZapiPoller(my.ParentParams)); err != nil { + if my.client, err = zapi.New(conf.ZapiPoller(my.ParentParams), my.Auth); err != nil { my.Logger.Error().Stack().Err(err).Msg("connecting") return err } diff --git a/cmd/collectors/zapi/plugins/snapmirror/snapmirror.go b/cmd/collectors/zapi/plugins/snapmirror/snapmirror.go index 52ee488a1..5c197a096 100644 --- a/cmd/collectors/zapi/plugins/snapmirror/snapmirror.go +++ b/cmd/collectors/zapi/plugins/snapmirror/snapmirror.go @@ -41,7 +41,7 @@ func (my *SnapMirror) Init() error { if err = my.InitAbc(); err != nil { return err } - if my.client, err = zapi.New(conf.ZapiPoller(my.ParentParams)); err != nil { + if my.client, err = zapi.New(conf.ZapiPoller(my.ParentParams), my.Auth); err != nil { my.Logger.Error().Stack().Err(err).Msg("connecting") return err } diff --git a/cmd/collectors/zapi/plugins/svm/svm.go b/cmd/collectors/zapi/plugins/svm/svm.go index 4a310bfd3..4aa48ce00 100644 --- a/cmd/collectors/zapi/plugins/svm/svm.go +++ b/cmd/collectors/zapi/plugins/svm/svm.go @@ -67,7 +67,7 @@ func (my *SVM) Init() error { return err } - if my.client, err = zapi.New(conf.ZapiPoller(my.ParentParams)); err != nil { + if my.client, err = zapi.New(conf.ZapiPoller(my.ParentParams), my.Auth); err != nil { my.Logger.Error().Stack().Err(err).Msg("connecting") return err } diff --git a/cmd/collectors/zapi/plugins/volume/volume.go b/cmd/collectors/zapi/plugins/volume/volume.go index 25cc50f14..79a962277 100644 --- a/cmd/collectors/zapi/plugins/volume/volume.go +++ b/cmd/collectors/zapi/plugins/volume/volume.go @@ -36,7 +36,7 @@ func (my *Volume) Init() error { return err } - if my.client, err = zapi.New(conf.ZapiPoller(my.ParentParams)); err != nil { + if my.client, err = zapi.New(conf.ZapiPoller(my.ParentParams), my.Auth); err != nil { my.Logger.Error().Stack().Err(err).Msg("connecting") return err } diff --git a/cmd/collectors/zapiperf/plugins/disk/disk.go b/cmd/collectors/zapiperf/plugins/disk/disk.go index e3faf3afb..03b7d2d33 100644 --- a/cmd/collectors/zapiperf/plugins/disk/disk.go +++ b/cmd/collectors/zapiperf/plugins/disk/disk.go @@ -114,7 +114,7 @@ func (d *Disk) Init() error { return err } - if d.client, err = zapi.New(conf.ZapiPoller(d.ParentParams)); err != nil { + if d.client, err = zapi.New(conf.ZapiPoller(d.ParentParams), d.Auth); err != nil { d.Logger.Error().Stack().Err(err).Msg("connecting") return err } diff --git a/cmd/collectors/zapiperf/plugins/volumetag/volumetag.go b/cmd/collectors/zapiperf/plugins/volumetag/volumetag.go index d3c582b78..4227c9c55 100644 --- a/cmd/collectors/zapiperf/plugins/volumetag/volumetag.go +++ b/cmd/collectors/zapiperf/plugins/volumetag/volumetag.go @@ -25,7 +25,7 @@ func (v *VolumeTag) Init() error { return err } - if v.client, err = zapi.New(conf.ZapiPoller(v.ParentParams)); err != nil { + if v.client, err = zapi.New(conf.ZapiPoller(v.ParentParams), v.Auth); err != nil { v.Logger.Error().Stack().Err(err).Msg("connecting") return err } diff --git a/cmd/poller/collector/collector.go b/cmd/poller/collector/collector.go index 634855dbd..7036dc0e7 100644 --- a/cmd/poller/collector/collector.go +++ b/cmd/poller/collector/collector.go @@ -18,6 +18,7 @@ package collector import ( "errors" + "github.com/netapp/harvest/v2/pkg/auth" "github.com/netapp/harvest/v2/pkg/conf" "github.com/netapp/harvest/v2/pkg/logging" "golang.org/x/text/cases" @@ -85,7 +86,7 @@ type AbstractCollector struct { Object string // object of the collector, describes what that collector is collecting Logger *logging.Logger // logger used for logging Status uint8 // current state of th - Message string // reason if collector is in failed state + Message string // reason if a collector is in failed state Options *options.Options // poller options Params *node.Node // collector parameters // note that this is a merge of poller parameters, collector conf and object conf ("subtemplate") @@ -97,27 +98,23 @@ type AbstractCollector struct { collectCount uint64 // count of collected data points // this is different from what the collector will have in its metadata, since this variable // holds count independent of the poll interval of the collector, used to give stats to Poller - countMux *sync.Mutex // used for atomic access to collectCount + countMux *sync.Mutex // used for atomic access to collectCount + Auth *auth.Credentials // used for authing the collector HostVersion string HostModel string HostUUID string } -// New creates an AbstractCollector with the given arguments: -// @name - name of the collector -// @object - object of the collector (something that best describes the data) -// @options - poller options -// @params - collector parameters -func New(name, object string, options *options.Options, params *node.Node) *AbstractCollector { - c := AbstractCollector{ +func New(name, object string, options *options.Options, params *node.Node, credentials *auth.Credentials) *AbstractCollector { + return &AbstractCollector{ Name: name, Object: object, Options: options, Logger: logging.Get().SubLogger("collector", name+":"+object), Params: params, countMux: &sync.Mutex{}, + Auth: credentials, } - return &c } // Init initializes a collector and does the trick of "inheritance", @@ -583,7 +580,7 @@ func (c *AbstractCollector) LoadPlugins(params *node.Node, collector Collector, x.SetNameS(name) } - abc = plugin.New(c.Name, c.Options, x, c.Params, c.Object) + abc = plugin.New(c.Name, c.Options, x, c.Params, c.Object, c.Auth) // case 1: available as built-in plugin if p = GetBuiltinPlugin(name, abc); p != nil { diff --git a/cmd/poller/plugin/aggregator/aggregator_test.go b/cmd/poller/plugin/aggregator/aggregator_test.go index a042c0eda..6946b004b 100644 --- a/cmd/poller/plugin/aggregator/aggregator_test.go +++ b/cmd/poller/plugin/aggregator/aggregator_test.go @@ -17,7 +17,7 @@ func TestMain(m *testing.M) { params := node.NewS("Aggregator") params.NewChildS("", "node") - abc := plugin.New("Test", nil, params, nil, "") + abc := plugin.New("Test", nil, params, nil, "", nil) p = &Aggregator{AbstractPlugin: abc} if err := p.Init(); err != nil { diff --git a/cmd/poller/plugin/labelagent/label_agent_test.go b/cmd/poller/plugin/labelagent/label_agent_test.go index d0db57754..d6d0aef38 100644 --- a/cmd/poller/plugin/labelagent/label_agent_test.go +++ b/cmd/poller/plugin/labelagent/label_agent_test.go @@ -70,7 +70,7 @@ func TestInitPlugin(t *testing.T) { // exclude instance if label "volstatus" has value which starts with "stopped_" params.NewChildS("exclude_contains", "").NewChildS("", "volstatus `stop`") - abc := plugin.New("Test", nil, params, nil, "") + abc := plugin.New("Test", nil, params, nil, "", nil) p = &LabelAgent{AbstractPlugin: abc} if err := p.Init(); err != nil { diff --git a/cmd/poller/plugin/metricagent/metric_agent_test.go b/cmd/poller/plugin/metricagent/metric_agent_test.go index 7051fae01..f94f1290d 100644 --- a/cmd/poller/plugin/metricagent/metric_agent_test.go +++ b/cmd/poller/plugin/metricagent/metric_agent_test.go @@ -32,7 +32,7 @@ func TestInitPlugin(t *testing.T) { // create metric "transmission_rate", which is division of the metric value of transfer.bytes_transferred by transfer.total_duration params.NewChildS("compute_metric", "").NewChildS("", "transmission_rate DIVIDE transfer.bytes_transferred transfer.total_duration") - abc := plugin.New("Test", nil, params, nil, "") + abc := plugin.New("Test", nil, params, nil, "", nil) p = &MetricAgent{AbstractPlugin: abc} if err := p.Init(); err != nil { diff --git a/cmd/poller/plugin/plugin.go b/cmd/poller/plugin/plugin.go index 592c77a69..9230cf7de 100644 --- a/cmd/poller/plugin/plugin.go +++ b/cmd/poller/plugin/plugin.go @@ -30,6 +30,7 @@ package plugin import ( "fmt" "github.com/netapp/harvest/v2/cmd/poller/options" + "github.com/netapp/harvest/v2/pkg/auth" "github.com/netapp/harvest/v2/pkg/errs" "github.com/netapp/harvest/v2/pkg/logging" "github.com/netapp/harvest/v2/pkg/matrix" @@ -101,25 +102,27 @@ type ModuleInfo struct { // AbstractPlugin implements methods of the Plugin interface, except Run() type AbstractPlugin struct { - Parent string - Name string - Object string // object of the collector, describes what that collector is collecting - Logger *logging.Logger // logger used for logging - Options *options.Options - Params *node.Node - ParentParams *node.Node + Parent string // name of the collector that owns this plugin + Name string // name of the plugin + Object string // object of the collector, describes what that collector is collecting + Logger *logging.Logger // logger used for logging + Options *options.Options // poller options + Params *node.Node // plugin parameters + ParentParams *node.Node // parent collector parameters PluginInvocationRate int + Auth *auth.Credentials } -// New creates an AbstractPlugin with arguments: -// @parent - name of the collector that owns this plugin -// @o - poller options -// @p - plugin parameters -// @pp - parent collector parameters -// @object - object name -func New(parent string, o *options.Options, p *node.Node, pp *node.Node, object string) *AbstractPlugin { - pl := AbstractPlugin{Parent: parent, Options: o, Params: p, ParentParams: pp, Object: object} - return &pl +// New creates an AbstractPlugin +func New(parent string, o *options.Options, p *node.Node, pp *node.Node, object string, auth *auth.Credentials) *AbstractPlugin { + return &AbstractPlugin{ + Parent: parent, + Options: o, + Params: p, + ParentParams: pp, + Object: object, + Auth: auth, + } } // GetName returns the name of the plugin diff --git a/cmd/poller/plugin/test/plugin_test.go b/cmd/poller/plugin/test/plugin_test.go index 91fdc904c..51fa16926 100644 --- a/cmd/poller/plugin/test/plugin_test.go +++ b/cmd/poller/plugin/test/plugin_test.go @@ -33,7 +33,7 @@ func TestMultipleRule(t *testing.T) { params1.NewChildS("", x2.GetContentS()) } } - abc := plugin.New("Test", nil, params, nil, "") + abc := plugin.New("Test", nil, params, nil, "", nil) lb = &labelagent.LabelAgent{AbstractPlugin: abc} if err := lb.Init(); err != nil { t.Fatal(err) @@ -46,7 +46,7 @@ func TestMultipleRule(t *testing.T) { for _, x1 := range x.GetChildren() { params.NewChildS("", x1.GetContentS()) } - abc := plugin.New("Test", nil, params, nil, "") + abc := plugin.New("Test", nil, params, nil, "", nil) ag := &aggregator.Aggregator{AbstractPlugin: abc} if err := ag.Init(); err != nil { diff --git a/cmd/poller/poller.go b/cmd/poller/poller.go index 07d096fe0..530724222 100644 --- a/cmd/poller/poller.go +++ b/cmd/poller/poller.go @@ -131,6 +131,7 @@ type Poller struct { status *matrix.Matrix certPool *x509.CertPool client *http.Client + auth *auth.Credentials hasPromExporter bool } @@ -272,7 +273,7 @@ func (p *Poller) Init() error { } // create a shared auth service that all collectors will use - auth.NewCredentials(p.params, logger) + p.auth = auth.NewCredentials(p.params, logger) // initialize our metadata, the metadata will host status of our // collectors and exporters, as well as ping stats to target host @@ -844,7 +845,7 @@ func (p *Poller) newCollector(class string, object string, template *node.Node) if !ok { return nil, errs.New(errs.ErrNoCollector, "no collectors") } - delegate := collector.New(class, object, p.options, template.Copy()) + delegate := collector.New(class, object, p.options, template.Copy(), p.auth) err = col.Init(delegate) return col, err } @@ -1147,7 +1148,7 @@ func (p *Poller) upgradeCollector(c conf.Collector) conf.Collector { } verWithDots := r.Client.Cluster().GetVersion() - return p.negotiateAPI(c, verWithDots, doZAPIsExist) + return p.negotiateAPI(c, verWithDots, p.doZAPIsExist) } // clusterVersion should be of the form 9.12.1 @@ -1210,18 +1211,18 @@ func (p *Poller) negotiateAPI(c conf.Collector, clusterVersion string, checkZAPI return c } -func doZAPIsExist() error { +func (p *Poller) doZAPIsExist() error { var ( poller *conf.Poller connection *zapi.Client err error ) - // connect to cluster and retrieve system version + // connect to the cluster and retrieve the system version if poller, err = conf.PollerNamed(args.Poller); err != nil { return err } - if connection, err = zapi.New(poller); err != nil { + if connection, err = zapi.New(poller, p.auth); err != nil { return err } @@ -1236,7 +1237,7 @@ func (p *Poller) newRestClient() (*rest.Rest, error) { // Set client_timeout to suppress logging a msg about the default client_timeout during Rest client creation params.NewChildS("client_timeout", rest2.DefaultTimeout) Union2(params, p.params) - delegate := collector.New("Rest", "", p.options, params) + delegate := collector.New("Rest", "", p.options, params, p.auth) r := &rest.Rest{ AbstractCollector: delegate, } diff --git a/cmd/tools/generate/generate.go b/cmd/tools/generate/generate.go index 1ffa17043..1cec382dd 100644 --- a/cmd/tools/generate/generate.go +++ b/cmd/tools/generate/generate.go @@ -4,8 +4,10 @@ import ( "fmt" "github.com/netapp/harvest/v2/cmd/tools/rest" "github.com/netapp/harvest/v2/pkg/api/ontapi/zapi" + "github.com/netapp/harvest/v2/pkg/auth" "github.com/netapp/harvest/v2/pkg/color" "github.com/netapp/harvest/v2/pkg/conf" + "github.com/netapp/harvest/v2/pkg/logging" "github.com/spf13/cobra" "io" "os" @@ -359,7 +361,8 @@ func generateMetrics(path string) { } timeout, _ := time.ParseDuration(rest.DefaultTimeout) - if restClient, err = rest.New(poller, timeout); err != nil { + credentials := auth.NewCredentials(poller, logging.Get()) + if restClient, err = rest.New(poller, timeout, credentials); err != nil { fmt.Printf("error creating new client %+v\n", err) os.Exit(1) } @@ -368,7 +371,7 @@ func generateMetrics(path string) { os.Exit(1) } - if zapiClient, err = zapi.New(poller); err != nil { + if zapiClient, err = zapi.New(poller, credentials); err != nil { fmt.Printf("error creating new client %+v\n", err) os.Exit(1) } diff --git a/cmd/tools/rest/client.go b/cmd/tools/rest/client.go index b06805b90..530c3d042 100644 --- a/cmd/tools/rest/client.go +++ b/cmd/tools/rest/client.go @@ -15,6 +15,7 @@ import ( "github.com/netapp/harvest/v2/pkg/util" "github.com/tidwall/gjson" "io" + "net" "net/http" "net/http/httputil" "os" @@ -25,6 +26,8 @@ import ( const ( // DefaultTimeout should be > than ONTAP's default REST timeout, which is 15 seconds for GET requests DefaultTimeout = "30s" + // DefaultDialerTimeout limits the time spent establishing a TCP connection + DefaultDialerTimeout = 10 * time.Second ) type Client struct { @@ -37,7 +40,7 @@ type Client struct { username string Timeout time.Duration logRest bool // used to log Rest request/response - + auth *auth.Credentials } type Cluster struct { @@ -47,7 +50,7 @@ type Cluster struct { Version [3]int } -func New(poller *conf.Poller, timeout time.Duration) (*Client, error) { +func New(poller *conf.Poller, timeout time.Duration, auth *auth.Credentials) (*Client, error) { var ( client Client httpclient *http.Client @@ -59,7 +62,9 @@ func New(poller *conf.Poller, timeout time.Duration) (*Client, error) { err error ) - client = Client{} + client = Client{ + auth: auth, + } client.Logger = logging.Get().SubLogger("REST", "Client") if addr = poller.Addr; addr == "" { @@ -133,7 +138,7 @@ func New(poller *conf.Poller, timeout time.Duration) (*Client, error) { } } else { username := poller.Username - password := auth.Get().Password() + password := auth.Password() client.username = username if username == "" { return nil, errs.New(errs.ErrMissingParam, "username") @@ -146,7 +151,7 @@ func New(poller *conf.Poller, timeout time.Duration) (*Client, error) { TLSClientConfig: &tls.Config{InsecureSkipVerify: useInsecureTLS}, //nolint:gosec } } - + transport.DialContext = (&net.Dialer{Timeout: DefaultDialerTimeout}).DialContext httpclient = &http.Client{Transport: transport, Timeout: timeout} client.client = httpclient @@ -194,7 +199,7 @@ func (c *Client) GetRest(request string) ([]byte, error) { } c.request.Header.Set("accept", "application/json") if c.username != "" { - c.request.SetBasicAuth(c.username, auth.Get().Password()) + c.request.SetBasicAuth(c.username, c.auth.Password()) } // ensure that we can change body dynamically c.request.GetBody = func() (io.ReadCloser, error) { @@ -272,13 +277,13 @@ func downloadSwagger(poller *conf.Poller, path string, url string, verbose bool) } timeout, _ := time.ParseDuration(DefaultTimeout) - if restClient, err = New(poller, timeout); err != nil { + if restClient, err = New(poller, timeout, auth.NewCredentials(poller, logging.Get())); err != nil { return 0, fmt.Errorf("error creating new client %w", err) } downClient := &http.Client{Transport: restClient.client.Transport, Timeout: restClient.client.Timeout} if restClient.username != "" { - request.SetBasicAuth(restClient.username, auth.Get().Password()) + request.SetBasicAuth(restClient.username, restClient.auth.Password()) } if verbose { requestOut, _ := httputil.DumpRequestOut(request, false) diff --git a/cmd/tools/rest/rest.go b/cmd/tools/rest/rest.go index 0e59df893..b450c32fa 100644 --- a/cmd/tools/rest/rest.go +++ b/cmd/tools/rest/rest.go @@ -4,16 +4,20 @@ package rest import ( "encoding/json" + "errors" "fmt" "github.com/netapp/harvest/v2/pkg/auth" "github.com/netapp/harvest/v2/pkg/conf" + "github.com/netapp/harvest/v2/pkg/errs" "github.com/netapp/harvest/v2/pkg/logging" "github.com/netapp/harvest/v2/pkg/util" "github.com/spf13/cobra" "github.com/tidwall/gjson" "github.com/tidwall/sjson" "log" + "net/url" "os" + "os/signal" "path/filepath" "strconv" "strings" @@ -45,6 +49,7 @@ type Args struct { MaxRecords string ForceDownload bool Verbose bool + Timeout string } var Cmd = &cobra.Command{ @@ -90,13 +95,13 @@ func ReadOrDownloadSwagger(pName string) (string, error) { } } if shouldDownload { - url := "https://" + addr + "/docs/api/swagger.yaml" - bytesDownloaded, err := downloadSwagger(poller, swaggerPath, url, args.Verbose) + swaggerURL := "https://" + addr + "/docs/api/swagger.yaml" + bytesDownloaded, err := downloadSwagger(poller, swaggerPath, swaggerURL, args.Verbose) if err != nil { fmt.Printf("error downloading swagger %s\n", err) return "", err } - fmt.Printf("downloaded %d bytes from %s\n", bytesDownloaded, url) + fmt.Printf("downloaded %d bytes from %s\n", bytesDownloaded, swaggerURL) } fmt.Printf("Using downloaded file %s with timestamp %s\n", swaggerPath, swagTime) return swaggerPath, nil @@ -152,6 +157,68 @@ func doCmd() { } } +func fetchData(poller *conf.Poller, timeout time.Duration) (*Results, error) { + var ( + err error + client *Client + ) + + if client, err = New(poller, timeout, auth.NewCredentials(poller, logging.Get())); err != nil { + return nil, fmt.Errorf("poller=%s %w", poller.Name, err) + } + + // Init is called to get the cluster version + err = client.Init(1) + if err != nil { + var re *errs.RestError + if errors.As(err, &re) { + return nil, fmt.Errorf("poller=%s statusCode=%d", poller.Name, re.StatusCode) + } + return nil, fmt.Errorf("poller=%s %w", poller.Name, err) + } + + // strip leading slash + args.API = strings.TrimPrefix(args.API, "/") + + now := time.Now() + var records []any + var curls []string + href := BuildHref(args.API, args.Fields, args.Field, args.QueryField, args.QueryValue, args.MaxRecords, "", args.Endpoint) + + err = FetchForCli(client, href, &records, args.DownloadAll, &curls) + if err != nil { + return nil, fmt.Errorf("poller=%s %w", poller.Name, err) + } + for _, curl := range curls { + stderr("%s # %s\n", curl, poller.Name) + } + results := &Results{ + Poller: poller.Name, + Addr: poller.Addr, + API: args.API, + Version: client.Cluster().GetVersion(), + ClusterName: client.cluster.Name, + Records: records, + NumRecords: len(records), + PollDurationMs: time.Since(now).Milliseconds(), + } + if len(records) == 0 { + results.Records = []any{} + } + return results, nil +} + +type Results struct { + Poller string `json:"poller,omitempty"` + Addr string `json:"addr,omitempty"` + API string `json:"api,omitempty"` + Version string `json:"version,omitempty"` + ClusterName string `json:"cluster_name,omitempty"` + Records []any `json:"records"` + NumRecords int `json:"num_records"` + PollDurationMs int64 `json:"poll_ms"` +} + type Pagination struct { Records []any `json:"records"` NumRecords int `json:"num_records"` @@ -169,38 +236,60 @@ type PerfRecord struct { func doData() { var ( - poller *conf.Poller - err error - client *Client + err error + results []*Results + timeout time.Duration ) - if poller, _, err = GetPollerAndAddr(args.Poller); err != nil { - return - } - - timeout, _ := time.ParseDuration(DefaultTimeout) - if client, err = New(poller, timeout); err != nil { - fmt.Printf("error creating new client %+v\n", err) - os.Exit(1) + timeout, err = time.ParseDuration(args.Timeout) + if err != nil { + stderr("Unable to parse timeout=%s using default %s\n", args.Timeout, DefaultTimeout) + timeout, _ = time.ParseDuration(DefaultTimeout) } - // strip leading slash - args.API = strings.TrimPrefix(args.API, "/") - - var records []any - href := BuildHref(args.API, args.Fields, args.Field, args.QueryField, args.QueryValue, args.MaxRecords, "", args.Endpoint) - stderr("fetching href=[%s]\n", href) + resultChan := make(chan *Results) + errChan := make(chan error) + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt) - err = FetchForCli(client, href, &records, args.DownloadAll) - if err != nil { - stderr("error %+v\n", err) - return + pollers := make([]string, 0) + if args.Poller == "*" { + pollers = append(pollers, conf.Config.PollersOrdered...) + } else { + pollers = append(pollers, args.Poller) } - all := Pagination{ - Records: records, - NumRecords: len(records), + + for _, pollerName := range pollers { + go func(pollerName string) { + var ( + poller *conf.Poller + ) + if poller, _, err = GetPollerAndAddr(pollerName); err != nil { + errChan <- err + return + } + data, err := fetchData(poller, timeout) + if err != nil { + errChan <- err + return + } + resultChan <- data + }(pollerName) + } + +outer: + for range pollers { + select { + case r := <-resultChan: + results = append(results, r) + case err := <-errChan: + stderr("failed to fetch data err: %+v\n", err) + case <-sigChan: + break outer + } } - pretty, err := json.MarshalIndent(all, "", " ") + + pretty, err := json.MarshalIndent(results, "", " ") if err != nil { stderr("error marshalling json %+v\n", err) return @@ -214,24 +303,23 @@ func GetPollerAndAddr(pName string) (*conf.Poller, string, error) { err error ) if poller, err = conf.PollerNamed(pName); err != nil { - fmt.Printf("Poller named [%s] does not exist\n", pName) - return nil, "", err + return nil, "", fmt.Errorf("poller=%s does not exist, err: %w", pName, err) } if poller.Addr == "" { - fmt.Printf("Poller named [%s] does not have a valid addr=[]\n", pName) - return nil, "", err + return nil, "", fmt.Errorf("poller=%s has blank addr", pName) } - auth.NewCredentials(poller, logging.Get()) return poller, poller.Addr, nil } // FetchForCli used for CLI only -func FetchForCli(client *Client, href string, records *[]any, downloadAll bool) error { +func FetchForCli(client *Client, href string, records *[]any, downloadAll bool, curls *[]string) error { getRest, err := client.GetRest(href) if err != nil { return fmt.Errorf("error making request %w", err) } + *curls = append(*curls, fmt.Sprintf("curl --user %s --insecure '%s%s'", client.username, client.baseURL, href)) + isNonIterRestCall := false value := gjson.GetBytes(getRest, "records") if value.String() == "" { @@ -262,13 +350,15 @@ func FetchForCli(client *Client, href string, records *[]any, downloadAll bool) // If all results are desired and there is a next link, follow it if downloadAll && page.Links != nil { - nextLink := page.Links.Next.Href + nextLink, _ := url.QueryUnescape(page.Links.Next.Href) if nextLink != "" { + // strip leading slash + nextLink = strings.TrimPrefix(nextLink, "/") if nextLink == href { - // nextLink is same as previous link, no progress is being made, exit + // if nextLink is the same as the previous link, no progress is being made, exit return nil } - err := FetchForCli(client, nextLink, records, downloadAll) + err := FetchForCli(client, nextLink, records, downloadAll, curls) if err != nil { return err } @@ -488,9 +578,10 @@ func init() { Cmd.AddCommand(showCmd) flags := Cmd.PersistentFlags() - flags.StringVarP(&args.Poller, "poller", "p", "", "name of poller (cluster), as defined in your harvest config") - flags.StringVarP(&args.SwaggerPath, "swagger", "s", "", "path to Swagger (OpenAPI) file to read from") - flags.StringVar(&args.Config, "config", configPath, "harvest config file path") + flags.StringVarP(&args.Poller, "poller", "p", "", "Name of poller (cluster), as defined in your harvest config. * for all pollers") + flags.StringVarP(&args.SwaggerPath, "swagger", "s", "", "Path to Swagger (OpenAPI) file to read from") + flags.StringVar(&args.Config, "config", configPath, "Harvest config file path") + flags.StringVarP(&args.Timeout, "timeout", "t", DefaultTimeout, "Duration to wait before giving up") showFlags := showCmd.Flags() showFlags.StringVarP(&args.API, "api", "a", "", "REST API PATTERN to show") @@ -515,19 +606,25 @@ func init() { Cmd.SetUsageTemplate(Cmd.UsageTemplate() + ` Examples: - harvest rest -p infinity show apis Query cluster infinity for available APIs - harvest rest -p infinity show params --api svm/svms Query cluster infinity for svm parameters. These query parameters are used - to filter requests. - harvest rest -p infinity show models --api svm/svms Query cluster infinity for svm models. These describe the REST response - received when sending the svm/svms GET request. - harvest rest -p infinity show data --api svm/svms --field "state=stopped" Query cluster infinity for stopped svms. - - harvest rest -p infinity show data --api storage/volumes \ Query cluster infinity for all volumes where - --field "space.physical_used_percent=>70" \ physical_used_percent is > 70% and - --field "space.total_footprint=>400G" \ total_footprint is > 400G - --fields "name,svm,space" The response should contain name, svm, and space attributes of matching volumes. - - harvest rest -p infinity show data --api storage/volumes \ Query cluster infinity for all volumes where the name of any volume or child - --query-field "name" --query-value "io_load|scale" resource matches io_load or scale. + # Query cluster infinity for available APIs + bin/harvest rest -p infinity show apis + + # Query cluster infinity for svm parameters. These query parameters are used to filter requests. + bin/harvest rest -p infinity show params --api svm/svms + + # Query cluster infinity for svm models. These describe the REST response of sending the svm/svms GET request. + bin/harvest rest -p infinity show models --api svm/svms + + # Query cluster infinity for stopped svms. + bin/harvest rest -p infinity show data --api svm/svms --field "state=stopped" + + # Query cluster infinity for all volumes where physical_used_percent is > 70% and total_footprint is >= 400G. The response should contain name, svm, and space attributes of matching volumes. + bin/harvest rest -p infinity show data --api storage/volumes --field "space.physical_used_percent=>70" --field "space.total_footprint=>=400G" --fields "name,svm,space" + + # Query cluster infinity for all volumes where the name of any volume or child resource matches io_load or scale. + bin/harvest rest -p infinity show data --api storage/volumes --query-field "name" --query-value "io_load|scale" + + # Query all clusters, in your harvest.yml file, for all qos policies. Pipe the results to jq, and print as CSV. + bin/harvest rest -p '*' show data --api storage/qos/policies | jq -r '.[] | [.poller, .addr, .num_records, .version, .cluster_name, .poll_ms, .api] | @csv' | column -ts, `) } diff --git a/cmd/tools/zapi/zapi.go b/cmd/tools/zapi/zapi.go index dba045f6c..0997bff32 100644 --- a/cmd/tools/zapi/zapi.go +++ b/cmd/tools/zapi/zapi.go @@ -133,12 +133,11 @@ func doCmd(cmd string) { if err != nil { log.Fatal(err) } - // connect to cluster and retrieve system version + // connect to a cluster and retrieve the system version if poller, err = conf.PollerNamed(args.Poller); err != nil { log.Fatal(err) } - auth.NewCredentials(poller, logging.Get()) - if connection, err = client.New(poller); err != nil { + if connection, err = client.New(poller, auth.NewCredentials(poller, logging.Get())); err != nil { log.Fatal(err) } diff --git a/pkg/api/ontapi/zapi/client.go b/pkg/api/ontapi/zapi/client.go index 3b29f3228..e9f8b87bb 100644 --- a/pkg/api/ontapi/zapi/client.go +++ b/pkg/api/ontapi/zapi/client.go @@ -38,9 +38,10 @@ type Client struct { vfiler string Logger *logging.Logger // logger used for logging logZapi bool // used to log ZAPI request/response + auth *auth.Credentials } -func New(poller *conf.Poller) (*Client, error) { +func New(poller *conf.Poller, c *auth.Credentials) (*Client, error) { var ( client Client httpclient *http.Client @@ -53,7 +54,9 @@ func New(poller *conf.Poller) (*Client, error) { err error ) - client = Client{} + client = Client{ + auth: c, + } client.Logger = logging.Get().SubLogger("Zapi", "Client") // check required & optional parameters @@ -99,7 +102,6 @@ func New(poller *conf.Poller) (*Client, error) { useInsecureTLS = *poller.UseInsecureTLS } - // check if a credentials file is being used and if so, parse and use the values from it if poller.CredentialsFile != "" { err := conf.ReadCredentialsFile(poller.CredentialsFile, poller) if err != nil { @@ -152,7 +154,7 @@ func New(poller *conf.Poller) (*Client, error) { }, } } else { - password := auth.Get().Password() + password := c.Password() if poller.Username == "" { return nil, errs.New(errs.ErrMissingParam, "username") } else if password == "" { diff --git a/pkg/api/ontapi/zapi/client_test.go b/pkg/api/ontapi/zapi/client_test.go index 47d95d441..c1ebfb863 100644 --- a/pkg/api/ontapi/zapi/client_test.go +++ b/pkg/api/ontapi/zapi/client_test.go @@ -54,8 +54,7 @@ func TestNew(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { poller := conf.ZapiPoller(tt.config) - auth.TestNewCredentials(poller, logging.Get()) - _, err := New(poller) + _, err := New(poller, auth.NewCredentials(poller, logging.Get())) if (err != nil) != tt.wantErr { t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 9fda36532..f743c3f54 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -17,23 +17,12 @@ const ( defaultTimeout = "10s" ) -var once sync.Once -var auth *Credentials - -func Get() *Credentials { - return auth -} - -func NewCredentials(p *conf.Poller, logger *logging.Logger) { - once.Do(func() { - if auth == nil { - auth = &Credentials{ - poller: p, - logger: logger, - authMu: &sync.Mutex{}, - } - } - }) +func NewCredentials(p *conf.Poller, logger *logging.Logger) *Credentials { + return &Credentials{ + poller: p, + logger: logger, + authMu: &sync.Mutex{}, + } } type Credentials struct { @@ -131,13 +120,3 @@ func (c *Credentials) setNextUpdate() { } c.nextUpdate = time.Now().Add(duration) } - -// TestNewCredentials is used by testing code to reload auth -func TestNewCredentials(p *conf.Poller, logger *logging.Logger) *Credentials { - auth = &Credentials{ - poller: p, - logger: logger, - authMu: &sync.Mutex{}, - } - return auth -}