feat: Supervisord input plugin (#9015)
This commit is contained in:
parent
401d2b2a28
commit
b80d34a422
|
|
@ -188,6 +188,7 @@ following works:
|
||||||
- github.com/karrick/godirwalk [BSD 2-Clause "Simplified" License](https://github.com/karrick/godirwalk/blob/master/LICENSE)
|
- github.com/karrick/godirwalk [BSD 2-Clause "Simplified" License](https://github.com/karrick/godirwalk/blob/master/LICENSE)
|
||||||
- github.com/kballard/go-shellquote [MIT License](https://github.com/kballard/go-shellquote/blob/master/LICENSE)
|
- github.com/kballard/go-shellquote [MIT License](https://github.com/kballard/go-shellquote/blob/master/LICENSE)
|
||||||
- github.com/klauspost/compress [BSD 3-Clause Clear License](https://github.com/klauspost/compress/blob/master/LICENSE)
|
- github.com/klauspost/compress [BSD 3-Clause Clear License](https://github.com/klauspost/compress/blob/master/LICENSE)
|
||||||
|
- github.com/kolo/xmlrpc [MIT License](https://github.com/kolo/xmlrpc/blob/master/LICENSE)
|
||||||
- github.com/kylelemons/godebug [Apache License 2.0](https://github.com/kylelemons/godebug/blob/master/LICENSE)
|
- github.com/kylelemons/godebug [Apache License 2.0](https://github.com/kylelemons/godebug/blob/master/LICENSE)
|
||||||
- github.com/leodido/ragel-machinery [MIT License](https://github.com/leodido/ragel-machinery/blob/develop/LICENSE)
|
- github.com/leodido/ragel-machinery [MIT License](https://github.com/leodido/ragel-machinery/blob/develop/LICENSE)
|
||||||
- github.com/magiconair/properties [BSD 2-Clause "Simplified" License](https://github.com/magiconair/properties/blob/main/LICENSE.md)
|
- github.com/magiconair/properties [BSD 2-Clause "Simplified" License](https://github.com/magiconair/properties/blob/main/LICENSE.md)
|
||||||
|
|
|
||||||
1
go.mod
1
go.mod
|
|
@ -302,6 +302,7 @@ require (
|
||||||
github.com/juju/webbrowser v1.0.0 // indirect
|
github.com/juju/webbrowser v1.0.0 // indirect
|
||||||
github.com/julienschmidt/httprouter v1.3.0 // indirect
|
github.com/julienschmidt/httprouter v1.3.0 // indirect
|
||||||
github.com/klauspost/compress v1.15.8 // indirect
|
github.com/klauspost/compress v1.15.8 // indirect
|
||||||
|
github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b
|
||||||
github.com/kr/fs v0.1.0 // indirect
|
github.com/kr/fs v0.1.0 // indirect
|
||||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
github.com/leodido/ragel-machinery v0.0.0-20181214104525-299bdde78165 // indirect
|
github.com/leodido/ragel-machinery v0.0.0-20181214104525-299bdde78165 // indirect
|
||||||
|
|
|
||||||
3
go.sum
3
go.sum
|
|
@ -1524,6 +1524,8 @@ github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPR
|
||||||
github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||||
github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||||
|
github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b h1:iNjcivnc6lhbvJA3LD622NPrUponluJrBWPIwGG/3Bg=
|
||||||
|
github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
|
@ -3339,6 +3341,7 @@ k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc=
|
||||||
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||||
k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
||||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||||
|
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||||
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||||
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||||
|
|
|
||||||
|
|
@ -187,6 +187,7 @@ import (
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/sqlserver"
|
_ "github.com/influxdata/telegraf/plugins/inputs/sqlserver"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/stackdriver"
|
_ "github.com/influxdata/telegraf/plugins/inputs/stackdriver"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/statsd"
|
_ "github.com/influxdata/telegraf/plugins/inputs/statsd"
|
||||||
|
_ "github.com/influxdata/telegraf/plugins/inputs/supervisor"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/suricata"
|
_ "github.com/influxdata/telegraf/plugins/inputs/suricata"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/swap"
|
_ "github.com/influxdata/telegraf/plugins/inputs/swap"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/synproxy"
|
_ "github.com/influxdata/telegraf/plugins/inputs/synproxy"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
# Supervisor Input Plugin
|
||||||
|
|
||||||
|
This plugin gathers information about processes that
|
||||||
|
running under supervisor using XML-RPC API.
|
||||||
|
|
||||||
|
Minimum tested version of supervisor: 3.3.2
|
||||||
|
|
||||||
|
## Supervisor configuration
|
||||||
|
|
||||||
|
This plugin needs an HTTP server to be enabled in supervisor,
|
||||||
|
also it's recommended to enable basic authentication on the
|
||||||
|
HTTP server. When using basic authentication make sure to
|
||||||
|
include the username and password in the plugin's url setting.
|
||||||
|
Here is an example of the `inet_http_server` section in supervisor's
|
||||||
|
config that will work with default plugin configuration:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[inet_http_server]
|
||||||
|
port = 127.0.0.1:9001
|
||||||
|
username = user
|
||||||
|
password = pass
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[inputs.supervisor]
|
||||||
|
## Url of supervisor's XML-RPC endpoint if basic auth enabled in supervisor http server,
|
||||||
|
## than you have to add credentials to url (ex. http://login:pass@localhost:9001/RPC2)
|
||||||
|
# url="http://localhost:9001/RPC2"
|
||||||
|
## With settings below you can manage gathering additional information about processes
|
||||||
|
## If both of them empty, then all additional information will be collected.
|
||||||
|
## Currently supported supported additional metrics are: pid, rc
|
||||||
|
# metrics_include = []
|
||||||
|
# metrics_exclude = ["pid", "rc"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Optional metrics
|
||||||
|
|
||||||
|
You can control gathering of some supervisor's metrics (processes PIDs
|
||||||
|
and exit codes) by setting metrics_include and metrics_exclude parameters
|
||||||
|
in configuration file.
|
||||||
|
|
||||||
|
### Server tag
|
||||||
|
|
||||||
|
Server tag is used to identify metrics source server. You have an option
|
||||||
|
to use host:port pair of supervisor's http endpoint by default or you
|
||||||
|
can use supervisor's identification string, which is set in supervisor's
|
||||||
|
configuration file.
|
||||||
|
|
||||||
|
## Metrics
|
||||||
|
|
||||||
|
- supervisor_processes
|
||||||
|
- Tags:
|
||||||
|
- source (Hostname or IP address of supervisor's instance)
|
||||||
|
- port (Port number of supervisor's HTTP server)
|
||||||
|
- id (Supervisor's identification string)
|
||||||
|
- name (Process name)
|
||||||
|
- group (Process group)
|
||||||
|
- Fields:
|
||||||
|
- state (int, see reference)
|
||||||
|
- uptime (int, seconds)
|
||||||
|
- pid (int, optional)
|
||||||
|
- exitCode (int, optional)
|
||||||
|
|
||||||
|
- supervisor_instance
|
||||||
|
- Tags:
|
||||||
|
- source (Hostname or IP address of supervisor's instance)
|
||||||
|
- port (Port number of supervisor's HTTP server)
|
||||||
|
- id (Supervisor's identification string)
|
||||||
|
- Fields:
|
||||||
|
- state (int, see reference)
|
||||||
|
|
||||||
|
### Supervisor process state field reference table
|
||||||
|
|
||||||
|
|Statecode|Statename| Description |
|
||||||
|
|--------|----------|--------------------------------------------------------------------------------------------------------|
|
||||||
|
| 0 | STOPPED | The process has been stopped due to a stop request or has never been started. |
|
||||||
|
| 10 | STARTING | The process is starting due to a start request. |
|
||||||
|
| 20 | RUNNING | The process is running. |
|
||||||
|
| 30 | BACKOFF |The process entered the STARTING state but subsequently exited too quickly to move to the RUNNING state.|
|
||||||
|
| 40 | STOPPING | The process is stopping due to a stop request. |
|
||||||
|
| 100 | EXITED | The process exited from the RUNNING state (expectedly or unexpectedly). |
|
||||||
|
| 200 | FATAL | The process could not be started successfully. |
|
||||||
|
| 1000 | UNKNOWN | The process is in an unknown state (supervisord programming error). |
|
||||||
|
|
||||||
|
### Supervisor instance state field reference
|
||||||
|
|
||||||
|
|Statecode| Statename | Description |
|
||||||
|
|---------|------------|----------------------------------------------|
|
||||||
|
| 2 | FATAL | Supervisor has experienced a serious error. |
|
||||||
|
| 1 | RUNNING | Supervisor is working normally. |
|
||||||
|
| 0 | RESTARTING | Supervisor is in the process of restarting. |
|
||||||
|
| -1 | SHUTDOWN |Supervisor is in the process of shutting down.|
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
|
||||||
|
```shell
|
||||||
|
supervisor_processes,group=ExampleGroup,id=supervisor,port=9001,process=ExampleProcess,source=localhost state=20i,uptime=75958i 1659786637000000000
|
||||||
|
supervisor_instance,id=supervisor,port=9001,source=localhost state=1i 1659786637000000000
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Gathers information about processes that running under supervisor using XML-RPC API
|
||||||
|
[[inputs.supervisor]]
|
||||||
|
## Url of supervisor's XML-RPC endpoint if basic auth enabled in supervisor http server,
|
||||||
|
## than you have to add credentials to url (ex. http://login:pass@localhost:9001/RPC2)
|
||||||
|
# url="http://localhost:9001/RPC2"
|
||||||
|
## With settings below you can manage gathering additional information about processes
|
||||||
|
## If both of them empty, then all additional information will be collected.
|
||||||
|
## Currently supported supported additional metrics are: pid, rc
|
||||||
|
# metrics_include = []
|
||||||
|
# metrics_exclude = ["pid", "rc"]
|
||||||
|
|
@ -0,0 +1,180 @@
|
||||||
|
package supervisor
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/kolo/xmlrpc"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/filter"
|
||||||
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Supervisor struct {
|
||||||
|
Server string `toml:"url"`
|
||||||
|
MetricsInc []string `toml:"metrics_include"`
|
||||||
|
MetricsExc []string `toml:"metrics_exclude"`
|
||||||
|
Log telegraf.Logger `toml:"-"`
|
||||||
|
|
||||||
|
rpcClient *xmlrpc.Client
|
||||||
|
fieldFilter filter.Filter
|
||||||
|
}
|
||||||
|
|
||||||
|
type processInfo struct {
|
||||||
|
Name string `xmlrpc:"name"`
|
||||||
|
Group string `xmlrpc:"group"`
|
||||||
|
Description string `xmlrpc:"description"`
|
||||||
|
Start int32 `xmlrpc:"start"`
|
||||||
|
Stop int32 `xmlrpc:"stop"`
|
||||||
|
Now int32 `xmlrpc:"now"`
|
||||||
|
State int16 `xmlrpc:"state"`
|
||||||
|
Statename string `xmlrpc:"statename"`
|
||||||
|
StdoutLogfile string `xmlrpc:"stdout_logfile"`
|
||||||
|
StderrLogfile string `xmlrpc:"stderr_logfile"`
|
||||||
|
SpawnErr string `xmlrpc:"spawnerr"`
|
||||||
|
ExitStatus int8 `xmlrpc:"exitstatus"`
|
||||||
|
Pid int32 `xmlrpc:"pid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type supervisorInfo struct {
|
||||||
|
StateCode int8 `xmlrpc:"statecode"`
|
||||||
|
StateName string `xmlrpc:"statename"`
|
||||||
|
Ident string
|
||||||
|
}
|
||||||
|
|
||||||
|
// DO NOT REMOVE THE NEXT TWO LINES! This is required to embed the sampleConfig data.
|
||||||
|
//go:embed sample.conf
|
||||||
|
var sampleConfig string
|
||||||
|
|
||||||
|
func (s *Supervisor) Description() string {
|
||||||
|
return "Gather info about processes state, that running under supervisor using its XML-RPC API"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Supervisor) SampleConfig() string {
|
||||||
|
return sampleConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Supervisor) Gather(acc telegraf.Accumulator) error {
|
||||||
|
// API call to get information about all running processes
|
||||||
|
var rawProcessData []processInfo
|
||||||
|
err := s.rpcClient.Call("supervisor.getAllProcessInfo", nil, &rawProcessData)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get processes info: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// API call to get information about instance status
|
||||||
|
var status supervisorInfo
|
||||||
|
err = s.rpcClient.Call("supervisor.getState", nil, &status)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get processes info: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// API call to get identification string
|
||||||
|
err = s.rpcClient.Call("supervisor.getIdentification", nil, &status.Ident)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get instance identification: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterating through array of structs with processes info and adding fields to accumulator
|
||||||
|
for _, process := range rawProcessData {
|
||||||
|
processTags, processFields, err := s.parseProcessData(process, status)
|
||||||
|
if err != nil {
|
||||||
|
acc.AddError(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
acc.AddFields("supervisor_processes", processFields, processTags)
|
||||||
|
}
|
||||||
|
// Adding instance info fields to accumulator
|
||||||
|
instanceTags, instanceFields, err := s.parseInstanceData(status)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse instance data: %v", err)
|
||||||
|
}
|
||||||
|
acc.AddFields("supervisor_instance", instanceFields, instanceTags)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Supervisor) parseProcessData(pInfo processInfo, status supervisorInfo) (map[string]string, map[string]interface{}, error) {
|
||||||
|
tags := map[string]string{
|
||||||
|
"process": pInfo.Name,
|
||||||
|
"group": pInfo.Group,
|
||||||
|
}
|
||||||
|
fields := map[string]interface{}{
|
||||||
|
"uptime": pInfo.Now - pInfo.Start,
|
||||||
|
"state": pInfo.State,
|
||||||
|
}
|
||||||
|
if s.fieldFilter.Match("pid") {
|
||||||
|
fields["pid"] = pInfo.Pid
|
||||||
|
}
|
||||||
|
if s.fieldFilter.Match("rc") {
|
||||||
|
fields["exitCode"] = pInfo.ExitStatus
|
||||||
|
}
|
||||||
|
splittedURL, err := beautifyServerString(s.Server)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to parse server string: %v", err)
|
||||||
|
}
|
||||||
|
tags["id"] = status.Ident
|
||||||
|
tags["source"] = splittedURL[0]
|
||||||
|
tags["port"] = splittedURL[1]
|
||||||
|
return tags, fields, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parsing of supervisor instance data
|
||||||
|
func (s *Supervisor) parseInstanceData(status supervisorInfo) (map[string]string, map[string]interface{}, error) {
|
||||||
|
splittedURL, err := beautifyServerString(s.Server)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to parse server string: %v", err)
|
||||||
|
}
|
||||||
|
tags := map[string]string{}
|
||||||
|
tags["id"] = status.Ident
|
||||||
|
tags["source"] = splittedURL[0]
|
||||||
|
tags["port"] = splittedURL[1]
|
||||||
|
fields := map[string]interface{}{"state": status.StateCode}
|
||||||
|
return tags, fields, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Supervisor) Init() error {
|
||||||
|
// Using default server URL if none was specified in config
|
||||||
|
if s.Server == "" {
|
||||||
|
s.Server = "http://localhost:9001/RPC2"
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
// Initializing XML-RPC client
|
||||||
|
s.rpcClient, err = xmlrpc.NewClient(s.Server, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("XML-RPC client initialization failed: %v", err)
|
||||||
|
}
|
||||||
|
// Setting filter for additional metrics
|
||||||
|
s.fieldFilter, err = filter.NewIncludeExcludeFilter(s.MetricsInc, s.MetricsExc)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("metrics filter setup failed: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
inputs.Add("supervisor", func() telegraf.Input {
|
||||||
|
return &Supervisor{
|
||||||
|
MetricsExc: []string{"pid", "rc"},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to get only address and port from URL
|
||||||
|
func beautifyServerString(rawurl string) ([]string, error) {
|
||||||
|
parsedURL, err := url.Parse(rawurl)
|
||||||
|
splittedURL := strings.Split(parsedURL.Host, ":")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(splittedURL) < 2 {
|
||||||
|
if parsedURL.Scheme == "https" {
|
||||||
|
splittedURL[1] = "443"
|
||||||
|
} else {
|
||||||
|
splittedURL[1] = "80"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return splittedURL, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,176 @@
|
||||||
|
package supervisor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/testcontainers/testcontainers-go/wait"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestShort_SampleData(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
supervisorData supervisorInfo
|
||||||
|
sampleProcInfo []processInfo
|
||||||
|
expProcessFields []map[string]interface{}
|
||||||
|
expProcessTags []map[string]string
|
||||||
|
expInstanceFields map[string]interface{}
|
||||||
|
expInstancesTags map[string]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "Case 1",
|
||||||
|
sampleProcInfo: []processInfo{
|
||||||
|
{
|
||||||
|
Name: "Process0",
|
||||||
|
Group: "ProcessGroup0",
|
||||||
|
Description: "pid 112 uptime 0:12:11",
|
||||||
|
Start: 1615632853,
|
||||||
|
Stop: 0,
|
||||||
|
Now: 1615632853 + 731,
|
||||||
|
State: 20,
|
||||||
|
Statename: "RUNNING",
|
||||||
|
StdoutLogfile: "/var/log/supervisor/process0-stdout.log",
|
||||||
|
StderrLogfile: "/var/log/supervisor/process0-stdout.log",
|
||||||
|
SpawnErr: "",
|
||||||
|
ExitStatus: 0,
|
||||||
|
Pid: 112,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Process1",
|
||||||
|
Group: "ProcessGroup1",
|
||||||
|
Description: "pid 113 uptime 0:12:11",
|
||||||
|
Start: 1615632853,
|
||||||
|
Stop: 0,
|
||||||
|
Now: 1615632853 + 731,
|
||||||
|
State: 20,
|
||||||
|
Statename: "RUNNING",
|
||||||
|
StdoutLogfile: "/var/log/supervisor/process1-stdout.log",
|
||||||
|
StderrLogfile: "/var/log/supervisor/process1-stderr.log",
|
||||||
|
SpawnErr: "",
|
||||||
|
ExitStatus: 0,
|
||||||
|
Pid: 113,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
supervisorData: supervisorInfo{
|
||||||
|
StateCode: int8(1),
|
||||||
|
StateName: "RUNNING",
|
||||||
|
Ident: "supervisor",
|
||||||
|
},
|
||||||
|
expProcessFields: []map[string]interface{}{
|
||||||
|
{
|
||||||
|
"uptime": int32(731),
|
||||||
|
"state": int16(20),
|
||||||
|
"pid": int32(112),
|
||||||
|
"exitCode": int8(0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uptime": int32(731),
|
||||||
|
"state": int16(20),
|
||||||
|
"pid": int32(113),
|
||||||
|
"exitCode": int8(0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expProcessTags: []map[string]string{
|
||||||
|
{
|
||||||
|
"process": "Process0",
|
||||||
|
"group": "ProcessGroup0",
|
||||||
|
"source": "example.org",
|
||||||
|
"port": "9001",
|
||||||
|
"id": "supervisor",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"process": "Process1",
|
||||||
|
"group": "ProcessGroup1",
|
||||||
|
"source": "example.org",
|
||||||
|
"port": "9001",
|
||||||
|
"id": "supervisor",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expInstanceFields: map[string]interface{}{
|
||||||
|
"state": int8(1),
|
||||||
|
},
|
||||||
|
expInstancesTags: map[string]string{
|
||||||
|
"source": "example.org",
|
||||||
|
"port": "9001",
|
||||||
|
"id": "supervisor",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tC := range testCases {
|
||||||
|
t.Run(tC.desc, func(t *testing.T) {
|
||||||
|
s := &Supervisor{
|
||||||
|
Server: "http://example.org:9001/RPC2",
|
||||||
|
MetricsInc: []string{},
|
||||||
|
MetricsExc: []string{},
|
||||||
|
}
|
||||||
|
status := supervisorInfo{
|
||||||
|
StateCode: tC.supervisorData.StateCode,
|
||||||
|
StateName: tC.supervisorData.StateName,
|
||||||
|
Ident: tC.supervisorData.Ident,
|
||||||
|
}
|
||||||
|
err := s.Init()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to run Init function: %v", err)
|
||||||
|
}
|
||||||
|
for k, v := range tC.sampleProcInfo {
|
||||||
|
processTags, processFields, err := s.parseProcessData(v, status)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tC.expProcessFields[k], processFields)
|
||||||
|
require.Equal(t, tC.expProcessTags[k], processTags)
|
||||||
|
}
|
||||||
|
instanceTags, instanceFields, err := s.parseInstanceData(status)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tC.expInstancesTags, instanceTags)
|
||||||
|
require.Equal(t, tC.expInstanceFields, instanceFields)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIntegration_BasicGathering(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping integration test in short mode")
|
||||||
|
}
|
||||||
|
const supervisorPort = "9001"
|
||||||
|
supervisorConfig, err := filepath.Abs("testdata/supervisord.conf")
|
||||||
|
require.NoError(t, err, "Failed to get absolute path of supervisord config")
|
||||||
|
ctr := testutil.Container{
|
||||||
|
Image: "niasar/supervisor:stretch-3.3",
|
||||||
|
ExposedPorts: []string{supervisorPort},
|
||||||
|
BindMounts: map[string]string{
|
||||||
|
"/etc/supervisor/supervisord.conf": supervisorConfig,
|
||||||
|
},
|
||||||
|
WaitingFor: wait.ForAll(
|
||||||
|
wait.ForLog("supervisord started with pid"),
|
||||||
|
wait.ForListeningPort(nat.Port(supervisorPort)),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
err = ctr.Start()
|
||||||
|
require.NoError(t, err, "failed to start container")
|
||||||
|
defer func() {
|
||||||
|
require.NoError(t, ctr.Terminate(), "terminating container failed")
|
||||||
|
}()
|
||||||
|
s := &Supervisor{
|
||||||
|
Server: "http://login:pass@" + testutil.GetLocalHost() + ":" + ctr.Ports[supervisorPort] + "/RPC2",
|
||||||
|
MetricsInc: []string{},
|
||||||
|
MetricsExc: []string{},
|
||||||
|
}
|
||||||
|
err = s.Init()
|
||||||
|
require.NoError(t, err, "failed to run Init function")
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
err = acc.GatherError(s.Gather)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, acc.HasField("supervisor_processes", "uptime"), true)
|
||||||
|
require.Equal(t, acc.HasField("supervisor_processes", "state"), true)
|
||||||
|
require.Equal(t, acc.HasField("supervisor_processes", "pid"), true)
|
||||||
|
require.Equal(t, acc.HasField("supervisor_processes", "exitCode"), true)
|
||||||
|
require.Equal(t, acc.HasField("supervisor_instance", "state"), true)
|
||||||
|
require.Equal(t, acc.HasTag("supervisor_processes", "id"), true)
|
||||||
|
require.Equal(t, acc.HasTag("supervisor_processes", "source"), true)
|
||||||
|
require.Equal(t, acc.HasTag("supervisor_processes", "port"), true)
|
||||||
|
require.Equal(t, acc.HasTag("supervisor_instance", "id"), true)
|
||||||
|
require.Equal(t, acc.HasTag("supervisor_instance", "source"), true)
|
||||||
|
require.Equal(t, acc.HasTag("supervisor_instance", "port"), true)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
[inet_http_server]
|
||||||
|
port = 0.0.0.0:9001
|
||||||
|
username = login
|
||||||
|
password = pass
|
||||||
|
|
||||||
|
[supervisord]
|
||||||
|
logfile=/dev/stdout
|
||||||
|
pidfile=/var/run/supervisord.pid
|
||||||
|
logfile_maxbytes = 0
|
||||||
|
nodaemon = true
|
||||||
|
|
||||||
|
[rpcinterface:supervisor]
|
||||||
|
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
|
||||||
|
|
||||||
|
[supervisorctl]
|
||||||
|
serverurl=http://localhost:9001
|
||||||
|
|
||||||
|
[program:sleep]
|
||||||
|
process_name = %(program_name)s-%(process_num)s
|
||||||
|
command=/bin/sleep infinity
|
||||||
|
numprocs=3
|
||||||
|
autorestart=true
|
||||||
Loading…
Reference in New Issue