1
0
Fork 0

Adding upstream version 1.34.4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 07:26:29 +02:00
parent e393c3af3f
commit 4978089aab
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
4963 changed files with 677545 additions and 0 deletions

View file

@ -0,0 +1,68 @@
# Salesforce Input Plugin
The Salesforce plugin gathers metrics about the limits in your Salesforce
organization and the remaining usage. It fetches its data from the [limits
endpoint][limits] of Salesforce's REST API.
[limits]: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_limits.htm
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
In addition to the plugin-specific configuration settings, plugins support
additional global and plugin configuration settings. These settings are used to
modify metrics, tags, and field or create aliases and configure ordering, etc.
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
## Configuration
```toml @sample.conf
# Read API usage and limits for a Salesforce organisation
[[inputs.salesforce]]
## specify your credentials
##
username = "your_username"
password = "your_password"
##
## (optional) security token
# security_token = "your_security_token"
##
## (optional) environment type (sandbox or production)
## default is: production
##
# environment = "production"
##
## (optional) API version (default: "39.0")
##
# version = "39.0"
```
## Metrics
Salesforce provide one measurement named "salesforce".
Each entry is converted to snake\_case and 2 fields are created.
- \<key\>_max represents the limit threshold
- \<key\>_remaining represents the usage remaining before hitting the limit threshold
- salesforce
- \<key\>_max (int)
- \<key\>_remaining (int)
- (...)
### Tags
- All measurements have the following tags:
- host
- organization_id (t18 char organisation ID)
## Example Output
```sh
$./telegraf --config telegraf.conf --input-filter salesforce --test
```
```text
salesforce,organization_id=XXXXXXXXXXXXXXXXXX,host=xxxxx.salesforce.com daily_workflow_emails_max=546000i,hourly_time_based_workflow_max=50i,daily_async_apex_executions_remaining=250000i,daily_durable_streaming_api_events_remaining=1000000i,streaming_api_concurrent_clients_remaining=2000i,daily_bulk_api_requests_remaining=10000i,hourly_sync_report_runs_remaining=500i,daily_api_requests_max=5000000i,data_storage_mb_remaining=1073i,file_storage_mb_remaining=1069i,daily_generic_streaming_api_events_remaining=10000i,hourly_async_report_runs_remaining=1200i,hourly_time_based_workflow_remaining=50i,daily_streaming_api_events_remaining=1000000i,single_email_max=5000i,hourly_dashboard_refreshes_remaining=200i,streaming_api_concurrent_clients_max=2000i,daily_durable_generic_streaming_api_events_remaining=1000000i,daily_api_requests_remaining=4999998i,hourly_dashboard_results_max=5000i,hourly_async_report_runs_max=1200i,daily_durable_generic_streaming_api_events_max=1000000i,hourly_dashboard_results_remaining=5000i,concurrent_sync_report_runs_max=20i,durable_streaming_api_concurrent_clients_remaining=2000i,daily_workflow_emails_remaining=546000i,hourly_dashboard_refreshes_max=200i,daily_streaming_api_events_max=1000000i,hourly_sync_report_runs_max=500i,hourly_o_data_callout_max=10000i,mass_email_max=5000i,mass_email_remaining=5000i,single_email_remaining=5000i,hourly_dashboard_statuses_max=999999999i,concurrent_async_get_report_instances_max=200i,daily_durable_streaming_api_events_max=1000000i,daily_generic_streaming_api_events_max=10000i,hourly_o_data_callout_remaining=10000i,concurrent_sync_report_runs_remaining=20i,daily_bulk_api_requests_max=10000i,data_storage_mb_max=1073i,hourly_dashboard_statuses_remaining=999999999i,concurrent_async_get_report_instances_remaining=200i,daily_async_apex_executions_max=250000i,durable_streaming_api_concurrent_clients_max=2000i,file_storage_mb_max=1073i 1501565661000000000
```

View file

@ -0,0 +1,233 @@
//go:generate ../../../tools/readme_config_includer/generator
package salesforce
import (
_ "embed"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/plugins/inputs"
)
//go:embed sample.conf
var sampleConfig string
const (
defaultVersion = "39.0"
defaultEnvironment = "production"
)
type Salesforce struct {
Username string `toml:"username"`
Password string `toml:"password"`
SecurityToken string `toml:"security_token"`
Environment string `toml:"environment"`
Version string `toml:"version"`
sessionID string
serverURL *url.URL
organizationID string
client *http.Client
}
type limit struct {
Max int
Remaining int
}
type limits map[string]limit
func (*Salesforce) SampleConfig() string {
return sampleConfig
}
func (s *Salesforce) Gather(acc telegraf.Accumulator) error {
limits, err := s.fetchLimits()
if err != nil {
return err
}
tags := map[string]string{
"organization_id": s.organizationID,
"host": s.serverURL.Host,
}
fields := make(map[string]interface{})
for k, v := range limits {
key := internal.SnakeCase(k)
fields[key+"_max"] = v.Max
fields[key+"_remaining"] = v.Remaining
}
acc.AddFields("salesforce", fields, tags)
return nil
}
// query the limits endpoint
func (s *Salesforce) queryLimits() (*http.Response, error) {
endpoint := fmt.Sprintf("%s://%s/services/data/v%s/limits", s.serverURL.Scheme, s.serverURL.Host, s.Version)
req, err := http.NewRequest(http.MethodGet, endpoint, nil)
if err != nil {
return nil, err
}
req.Header.Add("Accept", "encoding/json")
req.Header.Add("Authorization", "Bearer "+s.sessionID)
return s.client.Do(req)
}
func (s *Salesforce) isAuthenticated() bool {
return s.sessionID != ""
}
func (s *Salesforce) fetchLimits() (limits, error) {
var l limits
if !s.isAuthenticated() {
if err := s.login(); err != nil {
return l, err
}
}
resp, err := s.queryLimits()
if err != nil {
return l, err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusUnauthorized {
if err := s.login(); err != nil {
return l, err
}
resp, err = s.queryLimits()
if err != nil {
return l, err
}
defer resp.Body.Close()
}
if resp.StatusCode != http.StatusOK {
return l, fmt.Errorf("salesforce responded with unexpected status code %d", resp.StatusCode)
}
l = make(limits)
err = json.NewDecoder(resp.Body).Decode(&l)
return l, err
}
func (s *Salesforce) getLoginEndpoint() (string, error) {
switch s.Environment {
case "sandbox":
return fmt.Sprintf("https://test.salesforce.com/services/Soap/c/%s/", s.Version), nil
case "production":
return fmt.Sprintf("https://login.salesforce.com/services/Soap/c/%s/", s.Version), nil
default:
return "", fmt.Errorf("unknown environment type: %s", s.Environment)
}
}
// Authenticate with Salesforce
func (s *Salesforce) login() error {
if s.Username == "" || s.Password == "" {
return errors.New("missing username or password")
}
body := fmt.Sprintf(`<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:urn="urn:enterprise.soap.sforce.com">
<soapenv:Body>
<urn:login>
<urn:username>%s</urn:username>
<urn:password>%s%s</urn:password>
</urn:login>
</soapenv:Body>
</soapenv:Envelope>`,
s.Username, s.Password, s.SecurityToken)
loginEndpoint, err := s.getLoginEndpoint()
if err != nil {
return err
}
req, err := http.NewRequest(http.MethodPost, loginEndpoint, strings.NewReader(body))
if err != nil {
return err
}
req.Header.Add("Content-Type", "text/xml")
req.Header.Add("SOAPAction", "login")
resp, err := s.client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
//nolint:errcheck // LimitReader returns io.EOF and we're not interested in read errors.
body, _ := io.ReadAll(io.LimitReader(resp.Body, 200))
return fmt.Errorf("%s returned HTTP status %s: %q", loginEndpoint, resp.Status, body)
}
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
soapFault := struct {
Code string `xml:"Body>Fault>faultcode"`
Message string `xml:"Body>Fault>faultstring"`
}{}
err = xml.Unmarshal(respBody, &soapFault)
if err != nil {
return err
}
if soapFault.Code != "" {
return fmt.Errorf("login failed: %s", soapFault.Message)
}
loginResult := struct {
ServerURL string `xml:"Body>loginResponse>result>serverUrl"`
SessionID string `xml:"Body>loginResponse>result>sessionId"`
OrganizationID string `xml:"Body>loginResponse>result>userInfo>organizationId"`
}{}
err = xml.Unmarshal(respBody, &loginResult)
if err != nil {
return err
}
s.sessionID = loginResult.SessionID
s.organizationID = loginResult.OrganizationID
s.serverURL, err = url.Parse(loginResult.ServerURL)
return err
}
func newSalesforce() *Salesforce {
tr := &http.Transport{
ResponseHeaderTimeout: 5 * time.Second,
}
client := &http.Client{
Transport: tr,
Timeout: 10 * time.Second,
}
return &Salesforce{
client: client,
Version: defaultVersion,
Environment: defaultEnvironment}
}
func init() {
inputs.Add("salesforce", func() telegraf.Input {
return newSalesforce()
})
}

View file

@ -0,0 +1,519 @@
package salesforce
import (
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf/testutil"
)
func Test_Gather(t *testing.T) {
fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Add("Content-Type", "application/json")
if _, err := w.Write([]byte(testJSON)); err != nil {
w.WriteHeader(http.StatusInternalServerError)
t.Error(err)
return
}
}))
defer fakeServer.Close()
plugin := newSalesforce()
plugin.sessionID = "test_session"
u, err := url.Parse(fakeServer.URL)
if err != nil {
t.Error(err)
}
plugin.serverURL = u
var acc testutil.Accumulator
require.NoError(t, acc.GatherError(plugin.Gather))
require.Len(t, acc.Metrics, 1)
m := acc.Metrics[0]
require.Len(t, m.Fields, 46)
require.Len(t, m.Tags, 2)
}
var testJSON = `{
"ConcurrentAsyncGetReportInstances" : {
"Max" : 200,
"Remaining" : 200
},
"ConcurrentSyncReportRuns" : {
"Max" : 20,
"Remaining" : 20
},
"DailyApiRequests" : {
"Max" : 25000,
"Remaining" : 24926,
"AgilePoint" : {
"Max" : 0,
"Remaining" : 0
},
"Ant Migration Tool" : {
"Max" : 0,
"Remaining" : 0
},
"Axsy Server Integration" : {
"Max" : 0,
"Remaining" : 0
},
"Chatter Desktop" : {
"Max" : 0,
"Remaining" : 0
},
"Chatter Mobile for BlackBerry" : {
"Max" : 0,
"Remaining" : 0
},
"Dataloader Bulk" : {
"Max" : 0,
"Remaining" : 0
},
"Dataloader Partner" : {
"Max" : 0,
"Remaining" : 0
},
"EAHelperBot" : {
"Max" : 0,
"Remaining" : 0
},
"Force.com IDE" : {
"Max" : 0,
"Remaining" : 0
},
"LiveText for Salesforce" : {
"Max" : 0,
"Remaining" : 0
},
"LiveText for Salesforce (QA)" : {
"Max" : 0,
"Remaining" : 0
},
"MyU App" : {
"Max" : 0,
"Remaining" : 0
},
"SMS Magic Interact" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Chatter" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Files" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Mobile Dashboards" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Social Customer Service (SCS)" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Touch" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce for Outlook" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce1 for Android" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce1 for iOS" : {
"Max" : 0,
"Remaining" : 0
},
"SalesforceA" : {
"Max" : 0,
"Remaining" : 0
},
"SalesforceIQ" : {
"Max" : 0,
"Remaining" : 0
},
"Workbench" : {
"Max" : 0,
"Remaining" : 0
}
},
"DailyAsyncApexExecutions" : {
"Max" : 250000,
"Remaining" : 250000
},
"DailyBulkApiRequests" : {
"Max" : 10000,
"Remaining" : 10000,
"AgilePoint" : {
"Max" : 0,
"Remaining" : 0
},
"Ant Migration Tool" : {
"Max" : 0,
"Remaining" : 0
},
"Axsy Server Integration" : {
"Max" : 0,
"Remaining" : 0
},
"Chatter Desktop" : {
"Max" : 0,
"Remaining" : 0
},
"Chatter Mobile for BlackBerry" : {
"Max" : 0,
"Remaining" : 0
},
"Dataloader Bulk" : {
"Max" : 0,
"Remaining" : 0
},
"Dataloader Partner" : {
"Max" : 0,
"Remaining" : 0
},
"EAHelperBot" : {
"Max" : 0,
"Remaining" : 0
},
"Force.com IDE" : {
"Max" : 0,
"Remaining" : 0
},
"LiveText for Salesforce" : {
"Max" : 0,
"Remaining" : 0
},
"LiveText for Salesforce (QA)" : {
"Max" : 0,
"Remaining" : 0
},
"MyU App" : {
"Max" : 0,
"Remaining" : 0
},
"SMS Magic Interact" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Chatter" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Files" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Mobile Dashboards" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Social Customer Service (SCS)" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Touch" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce for Outlook" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce1 for Android" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce1 for iOS" : {
"Max" : 0,
"Remaining" : 0
},
"SalesforceA" : {
"Max" : 0,
"Remaining" : 0
},
"SalesforceIQ" : {
"Max" : 0,
"Remaining" : 0
},
"Workbench" : {
"Max" : 0,
"Remaining" : 0
}
},
"DailyDurableGenericStreamingApiEvents" : {
"Max" : 10000,
"Remaining" : 10000
},
"DailyDurableStreamingApiEvents" : {
"Max" : 10000,
"Remaining" : 10000
},
"DailyGenericStreamingApiEvents" : {
"Max" : 10000,
"Remaining" : 10000,
"AgilePoint" : {
"Max" : 0,
"Remaining" : 0
},
"Ant Migration Tool" : {
"Max" : 0,
"Remaining" : 0
},
"Axsy Server Integration" : {
"Max" : 0,
"Remaining" : 0
},
"Chatter Desktop" : {
"Max" : 0,
"Remaining" : 0
},
"Chatter Mobile for BlackBerry" : {
"Max" : 0,
"Remaining" : 0
},
"Dataloader Bulk" : {
"Max" : 0,
"Remaining" : 0
},
"Dataloader Partner" : {
"Max" : 0,
"Remaining" : 0
},
"EAHelperBot" : {
"Max" : 0,
"Remaining" : 0
},
"Force.com IDE" : {
"Max" : 0,
"Remaining" : 0
},
"LiveText for Salesforce" : {
"Max" : 0,
"Remaining" : 0
},
"LiveText for Salesforce (QA)" : {
"Max" : 0,
"Remaining" : 0
},
"MyU App" : {
"Max" : 0,
"Remaining" : 0
},
"SMS Magic Interact" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Chatter" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Files" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Mobile Dashboards" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Social Customer Service (SCS)" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Touch" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce for Outlook" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce1 for Android" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce1 for iOS" : {
"Max" : 0,
"Remaining" : 0
},
"SalesforceA" : {
"Max" : 0,
"Remaining" : 0
},
"SalesforceIQ" : {
"Max" : 0,
"Remaining" : 0
},
"Workbench" : {
"Max" : 0,
"Remaining" : 0
}
},
"DailyStreamingApiEvents" : {
"Max" : 20000,
"Remaining" : 20000,
"AgilePoint" : {
"Max" : 0,
"Remaining" : 0
},
"Ant Migration Tool" : {
"Max" : 0,
"Remaining" : 0
},
"Axsy Server Integration" : {
"Max" : 0,
"Remaining" : 0
},
"Chatter Desktop" : {
"Max" : 0,
"Remaining" : 0
},
"Chatter Mobile for BlackBerry" : {
"Max" : 0,
"Remaining" : 0
},
"Dataloader Bulk" : {
"Max" : 0,
"Remaining" : 0
},
"Dataloader Partner" : {
"Max" : 0,
"Remaining" : 0
},
"EAHelperBot" : {
"Max" : 0,
"Remaining" : 0
},
"Force.com IDE" : {
"Max" : 0,
"Remaining" : 0
},
"LiveText for Salesforce" : {
"Max" : 0,
"Remaining" : 0
},
"LiveText for Salesforce (QA)" : {
"Max" : 0,
"Remaining" : 0
},
"MyU App" : {
"Max" : 0,
"Remaining" : 0
},
"SMS Magic Interact" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Chatter" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Files" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Mobile Dashboards" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Social Customer Service (SCS)" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce Touch" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce for Outlook" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce1 for Android" : {
"Max" : 0,
"Remaining" : 0
},
"Salesforce1 for iOS" : {
"Max" : 0,
"Remaining" : 0
},
"SalesforceA" : {
"Max" : 0,
"Remaining" : 0
},
"SalesforceIQ" : {
"Max" : 0,
"Remaining" : 0
},
"Workbench" : {
"Max" : 0,
"Remaining" : 0
}
},
"DailyWorkflowEmails" : {
"Max" : 20000,
"Remaining" : 20000
},
"DataStorageMB" : {
"Max" : 209,
"Remaining" : 207
},
"DurableStreamingApiConcurrentClients" : {
"Max" : 20,
"Remaining" : 20
},
"FileStorageMB" : {
"Max" : 209,
"Remaining" : 206
},
"HourlyAsyncReportRuns" : {
"Max" : 1200,
"Remaining" : 1200
},
"HourlyDashboardRefreshes" : {
"Max" : 200,
"Remaining" : 200
},
"HourlyDashboardResults" : {
"Max" : 5000,
"Remaining" : 5000
},
"HourlyDashboardStatuses" : {
"Max" : 999999999,
"Remaining" : 999999999
},
"HourlyODataCallout" : {
"Max" : 20000,
"Remaining" : 19998
},
"HourlySyncReportRuns" : {
"Max" : 500,
"Remaining" : 500
},
"HourlyTimeBasedWorkflow" : {
"Max" : 50,
"Remaining" : 50
},
"MassEmail" : {
"Max" : 5000,
"Remaining" : 5000
},
"SingleEmail" : {
"Max" : 5000,
"Remaining" : 5000
},
"StreamingApiConcurrentClients" : {
"Max" : 20,
"Remaining" : 20
}
}`

View file

@ -0,0 +1,18 @@
# Read API usage and limits for a Salesforce organisation
[[inputs.salesforce]]
## specify your credentials
##
username = "your_username"
password = "your_password"
##
## (optional) security token
# security_token = "your_security_token"
##
## (optional) environment type (sandbox or production)
## default is: production
##
# environment = "production"
##
## (optional) API version (default: "39.0")
##
# version = "39.0"