master
go 113 lines 2.69 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package rabbitmq
4
5 import (
6 "encoding/json"
7 "fmt"
8 "net/http"
9 "slices"
10 "strings"
11
12 "github.com/netdata/netdata/go/plugins/pkg/web"
13 )
14
15 func (c *Collector) collect() (map[string]int64, error) {
16 if c.queryClusterMeta {
17 id, name, err := c.getClusterMeta()
18 if err != nil {
19 return nil, err
20 }
21 c.queryClusterMeta = false
22 c.clusterId = id
23 c.clusterName = name
24 }
25
26 c.cache.resetSeen()
27
28 mx := make(map[string]int64)
29
30 if err := c.collectOverview(mx); err != nil {
31 return nil, err
32 }
33 if err := c.collectNodes(mx); err != nil {
34 return mx, err
35 }
36 if err := c.collectVhosts(mx); err != nil {
37 return mx, err
38 }
39 if c.CollectQueues {
40 if err := c.collectQueues(mx); err != nil {
41 return mx, err
42 }
43 }
44
45 c.updateCharts()
46
47 return mx, nil
48 }
49
50 func (c *Collector) getClusterMeta() (id string, name string, err error) {
51 req, err := web.NewHTTPRequestWithPath(c.RequestConfig, urlPathAPIWhoami)
52 if err != nil {
53 return "", "", fmt.Errorf("failed to create whoami request: %w", err)
54 }
55
56 var user apiWhoamiResp
57 if err := c.webClient().RequestJSON(req, &user); err != nil {
58 return "", "", fmt.Errorf("failed to send whoami request: %w", err)
59 }
60
61 if user.Name == "" {
62 return "", "", fmt.Errorf("unexpected response: whoami: user name n is empty")
63 }
64
65 // In RabbitMQ < 3.8.3 the `tags` field may be returned as a single string
66 // (e.g. "administrator,management") instead of an array. We intentionally
67 // treat it as one tag and do not split on commas here.
68 if !slices.ContainsFunc(user.Tags, func(s string) bool {
69 return strings.Contains(s, "administrator")
70 }) {
71 c.Warningf("user %s lacks 'administrator' tag: cluster ID and name cannot be collected.", user.Name)
72 return "", "", nil
73 }
74
75 req, err = web.NewHTTPRequestWithPath(c.RequestConfig, urlPathAPIDefinitions)
76 if err != nil {
77 return "", "", fmt.Errorf("failed to create definitions request: %w", err)
78 }
79
80 var resp apiDefinitionsResp
81
82 if err := c.webClient().RequestJSON(req, &resp); err != nil {
83 return "", "", err
84 }
85
86 id = "unknown"
87 name = "unset"
88
89 for _, v := range resp.GlobalParams {
90 switch v.Name {
91 case "cluster_name":
92 name, _ = v.Value.(string)
93 case "internal_cluster_id":
94 id, _ = v.Value.(string)
95 id = strings.TrimPrefix(id, "rabbitmq-cluster-id-")
96 }
97 }
98
99 return id, name, nil
100 }
101
102 func (c *Collector) webClient() *web.Client {
103 return web.DoHTTP(c.httpClient).OnNokCode(func(resp *http.Response) (bool, error) {
104 var msg struct {
105 Error string `json:"error"`
106 Reason string `json:"reason"`
107 }
108 if err := json.NewDecoder(resp.Body).Decode(&msg); err == nil && msg.Error != "" {
109 return false, fmt.Errorf("err '%s', reason '%s'", msg.Error, msg.Reason)
110 }
111 return false, nil
112 })
113 }