github.com/netdata/go.d.plugin@v0.58.1/modules/supervisord/client.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package supervisord
     4  
     5  import (
     6  	"context"
     7  	"errors"
     8  	"fmt"
     9  	"net"
    10  	"net/http"
    11  	"net/url"
    12  	"strings"
    13  
    14  	"github.com/mattn/go-xmlrpc"
    15  )
    16  
    17  type supervisorRPCClient struct {
    18  	client *xmlrpc.Client
    19  }
    20  
    21  func newSupervisorRPCClient(serverURL *url.URL, httpClient *http.Client) (supervisorClient, error) {
    22  	switch serverURL.Scheme {
    23  	case "http", "https":
    24  		c := xmlrpc.NewClient(serverURL.String())
    25  		c.HttpClient = httpClient
    26  		return &supervisorRPCClient{client: c}, nil
    27  	case "unix":
    28  		c := xmlrpc.NewClient("http://unix/RPC2")
    29  		t, ok := httpClient.Transport.(*http.Transport)
    30  		if !ok {
    31  			return nil, errors.New("unexpected HTTP client transport")
    32  		}
    33  		t.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) {
    34  			d := net.Dialer{Timeout: httpClient.Timeout}
    35  			return d.DialContext(ctx, "unix", serverURL.Path)
    36  		}
    37  		c.HttpClient = httpClient
    38  		return &supervisorRPCClient{client: c}, nil
    39  	default:
    40  		return nil, fmt.Errorf("unexpected URL scheme: %s", serverURL)
    41  	}
    42  }
    43  
    44  // http://supervisord.org/api.html#process-control
    45  type processStatus struct {
    46  	name       string // name of the process.
    47  	group      string // name of the process’ group.
    48  	start      int    // UNIX timestamp of when the process was started.
    49  	stop       int    // UNIX timestamp of when the process last ended, or 0 if the process has never been stopped.
    50  	now        int    // UNIX timestamp of the current time, which can be used to calculate process up-time.
    51  	state      int    // state code.
    52  	stateName  string // string description of state.
    53  	exitStatus int    // exit status (errorlevel) of process, or 0 if the process is still running.
    54  }
    55  
    56  func (c *supervisorRPCClient) getAllProcessInfo() ([]processStatus, error) {
    57  	const fn = "supervisor.getAllProcessInfo"
    58  	resp, err := c.client.Call(fn)
    59  	if err != nil {
    60  		return nil, fmt.Errorf("error on '%s' function call: %v", fn, err)
    61  	}
    62  	return parseGetAllProcessInfo(resp)
    63  }
    64  
    65  func (c *supervisorRPCClient) closeIdleConnections() {
    66  	c.client.HttpClient.CloseIdleConnections()
    67  }
    68  
    69  func parseGetAllProcessInfo(resp interface{}) ([]processStatus, error) {
    70  	arr, ok := resp.(xmlrpc.Array)
    71  	if !ok {
    72  		return nil, fmt.Errorf("unexpected response type, want=xmlrpc.Array, got=%T", resp)
    73  	}
    74  
    75  	var info []processStatus
    76  
    77  	for _, item := range arr {
    78  		s, ok := item.(xmlrpc.Struct)
    79  		if !ok {
    80  			continue
    81  		}
    82  
    83  		var p processStatus
    84  		for k, v := range s {
    85  			switch strings.ToLower(k) {
    86  			case "name":
    87  				p.name, _ = v.(string)
    88  			case "group":
    89  				p.group, _ = v.(string)
    90  			case "start":
    91  				p.start, _ = v.(int)
    92  			case "stop":
    93  				p.stop, _ = v.(int)
    94  			case "now":
    95  				p.now, _ = v.(int)
    96  			case "state":
    97  				p.state, _ = v.(int)
    98  			case "statename":
    99  				p.stateName, _ = v.(string)
   100  			case "exitstatus":
   101  				p.exitStatus, _ = v.(int)
   102  			}
   103  		}
   104  		if p.name != "" && p.group != "" && p.stateName != "" {
   105  			info = append(info, p)
   106  		}
   107  	}
   108  	return info, nil
   109  }