Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
266
plugins/inputs/http/http.go
Normal file
266
plugins/inputs/http/http.go
Normal file
|
@ -0,0 +1,266 @@
|
|||
//go:generate ../../../tools/config_includer/generator
|
||||
//go:generate ../../../tools/readme_config_includer/generator
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/internal"
|
||||
common_http "github.com/influxdata/telegraf/plugins/common/http"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
//go:embed sample.conf
|
||||
var sampleConfig string
|
||||
|
||||
var once sync.Once
|
||||
|
||||
type HTTP struct {
|
||||
URLs []string `toml:"urls"`
|
||||
Method string `toml:"method"`
|
||||
Body string `toml:"body"`
|
||||
ContentEncoding string `toml:"content_encoding"`
|
||||
|
||||
// Basic authentication
|
||||
Username config.Secret `toml:"username"`
|
||||
Password config.Secret `toml:"password"`
|
||||
|
||||
// Bearer authentication
|
||||
BearerToken string `toml:"bearer_token" deprecated:"1.28.0;1.35.0;use 'token_file' instead"`
|
||||
Token config.Secret `toml:"token"`
|
||||
TokenFile string `toml:"token_file"`
|
||||
|
||||
Headers map[string]*config.Secret `toml:"headers"`
|
||||
SuccessStatusCodes []int `toml:"success_status_codes"`
|
||||
Log telegraf.Logger `toml:"-"`
|
||||
|
||||
common_http.HTTPClientConfig
|
||||
|
||||
client *http.Client
|
||||
parserFunc telegraf.ParserFunc
|
||||
}
|
||||
|
||||
func (*HTTP) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
func (h *HTTP) Init() error {
|
||||
// For backward compatibility
|
||||
if h.TokenFile != "" && h.BearerToken != "" && h.TokenFile != h.BearerToken {
|
||||
return errors.New("conflicting settings for 'bearer_token' and 'token_file'")
|
||||
} else if h.TokenFile == "" && h.BearerToken != "" {
|
||||
h.TokenFile = h.BearerToken
|
||||
}
|
||||
|
||||
// We cannot use multiple sources for tokens
|
||||
if h.TokenFile != "" && !h.Token.Empty() {
|
||||
return errors.New("either use 'token_file' or 'token' not both")
|
||||
}
|
||||
|
||||
// Create the client
|
||||
ctx := context.Background()
|
||||
client, err := h.HTTPClientConfig.CreateClient(ctx, h.Log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.client = client
|
||||
|
||||
// Set default as [200]
|
||||
if len(h.SuccessStatusCodes) == 0 {
|
||||
h.SuccessStatusCodes = []int{200}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HTTP) SetParserFunc(fn telegraf.ParserFunc) {
|
||||
h.parserFunc = fn
|
||||
}
|
||||
|
||||
func (*HTTP) Start(telegraf.Accumulator) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HTTP) Gather(acc telegraf.Accumulator) error {
|
||||
var wg sync.WaitGroup
|
||||
for _, u := range h.URLs {
|
||||
wg.Add(1)
|
||||
go func(url string) {
|
||||
defer wg.Done()
|
||||
if err := h.gatherURL(acc, url); err != nil {
|
||||
acc.AddError(fmt.Errorf("[url=%s]: %w", url, err))
|
||||
}
|
||||
}(u)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HTTP) Stop() {
|
||||
if h.client != nil {
|
||||
h.client.CloseIdleConnections()
|
||||
}
|
||||
}
|
||||
|
||||
// Gathers data from a particular URL
|
||||
// Parameters:
|
||||
//
|
||||
// acc : The telegraf Accumulator to use
|
||||
// url : endpoint to send request to
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// error: Any error that may have occurred
|
||||
func (h *HTTP) gatherURL(acc telegraf.Accumulator, url string) error {
|
||||
body := makeRequestBodyReader(h.ContentEncoding, h.Body)
|
||||
request, err := http.NewRequest(h.Method, url, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !h.Token.Empty() {
|
||||
token, err := h.Token.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bearer := "Bearer " + strings.TrimSpace(token.String())
|
||||
token.Destroy()
|
||||
request.Header.Set("Authorization", bearer)
|
||||
} else if h.TokenFile != "" {
|
||||
token, err := os.ReadFile(h.TokenFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bearer := "Bearer " + strings.Trim(string(token), "\n")
|
||||
request.Header.Set("Authorization", bearer)
|
||||
}
|
||||
|
||||
if h.ContentEncoding == "gzip" {
|
||||
request.Header.Set("Content-Encoding", "gzip")
|
||||
}
|
||||
|
||||
for k, v := range h.Headers {
|
||||
secret, err := v.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
headerVal := secret.String()
|
||||
if strings.EqualFold(k, "host") {
|
||||
request.Host = headerVal
|
||||
} else {
|
||||
request.Header.Add(k, headerVal)
|
||||
}
|
||||
|
||||
secret.Destroy()
|
||||
}
|
||||
|
||||
if err := h.setRequestAuth(request); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := h.client.Do(request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
responseHasSuccessCode := false
|
||||
for _, statusCode := range h.SuccessStatusCodes {
|
||||
if resp.StatusCode == statusCode {
|
||||
responseHasSuccessCode = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !responseHasSuccessCode {
|
||||
return fmt.Errorf("received status code %d (%s), expected any value out of %v",
|
||||
resp.StatusCode,
|
||||
http.StatusText(resp.StatusCode),
|
||||
h.SuccessStatusCodes)
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading body failed: %w", err)
|
||||
}
|
||||
|
||||
// Instantiate a new parser for the new data to avoid trouble with stateful parsers
|
||||
parser, err := h.parserFunc()
|
||||
if err != nil {
|
||||
return fmt.Errorf("instantiating parser failed: %w", err)
|
||||
}
|
||||
metrics, err := parser.Parse(b)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing metrics failed: %w", err)
|
||||
}
|
||||
|
||||
if len(metrics) == 0 {
|
||||
once.Do(func() {
|
||||
h.Log.Debug(internal.NoMetricsCreatedMsg)
|
||||
})
|
||||
}
|
||||
|
||||
for _, metric := range metrics {
|
||||
if !metric.HasTag("url") {
|
||||
metric.AddTag("url", url)
|
||||
}
|
||||
acc.AddFields(metric.Name(), metric.Fields(), metric.Tags(), metric.Time())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HTTP) setRequestAuth(request *http.Request) error {
|
||||
if h.Username.Empty() && h.Password.Empty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
username, err := h.Username.Get()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting username failed: %w", err)
|
||||
}
|
||||
defer username.Destroy()
|
||||
|
||||
password, err := h.Password.Get()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting password failed: %w", err)
|
||||
}
|
||||
defer password.Destroy()
|
||||
|
||||
request.SetBasicAuth(username.String(), password.String())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeRequestBodyReader(contentEncoding, body string) io.Reader {
|
||||
if body == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var reader io.Reader = strings.NewReader(body)
|
||||
if contentEncoding == "gzip" {
|
||||
return internal.CompressWithGzip(reader)
|
||||
}
|
||||
|
||||
return reader
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("http", func() telegraf.Input {
|
||||
return &HTTP{
|
||||
Method: "GET",
|
||||
}
|
||||
})
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue