Skip to content

Commit 0a9532f

Browse files
author
Camden Cheek
authored
Merge pull request #18 from newrelic/local-only
Add support for local-only collection
2 parents ac32a82 + 68905ee commit 0a9532f

File tree

5 files changed

+157
-39
lines changed

5 files changed

+157
-39
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

8+
## 1.1.0 - 2019-04-08
9+
### Added
10+
- Local-only collection option
11+
812
## 1.0.1 - 2019-03-19
913
### Fixed
1014
- Stop failing when the leader can't be contacted

src/agent/inventory.go

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,26 @@ func inventoryWorker(agentChan <-chan *Agent, wg *sync.WaitGroup) {
3939
return
4040
}
4141

42-
selfData, err := agent.Client.Agent().Self()
43-
if err != nil {
44-
log.Error("Error retrieving self configuration data for Agent '%s': %s", agent.entity.Metadata.Name, err.Error())
45-
continue
46-
}
42+
CollectInventoryFromOne(agent)
43+
}
44+
}
4745

48-
// Config data
49-
if configData, ok := selfData["Config"]; ok {
50-
agent.processConfig(configData, "Config")
51-
}
46+
//CollectInventoryFromOne collects inventory data for a single agent entity
47+
func CollectInventoryFromOne(agent *Agent) {
48+
selfData, err := agent.Client.Agent().Self()
49+
if err != nil {
50+
log.Error("Error retrieving self configuration data for Agent '%s': %s", agent.entity.Metadata.Name, err.Error())
51+
return
52+
}
5253

53-
// Debug config data
54-
if debugConfig, ok := selfData["DebugConfig"]; ok {
55-
agent.processConfig(debugConfig, "DebugConfig")
56-
}
54+
// Config data
55+
if configData, ok := selfData["Config"]; ok {
56+
agent.processConfig(configData, "Config")
57+
}
58+
59+
// Debug config data
60+
if debugConfig, ok := selfData["DebugConfig"]; ok {
61+
agent.processConfig(debugConfig, "DebugConfig")
5762
}
63+
5864
}

src/agent/metric_collection.go

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,26 +40,33 @@ func metricWorker(agentChan <-chan *Agent, wg *sync.WaitGroup) {
4040
return
4141
}
4242

43-
metricSet := agent.entity.NewMetricSet("ConsulAgentSample",
44-
metric.Attribute{Key: "displayName", Value: agent.entity.Metadata.Name},
45-
metric.Attribute{Key: "entityName", Value: agent.entity.Metadata.Namespace + ":" + agent.entity.Metadata.Name},
46-
metric.Attribute{Key: "ip", Value: agent.ipAddr},
47-
metric.Attribute{Key: "datacenter", Value: agent.datacenter},
48-
)
49-
50-
// Collect core metrics
51-
if err := agent.CollectCoreMetrics(metricSet, gaugeMetrics, counterMetrics, timerMetrics); err != nil {
52-
log.Error("Error collecting core metrics for Agent '%s': %s", agent.entity.Metadata.Name, err.Error())
53-
}
43+
CollectMetricsFromOne(agent)
5444

55-
// Peer Count
56-
if err := agent.collectPeerCount(metricSet); err != nil {
57-
log.Error("Error collecting peer count for Agent '%s': %s", agent.entity.Metadata.Name, err.Error())
58-
}
45+
}
46+
}
5947

60-
// Latency metrics
61-
if err := agent.collectLatencyMetrics(metricSet); err != nil {
62-
log.Error("Error collecting latency metrics for Agent '%s': %s", agent.entity.Metadata.Name, err.Error())
63-
}
48+
// CollectMetricsFromOne does a metric collect for a single agent
49+
func CollectMetricsFromOne(agent *Agent) {
50+
metricSet := agent.entity.NewMetricSet("ConsulAgentSample",
51+
metric.Attribute{Key: "displayName", Value: agent.entity.Metadata.Name},
52+
metric.Attribute{Key: "entityName", Value: agent.entity.Metadata.Namespace + ":" + agent.entity.Metadata.Name},
53+
metric.Attribute{Key: "ip", Value: agent.ipAddr},
54+
metric.Attribute{Key: "datacenter", Value: agent.datacenter},
55+
)
56+
57+
// Collect core metrics
58+
if err := agent.CollectCoreMetrics(metricSet, gaugeMetrics, counterMetrics, timerMetrics); err != nil {
59+
log.Error("Error collecting core metrics for Agent '%s': %s", agent.entity.Metadata.Name, err.Error())
6460
}
61+
62+
// Peer Count
63+
if err := agent.collectPeerCount(metricSet); err != nil {
64+
log.Error("Error collecting peer count for Agent '%s': %s", agent.entity.Metadata.Name, err.Error())
65+
}
66+
67+
// Latency metrics
68+
if err := agent.collectLatencyMetrics(metricSet); err != nil {
69+
log.Error("Error collecting latency metrics for Agent '%s': %s", agent.entity.Metadata.Name, err.Error())
70+
}
71+
6572
}

src/args/argument_list.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type ArgumentList struct {
1919
TrustServerCertificate bool `default:"false" help:"If true server certificate is not verified for SSL. If false certificate will be verified against supplied certificate"`
2020
CABundleFile string `default:"" help:"Alternative Certificate Authority bundle file"`
2121
CABundleDir string `default:"" help:"Alternative Certificate Authority bundle directory"`
22+
FanOut bool `default:"true" help:"If true will attempt to gather metrics from all other nodes in consul cluster"`
2223
}
2324

2425
// Validate validates Consul arguments

src/consul.go

Lines changed: 107 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package main
22

33
import (
4+
"fmt"
45
"os"
6+
"strconv"
57

68
"github.com/hashicorp/consul/api"
79
"github.com/newrelic/infra-integrations-sdk/integration"
@@ -13,7 +15,7 @@ import (
1315

1416
const (
1517
integrationName = "com.newrelic.consul"
16-
integrationVersion = "1.0.1"
18+
integrationVersion = "1.1.0"
1719
)
1820

1921
func main() {
@@ -35,16 +37,36 @@ func main() {
3537
os.Exit(1)
3638
}
3739

40+
var collectionError error
41+
if args.FanOut {
42+
collectionError = fanOutCollection(client, i, &args)
43+
} else {
44+
collectionError = localCollection(client, i, &args)
45+
}
46+
47+
if collectionError != nil {
48+
log.Error("Error collecting metrics: %s", collectionError.Error())
49+
os.Exit(1)
50+
}
51+
52+
if err = i.Publish(); err != nil {
53+
log.Error("Failed to publish metrics: %s", err.Error())
54+
os.Exit(1)
55+
}
56+
}
57+
58+
func fanOutCollection(client *api.Client, i *integration.Integration, args *args.ArgumentList) error {
3859
// Create the list of agents in LAN pool
39-
agents, leader, err := agent.CreateAgents(client, i, &args)
60+
agents, leader, err := agent.CreateAgents(client, i, args)
4061
if err != nil {
41-
log.Error("Error creating Agent entities: %s", err.Error())
42-
os.Exit(1)
62+
return fmt.Errorf("Error creating Agent entities: %s", err.Error())
4363
}
4464

4565
dc, err := datacenter.NewDatacenter(leader, i)
4666
if err != nil {
4767
log.Error("Error creating Datacenter entity: %s", err.Error())
68+
} else if args.HasMetrics() {
69+
dc.CollectMetrics()
4870
}
4971

5072
// Collect inventory for agents
@@ -55,10 +77,88 @@ func main() {
5577
// Collect metrics for Agents and cluster
5678
if args.HasMetrics() {
5779
agent.CollectMetrics(agents)
58-
dc.CollectMetrics()
5980
}
6081

61-
if err = i.Publish(); err != nil {
62-
log.Error(err.Error())
82+
return nil
83+
}
84+
85+
func maybeConvertBool(value interface{}) (bool, error) {
86+
switch v := value.(type) {
87+
default:
88+
return false, fmt.Errorf("Unexpected type: %T", v)
89+
case bool:
90+
return value.(bool), nil
91+
case string:
92+
val, err := strconv.ParseBool(value.(string))
93+
if err != nil {
94+
return false, fmt.Errorf("Unable to convert %v to a bool: %v", value, err)
95+
}
96+
return val, nil
97+
}
98+
}
99+
100+
func localCollection(client *api.Client, i *integration.Integration, args *args.ArgumentList) error {
101+
localAgentData, err := client.Agent().Self()
102+
if err != nil {
103+
return fmt.Errorf("Failed to collect local agent data: %v", err)
63104
}
105+
106+
// TODO: It would be nice if this was available to us as a MemberAgent
107+
// object but I don't think it is
108+
member, ok := localAgentData["Member"]
109+
if !ok {
110+
return fmt.Errorf("Failed to get local agent member: %v", ok)
111+
}
112+
113+
memberName, ok := member["Name"].(string)
114+
if !ok {
115+
return fmt.Errorf("Failed to get member name: %v", ok)
116+
}
117+
118+
memberAddr, ok := member["Addr"].(string)
119+
if !ok {
120+
return fmt.Errorf("Failed to get member address: %v", ok)
121+
}
122+
123+
memberDataCenter, ok := member["Tags"].(map[string]interface{})["dc"].(string)
124+
if !ok {
125+
return fmt.Errorf("Failed to get member datacenter: %v", ok)
126+
}
127+
128+
isLeaderValue, ok := localAgentData["Stats"]["consul"].(map[string]interface{})["leader"]
129+
if !ok {
130+
return fmt.Errorf("Failed to check for leadership: %v", ok)
131+
}
132+
133+
// The key currently comes back to us as a string, so we convert it if we need to
134+
// Little bit of future proofing in case the api changes
135+
isLeader, err := maybeConvertBool(isLeaderValue)
136+
if err != nil {
137+
log.Error("Leadership value is not a bool. Defaulting to false: %v", err)
138+
isLeader = false
139+
}
140+
141+
entity, err := i.Entity(memberName, "agent")
142+
agentInstance := agent.NewAgent(client, entity, memberAddr, memberDataCenter)
143+
144+
if args.HasMetrics() {
145+
if isLeader {
146+
log.Debug("Checking Leader Metrics")
147+
dc, err := datacenter.NewDatacenter(agentInstance, i)
148+
if err != nil {
149+
log.Error("Failed to get datacenter metrics: %v", err)
150+
} else {
151+
dc.CollectMetrics()
152+
}
153+
} else {
154+
log.Debug("Not Checking Leader Metrics")
155+
}
156+
agent.CollectMetricsFromOne(agentInstance)
157+
}
158+
159+
if args.HasInventory() {
160+
agent.CollectInventoryFromOne(agentInstance)
161+
}
162+
163+
return nil
64164
}

0 commit comments

Comments
 (0)