From 873d61278aca90fd2e50f1de6a89d5a4d046367e Mon Sep 17 00:00:00 2001 From: peevo Date: Wed, 21 Oct 2020 16:42:46 +0300 Subject: [PATCH 1/4] #8295 Initial Yandex.Clout monitoring --- README.md | 1 + .../outputs/yandex_cloud_monitoring/README.md | 29 ++ .../yandex_cloud_monitoring.go | 276 ++++++++++++++++++ .../yandex_cloud_monitoring_test.go | 97 ++++++ 4 files changed, 403 insertions(+) create mode 100644 plugins/outputs/yandex_cloud_monitoring/README.md create mode 100644 plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring.go create mode 100644 plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring_test.go diff --git a/README.md b/README.md index 168db50fd6a24..89c05b8827773 100644 --- a/README.md +++ b/README.md @@ -446,3 +446,4 @@ For documentation on the latest development code see the [documentation index][d * [warp10](./plugins/outputs/warp10) * [wavefront](./plugins/outputs/wavefront) * [sumologic](./plugins/outputs/sumologic) +* [yandex_cloud_monitoring](./plugins/outputs/yandex_cloud_monitoring) diff --git a/plugins/outputs/yandex_cloud_monitoring/README.md b/plugins/outputs/yandex_cloud_monitoring/README.md new file mode 100644 index 0000000000000..b1151e2637535 --- /dev/null +++ b/plugins/outputs/yandex_cloud_monitoring/README.md @@ -0,0 +1,29 @@ +# Yandex Cloud Monitoring + +This plugin will send custom metrics to Yandex Cloud Monitoring. +https://cloud.yandex.com/services/monitoring + +### Configuration: + +```toml +[[outputs.yandex_cloud_monitoring]] + ## Timeout for HTTP writes. + # timeout = "20s" + + ## Normally should not be changed + # endpoint_url = "https://monitoring.api.cloud.yandex.net/monitoring/v2/data/write" + + ## Normally folder ID is taken from Compute instance metadata + # folder_id = "..." + + ## Can be set explicitly for authentification debugging purposes + # iam_token = "..." +``` + +### Authentication + +This plugin currently support only YC.Compute metadata based authentication. + +When plugin is working inside a YC.Compute instance it will take IAM token and Folder ID from instance metadata. + +Other authentication methods will be added later. diff --git a/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring.go b/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring.go new file mode 100644 index 0000000000000..442e622663f9e --- /dev/null +++ b/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring.go @@ -0,0 +1,276 @@ +package yandex_cloud_monitoring + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "time" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/internal" + "github.com/influxdata/telegraf/plugins/outputs" + "github.com/influxdata/telegraf/selfstat" +) + +// YandexCloudMonitoring allows publishing of metrics to the Yandex Cloud Monitoring custom metrics +// service +type YandexCloudMonitoring struct { + Timeout internal.Duration + EndpointUrl string `toml:"endpoint_url"` + MetadataTokenUrl string `toml:"metadata_token_url"` + MetadataFolderUrl string `toml:"metadata_folder_url"` + Service string `toml:"service"` + FolderID string `toml:"folder_id"` + IAMTokenFromConfig string `toml:"iam_token"` + + IAMToken string + IamTokenExpirationTime time.Time + + client *http.Client + + timeFunc func() time.Time + + MetricOutsideWindow selfstat.Stat +} + +type yandexCloudMonitoringMessage struct { + TS string `json:"ts,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + Metrics []yandexCloudMonitoringMetric `json:"metrics"` +} + +type yandexCloudMonitoringMetric struct { + Name string `json:"name"` + Labels map[string]string `json:"labels"` + MetricType string `json:"type,omitempty"` // DGAUGE|IGAUGE|COUNTER|RATE. Default: DGAUGE + TS string `json:"ts,omitempty"` + Value float64 `json:"value"` +} + +type MetadataIamToken struct { + AccessToken string `json:"access_token"` + ExpiresIn int64 `json:"expires_in"` + TokenType string `json:"token_type"` +} + +const ( + defaultRequestTimeout = time.Second * 5 + defaultEndpointUrl = "https://monitoring.api.cloud.yandex.net/monitoring/v2/data/write" + defaultMetadataTokenUrl = "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token" + defaultMetadataFolderUrl = "http://169.254.169.254/computeMetadata/v1/instance/attributes/folder-id" + maxRequestBodySize = 4000000 +) + +var sampleConfig = ` + ## Timeout for HTTP writes. + # timeout = "20s" + + ## Normally should not be changed + # endpoint_url = "https://monitoring.api.cloud.yandex.net/monitoring/v2/data/write" + + ## Normally folder ID is taken from Compute instance metadata + # folder_id = "..." + + ## Can be set explicitly for authentification debugging purposes + # iam_token = "..." +` + +// Description provides a description of the plugin +func (a *YandexCloudMonitoring) Description() string { + return "Send aggregated metrics to Yandex.Cloud Monitoring" +} + +// SampleConfig provides a sample configuration for the plugin +func (a *YandexCloudMonitoring) SampleConfig() string { + return sampleConfig +} + +// Connect initializes the plugin and validates connectivity +func (a *YandexCloudMonitoring) Connect() error { + if a.Timeout.Duration == 0 { + a.Timeout.Duration = defaultRequestTimeout + } + + a.client = &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + }, + Timeout: a.Timeout.Duration, + } + + if a.EndpointUrl == "" { + a.EndpointUrl = defaultEndpointUrl + } + if a.MetadataTokenUrl == "" { + a.MetadataTokenUrl = defaultMetadataTokenUrl + } + if a.MetadataFolderUrl == "" { + a.MetadataFolderUrl = defaultMetadataFolderUrl + } + if a.FolderID == "" { + folderID, err := getFolderIDFromMetadata(a.client, a.MetadataFolderUrl) + if err != nil { + return err + } + a.FolderID = folderID + } + if a.Service == "" { + a.Service = "custom" + } + + log.Printf("D! Writing to Yandex.Cloud Monitoring URL: %s", a.EndpointUrl) + + tags := map[string]string{} + a.MetricOutsideWindow = selfstat.Register("yandex_cloud_monitoring", "metric_outside_window", tags) + + return nil +} + +// Close shuts down an any active connections +func (a *YandexCloudMonitoring) Close() error { + a.client = nil + return nil +} + +// Write writes metrics to the remote endpoint +func (a *YandexCloudMonitoring) Write(metrics []telegraf.Metric) error { + var yandexCloudMonitoringMetrics []yandexCloudMonitoringMetric + for _, m := range metrics { + for _, field := range m.FieldList() { + yandexCloudMonitoringMetrics = append( + yandexCloudMonitoringMetrics, + yandexCloudMonitoringMetric{ + Name: field.Key, + Labels: m.Tags(), + TS: fmt.Sprint(m.Time().Format(time.RFC3339)), + Value: field.Value.(float64), + }, + ) + } + } + + var body []byte + jsonBytes, err := json.Marshal( + yandexCloudMonitoringMessage{ + Metrics: yandexCloudMonitoringMetrics, + }, + ) + + if err != nil { + return err + } + // Send batches that exceed this size via separate write requests. + if (len(body) + len(jsonBytes) + 1) > maxRequestBodySize { + err := a.send(body) + if err != nil { + return err + } + body = nil + } + body = append(body, jsonBytes...) + body = append(body, '\n') + + return a.send(body) +} + +func getResponseFromMetadata(c *http.Client, metadataUrl string) ([]byte, error) { + req, err := http.NewRequest("GET", metadataUrl, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %v", err) + } + req.Header.Set("Metadata-Flavor", "Google") + resp, err := c.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + if resp.StatusCode >= 300 || resp.StatusCode < 200 { + return nil, fmt.Errorf("unable to fetch instance metadata: [%s] %d", + metadataUrl, resp.StatusCode) + } + return body, nil +} + +func getFolderIDFromMetadata(c *http.Client, metadataUrl string) (string, error) { + log.Printf("!D getting folder ID in %s", metadataUrl) + body, err := getResponseFromMetadata(c, metadataUrl) + if err != nil { + return "", err + } + folderID := string(body) + if folderID == "" { + return "", fmt.Errorf("unable to fetch folder id from URL %s: %v", metadataUrl, err) + } + return folderID, nil +} + +func getIAMTokenFromMetadata(c *http.Client, metadataUrl string) (string, int, error) { + log.Printf("!D getting new IAM token in %s", metadataUrl) + body, err := getResponseFromMetadata(c, metadataUrl) + if err != nil { + return "", 0, err + } + var metadata MetadataIamToken + if err := json.Unmarshal(body, &metadata); err != nil { + return "", 0, err + } + if metadata.AccessToken == "" || metadata.ExpiresIn == 0 { + return "", 0, fmt.Errorf("unable to fetch authentication credentials: %v", err) + } + return metadata.AccessToken, int(metadata.ExpiresIn), nil +} + +func (a *YandexCloudMonitoring) send(body []byte) error { + req, err := http.NewRequest("POST", a.EndpointUrl, bytes.NewBuffer(body)) + if err != nil { + return err + } + q := req.URL.Query() + q.Add("folderId", a.FolderID) + q.Add("service", a.Service) + req.URL.RawQuery = q.Encode() + + req.Header.Set("Content-Type", "application/json") + isTokenExpired := !a.IamTokenExpirationTime.After(time.Now()) + if a.IAMTokenFromConfig != "" { + a.IAMToken = a.IAMTokenFromConfig + } else if isTokenExpired { + token, expiresIn, err := getIAMTokenFromMetadata(a.client, a.MetadataTokenUrl) + if err != nil { + return err + } + a.IamTokenExpirationTime = time.Now().Add(time.Duration(expiresIn) * time.Second) + a.IAMToken = token + } + req.Header.Set("Authorization", "Bearer "+a.IAMToken) + + log.Printf("!D sending metrics to %s", req.URL.String()) + resp, err := a.client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = ioutil.ReadAll(resp.Body) + if err != nil || resp.StatusCode < 200 || resp.StatusCode > 299 { + return fmt.Errorf("failed to write batch: [%v] %s", resp.StatusCode, resp.Status) + } + + return nil +} + +func init() { + outputs.Add("yandex_cloud_monitoring", func() telegraf.Output { + return &YandexCloudMonitoring{ + timeFunc: time.Now, + } + }) +} diff --git a/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring_test.go b/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring_test.go new file mode 100644 index 0000000000000..0d9c2e0a4e03c --- /dev/null +++ b/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring_test.go @@ -0,0 +1,97 @@ +package yandex_cloud_monitoring + +import ( + "encoding/json" + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/testutil" + "github.com/stretchr/testify/require" + "io" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" +) + +func TestWrite(t *testing.T) { + readBody := func(r *http.Request) (yandexCloudMonitoringMessage, error) { + decoder := json.NewDecoder(r.Body) + var message yandexCloudMonitoringMessage + err := decoder.Decode(&message) + if err != nil { + panic(err) + } + return message, nil + } + + testMetadataHttpServer := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if strings.HasSuffix(r.URL.Path, "/token") { + token := MetadataIamToken{ + AccessToken: "token1", + ExpiresIn: 123, + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + err := json.NewEncoder(w).Encode(token) + require.NoError(t, err) + } else if strings.HasSuffix(r.URL.Path, "/folder") { + _, err := io.WriteString(w, "folder1") + require.NoError(t, err) + } + w.WriteHeader(http.StatusOK) + }), + ) + defer testMetadataHttpServer.Close() + metadataTokenUrl := "http://" + testMetadataHttpServer.Listener.Addr().String() + "/token" + metadataFolderUrl := "http://" + testMetadataHttpServer.Listener.Addr().String() + "/folder" + + ts := httptest.NewServer(http.NotFoundHandler()) + defer ts.Close() + url := "http://" + ts.Listener.Addr().String() + "/metrics" + + tests := []struct { + name string + plugin *YandexCloudMonitoring + metrics []telegraf.Metric + handler func(t *testing.T, w http.ResponseWriter, r *http.Request) + }{ + { + name: "metric is converted to json value", + plugin: &YandexCloudMonitoring{}, + metrics: []telegraf.Metric{ + testutil.MustMetric( + "cluster", + map[string]string{}, + map[string]interface{}{ + "cpu": 42.0, + }, + time.Unix(0, 0), + ), + }, + handler: func(t *testing.T, w http.ResponseWriter, r *http.Request) { + message, err := readBody(r) + require.NoError(t, err) + require.Len(t, message.Metrics, 1) + require.Equal(t, "cpu", message.Metrics[0].Name) + require.Equal(t, 42.0, message.Metrics[0].Value) + w.WriteHeader(http.StatusOK) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ts.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + tt.handler(t, w, r) + }) + tt.plugin.EndpointUrl = url + tt.plugin.MetadataTokenUrl = metadataTokenUrl + tt.plugin.MetadataFolderUrl = metadataFolderUrl + err := tt.plugin.Connect() + require.NoError(t, err) + + err = tt.plugin.Write(tt.metrics) + + require.NoError(t, err) + }) + } +} From 76d4b48dcd7550440280e17a655e05c4d7f4fbf3 Mon Sep 17 00:00:00 2001 From: peevo Date: Fri, 23 Oct 2020 14:19:26 +0300 Subject: [PATCH 2/4] #8295 configurable timeout --- .../yandex_cloud_monitoring.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring.go b/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring.go index 442e622663f9e..077413ab6facb 100644 --- a/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring.go +++ b/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring.go @@ -18,13 +18,13 @@ import ( // YandexCloudMonitoring allows publishing of metrics to the Yandex Cloud Monitoring custom metrics // service type YandexCloudMonitoring struct { - Timeout internal.Duration - EndpointUrl string `toml:"endpoint_url"` - MetadataTokenUrl string `toml:"metadata_token_url"` - MetadataFolderUrl string `toml:"metadata_folder_url"` - Service string `toml:"service"` - FolderID string `toml:"folder_id"` - IAMTokenFromConfig string `toml:"iam_token"` + Timeout internal.Duration `toml:"timeout"` + EndpointUrl string `toml:"endpoint_url"` + MetadataTokenUrl string `toml:"metadata_token_url"` + MetadataFolderUrl string `toml:"metadata_folder_url"` + Service string `toml:"service"` + FolderID string `toml:"folder_id"` + IAMTokenFromConfig string `toml:"iam_token"` IAMToken string IamTokenExpirationTime time.Time @@ -90,7 +90,7 @@ func (a *YandexCloudMonitoring) SampleConfig() string { // Connect initializes the plugin and validates connectivity func (a *YandexCloudMonitoring) Connect() error { - if a.Timeout.Duration == 0 { + if a.Timeout.Duration <= 0 { a.Timeout.Duration = defaultRequestTimeout } From 672db0c4c2d788aeae80ddfe1791d4d165ef22cd Mon Sep 17 00:00:00 2001 From: peevo Date: Sat, 31 Oct 2020 02:23:14 +0300 Subject: [PATCH 3/4] #8295 use telegraf logger --- plugins/outputs/all/all.go | 1 + .../outputs/yandex_cloud_monitoring/README.md | 11 +-- .../yandex_cloud_monitoring.go | 95 ++++++++----------- .../yandex_cloud_monitoring_test.go | 5 +- 4 files changed, 46 insertions(+), 66 deletions(-) diff --git a/plugins/outputs/all/all.go b/plugins/outputs/all/all.go index f81aa9d71b072..fdd3daa0b822f 100644 --- a/plugins/outputs/all/all.go +++ b/plugins/outputs/all/all.go @@ -40,4 +40,5 @@ import ( _ "github.com/influxdata/telegraf/plugins/outputs/timestream" _ "github.com/influxdata/telegraf/plugins/outputs/warp10" _ "github.com/influxdata/telegraf/plugins/outputs/wavefront" + _ "github.com/influxdata/telegraf/plugins/outputs/yandex_cloud_monitoring" ) diff --git a/plugins/outputs/yandex_cloud_monitoring/README.md b/plugins/outputs/yandex_cloud_monitoring/README.md index b1151e2637535..2b98729889b94 100644 --- a/plugins/outputs/yandex_cloud_monitoring/README.md +++ b/plugins/outputs/yandex_cloud_monitoring/README.md @@ -8,16 +8,13 @@ https://cloud.yandex.com/services/monitoring ```toml [[outputs.yandex_cloud_monitoring]] ## Timeout for HTTP writes. - # timeout = "20s" + # timeout = "5s" - ## Normally should not be changed + ## Yandex.Cloud monitoring API endpoint. Normally should not be changed # endpoint_url = "https://monitoring.api.cloud.yandex.net/monitoring/v2/data/write" - ## Normally folder ID is taken from Compute instance metadata - # folder_id = "..." - - ## Can be set explicitly for authentification debugging purposes - # iam_token = "..." + ## All user metrics should be sent with "custom" service specified. Normally should not be changed + # service = "custom" ``` ### Authentication diff --git a/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring.go b/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring.go index 077413ab6facb..fe617621eac58 100644 --- a/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring.go +++ b/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io/ioutil" - "log" "net/http" "time" @@ -18,14 +17,15 @@ import ( // YandexCloudMonitoring allows publishing of metrics to the Yandex Cloud Monitoring custom metrics // service type YandexCloudMonitoring struct { - Timeout internal.Duration `toml:"timeout"` - EndpointUrl string `toml:"endpoint_url"` - MetadataTokenUrl string `toml:"metadata_token_url"` - MetadataFolderUrl string `toml:"metadata_folder_url"` - Service string `toml:"service"` - FolderID string `toml:"folder_id"` - IAMTokenFromConfig string `toml:"iam_token"` + Timeout internal.Duration `toml:"timeout"` + EndpointUrl string `toml:"endpoint_url"` + Service string `toml:"service"` + Log telegraf.Logger + + MetadataTokenUrl string + MetadataFolderUrl string + FolderID string IAMToken string IamTokenExpirationTime time.Time @@ -61,21 +61,17 @@ const ( defaultEndpointUrl = "https://monitoring.api.cloud.yandex.net/monitoring/v2/data/write" defaultMetadataTokenUrl = "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token" defaultMetadataFolderUrl = "http://169.254.169.254/computeMetadata/v1/instance/attributes/folder-id" - maxRequestBodySize = 4000000 ) var sampleConfig = ` ## Timeout for HTTP writes. - # timeout = "20s" + # timeout = "5s" - ## Normally should not be changed + ## Yandex.Cloud monitoring API endpoint. Normally should not be changed # endpoint_url = "https://monitoring.api.cloud.yandex.net/monitoring/v2/data/write" - ## Normally folder ID is taken from Compute instance metadata - # folder_id = "..." - - ## Can be set explicitly for authentification debugging purposes - # iam_token = "..." + ## All user metrics should be sent with "custom" service specified. Normally should not be changed + # service = "custom" ` // Description provides a description of the plugin @@ -93,35 +89,33 @@ func (a *YandexCloudMonitoring) Connect() error { if a.Timeout.Duration <= 0 { a.Timeout.Duration = defaultRequestTimeout } - - a.client = &http.Client{ - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - }, - Timeout: a.Timeout.Duration, - } - if a.EndpointUrl == "" { a.EndpointUrl = defaultEndpointUrl } + if a.Service == "" { + a.Service = "custom" + } if a.MetadataTokenUrl == "" { a.MetadataTokenUrl = defaultMetadataTokenUrl } if a.MetadataFolderUrl == "" { a.MetadataFolderUrl = defaultMetadataFolderUrl } - if a.FolderID == "" { - folderID, err := getFolderIDFromMetadata(a.client, a.MetadataFolderUrl) - if err != nil { - return err - } - a.FolderID = folderID + + a.client = &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + }, + Timeout: a.Timeout.Duration, } - if a.Service == "" { - a.Service = "custom" + + var err error + a.FolderID, err = a.getFolderIDFromMetadata() + if err != nil { + return err } - log.Printf("D! Writing to Yandex.Cloud Monitoring URL: %s", a.EndpointUrl) + a.Log.Infof("Writing to Yandex.Cloud Monitoring URL: %s", a.EndpointUrl) tags := map[string]string{} a.MetricOutsideWindow = selfstat.Register("yandex_cloud_monitoring", "metric_outside_window", tags) @@ -162,17 +156,8 @@ func (a *YandexCloudMonitoring) Write(metrics []telegraf.Metric) error { if err != nil { return err } - // Send batches that exceed this size via separate write requests. - if (len(body) + len(jsonBytes) + 1) > maxRequestBodySize { - err := a.send(body) - if err != nil { - return err - } - body = nil - } body = append(body, jsonBytes...) - body = append(body, '\n') - + body = append(jsonBytes, '\n') return a.send(body) } @@ -199,22 +184,22 @@ func getResponseFromMetadata(c *http.Client, metadataUrl string) ([]byte, error) return body, nil } -func getFolderIDFromMetadata(c *http.Client, metadataUrl string) (string, error) { - log.Printf("!D getting folder ID in %s", metadataUrl) - body, err := getResponseFromMetadata(c, metadataUrl) +func (a *YandexCloudMonitoring) getFolderIDFromMetadata() (string, error) { + a.Log.Infof("getting folder ID in %s", a.MetadataFolderUrl) + body, err := getResponseFromMetadata(a.client, a.MetadataFolderUrl) if err != nil { return "", err } folderID := string(body) if folderID == "" { - return "", fmt.Errorf("unable to fetch folder id from URL %s: %v", metadataUrl, err) + return "", fmt.Errorf("unable to fetch folder id from URL %s: %v", a.MetadataFolderUrl, err) } return folderID, nil } -func getIAMTokenFromMetadata(c *http.Client, metadataUrl string) (string, int, error) { - log.Printf("!D getting new IAM token in %s", metadataUrl) - body, err := getResponseFromMetadata(c, metadataUrl) +func (a *YandexCloudMonitoring) getIAMTokenFromMetadata() (string, int, error) { + a.Log.Debugf("getting new IAM token in %s", a.MetadataTokenUrl) + body, err := getResponseFromMetadata(a.client, a.MetadataTokenUrl) if err != nil { return "", 0, err } @@ -223,7 +208,7 @@ func getIAMTokenFromMetadata(c *http.Client, metadataUrl string) (string, int, e return "", 0, err } if metadata.AccessToken == "" || metadata.ExpiresIn == 0 { - return "", 0, fmt.Errorf("unable to fetch authentication credentials: %v", err) + return "", 0, fmt.Errorf("unable to fetch authentication credentials %s: %v", a.MetadataTokenUrl, err) } return metadata.AccessToken, int(metadata.ExpiresIn), nil } @@ -240,10 +225,8 @@ func (a *YandexCloudMonitoring) send(body []byte) error { req.Header.Set("Content-Type", "application/json") isTokenExpired := !a.IamTokenExpirationTime.After(time.Now()) - if a.IAMTokenFromConfig != "" { - a.IAMToken = a.IAMTokenFromConfig - } else if isTokenExpired { - token, expiresIn, err := getIAMTokenFromMetadata(a.client, a.MetadataTokenUrl) + if a.IAMToken == "" || isTokenExpired { + token, expiresIn, err := a.getIAMTokenFromMetadata() if err != nil { return err } @@ -252,7 +235,7 @@ func (a *YandexCloudMonitoring) send(body []byte) error { } req.Header.Set("Authorization", "Bearer "+a.IAMToken) - log.Printf("!D sending metrics to %s", req.URL.String()) + a.Log.Debugf("sending metrics to %s", req.URL.String()) resp, err := a.client.Do(req) if err != nil { return err diff --git a/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring_test.go b/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring_test.go index 0d9c2e0a4e03c..52e9be0afcb06 100644 --- a/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring_test.go +++ b/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring_test.go @@ -18,9 +18,7 @@ func TestWrite(t *testing.T) { decoder := json.NewDecoder(r.Body) var message yandexCloudMonitoringMessage err := decoder.Decode(&message) - if err != nil { - panic(err) - } + require.NoError(t, err) return message, nil } @@ -83,6 +81,7 @@ func TestWrite(t *testing.T) { ts.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { tt.handler(t, w, r) }) + tt.plugin.Log = testutil.Logger{} tt.plugin.EndpointUrl = url tt.plugin.MetadataTokenUrl = metadataTokenUrl tt.plugin.MetadataFolderUrl = metadataFolderUrl From 1f9145c9aadbffd287613dae3a98f95c63eb1b67 Mon Sep 17 00:00:00 2001 From: peevo Date: Mon, 2 Nov 2020 17:46:16 +0300 Subject: [PATCH 4/4] #8295 increase default http timeout --- .../outputs/yandex_cloud_monitoring/README.md | 2 +- .../yandex_cloud_monitoring.go | 28 +++++++++---------- .../yandex_cloud_monitoring_test.go | 4 +-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/plugins/outputs/yandex_cloud_monitoring/README.md b/plugins/outputs/yandex_cloud_monitoring/README.md index 2b98729889b94..3bace22b4adb2 100644 --- a/plugins/outputs/yandex_cloud_monitoring/README.md +++ b/plugins/outputs/yandex_cloud_monitoring/README.md @@ -8,7 +8,7 @@ https://cloud.yandex.com/services/monitoring ```toml [[outputs.yandex_cloud_monitoring]] ## Timeout for HTTP writes. - # timeout = "5s" + # timeout = "20s" ## Yandex.Cloud monitoring API endpoint. Normally should not be changed # endpoint_url = "https://monitoring.api.cloud.yandex.net/monitoring/v2/data/write" diff --git a/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring.go b/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring.go index fe617621eac58..36fd4ab0bef9f 100644 --- a/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring.go +++ b/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring.go @@ -23,8 +23,8 @@ type YandexCloudMonitoring struct { Log telegraf.Logger - MetadataTokenUrl string - MetadataFolderUrl string + MetadataTokenURL string + MetadataFolderURL string FolderID string IAMToken string IamTokenExpirationTime time.Time @@ -57,7 +57,7 @@ type MetadataIamToken struct { } const ( - defaultRequestTimeout = time.Second * 5 + defaultRequestTimeout = time.Second * 20 defaultEndpointUrl = "https://monitoring.api.cloud.yandex.net/monitoring/v2/data/write" defaultMetadataTokenUrl = "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token" defaultMetadataFolderUrl = "http://169.254.169.254/computeMetadata/v1/instance/attributes/folder-id" @@ -65,7 +65,7 @@ const ( var sampleConfig = ` ## Timeout for HTTP writes. - # timeout = "5s" + # timeout = "20s" ## Yandex.Cloud monitoring API endpoint. Normally should not be changed # endpoint_url = "https://monitoring.api.cloud.yandex.net/monitoring/v2/data/write" @@ -95,11 +95,11 @@ func (a *YandexCloudMonitoring) Connect() error { if a.Service == "" { a.Service = "custom" } - if a.MetadataTokenUrl == "" { - a.MetadataTokenUrl = defaultMetadataTokenUrl + if a.MetadataTokenURL == "" { + a.MetadataTokenURL = defaultMetadataTokenUrl } - if a.MetadataFolderUrl == "" { - a.MetadataFolderUrl = defaultMetadataFolderUrl + if a.MetadataFolderURL == "" { + a.MetadataFolderURL = defaultMetadataFolderUrl } a.client = &http.Client{ @@ -185,21 +185,21 @@ func getResponseFromMetadata(c *http.Client, metadataUrl string) ([]byte, error) } func (a *YandexCloudMonitoring) getFolderIDFromMetadata() (string, error) { - a.Log.Infof("getting folder ID in %s", a.MetadataFolderUrl) - body, err := getResponseFromMetadata(a.client, a.MetadataFolderUrl) + a.Log.Infof("getting folder ID in %s", a.MetadataFolderURL) + body, err := getResponseFromMetadata(a.client, a.MetadataFolderURL) if err != nil { return "", err } folderID := string(body) if folderID == "" { - return "", fmt.Errorf("unable to fetch folder id from URL %s: %v", a.MetadataFolderUrl, err) + return "", fmt.Errorf("unable to fetch folder id from URL %s: %v", a.MetadataFolderURL, err) } return folderID, nil } func (a *YandexCloudMonitoring) getIAMTokenFromMetadata() (string, int, error) { - a.Log.Debugf("getting new IAM token in %s", a.MetadataTokenUrl) - body, err := getResponseFromMetadata(a.client, a.MetadataTokenUrl) + a.Log.Debugf("getting new IAM token in %s", a.MetadataTokenURL) + body, err := getResponseFromMetadata(a.client, a.MetadataTokenURL) if err != nil { return "", 0, err } @@ -208,7 +208,7 @@ func (a *YandexCloudMonitoring) getIAMTokenFromMetadata() (string, int, error) { return "", 0, err } if metadata.AccessToken == "" || metadata.ExpiresIn == 0 { - return "", 0, fmt.Errorf("unable to fetch authentication credentials %s: %v", a.MetadataTokenUrl, err) + return "", 0, fmt.Errorf("unable to fetch authentication credentials %s: %v", a.MetadataTokenURL, err) } return metadata.AccessToken, int(metadata.ExpiresIn), nil } diff --git a/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring_test.go b/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring_test.go index 52e9be0afcb06..edd2960bf0cff 100644 --- a/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring_test.go +++ b/plugins/outputs/yandex_cloud_monitoring/yandex_cloud_monitoring_test.go @@ -83,8 +83,8 @@ func TestWrite(t *testing.T) { }) tt.plugin.Log = testutil.Logger{} tt.plugin.EndpointUrl = url - tt.plugin.MetadataTokenUrl = metadataTokenUrl - tt.plugin.MetadataFolderUrl = metadataFolderUrl + tt.plugin.MetadataTokenURL = metadataTokenUrl + tt.plugin.MetadataFolderURL = metadataFolderUrl err := tt.plugin.Connect() require.NoError(t, err)