Skip to content

Commit ad165dd

Browse files
authored
Merge pull request #288 from newrelic/host_id
feat: add host_id argument
2 parents 5411874 + f3034f1 commit ad165dd

File tree

3 files changed

+121
-0
lines changed

3 files changed

+121
-0
lines changed

args/args.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type DefaultArgumentList struct {
2121
Metadata bool `default:"false" help:"Add customer defined key-value attributes to the samples."`
2222
NriCluster string `default:"" help:"Optional. Cluster name"`
2323
NriService string `default:"" help:"Optional. Service name"`
24+
HostID string `default:"" help:"Optional. Host ID to be set in entity or/and in the payload"`
2425
}
2526

2627
// All returns if all data should be published

integration/integration.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"io"
88
"os"
99
"reflect"
10+
"regexp"
1011
"strings"
1112
"sync"
1213
"time"
@@ -27,6 +28,10 @@ const (
2728
protocolVersion = "4"
2829
)
2930

31+
// A different regex is needed for replacing because `localhostRE` matches
32+
// IPV6 by using extra `:` that don't belong to the IP but are separators.
33+
var localhostReplaceRE = regexp.MustCompile(`(localhost|LOCALHOST|127(?:\.[0-9]+){0,2}\.[0-9]+|::1)`)
34+
3035
// Metadata describes the integration
3136
type Metadata struct {
3237
Name string `json:"name"`
@@ -125,6 +130,15 @@ func (i *Integration) AddEntity(e *Entity) {
125130
func (i *Integration) Publish() error {
126131
defer i.Clear()
127132

133+
hostID := i.GetHostID()
134+
if hostID != "" {
135+
for k := range i.Entities {
136+
if i.Entities[k].Metadata != nil {
137+
i.Entities[k].Metadata.Name = replaceLocalhost(i.Entities[k].Metadata.Name, hostID)
138+
}
139+
}
140+
}
141+
128142
// add the host entity to the list of entities to be serialized, if not empty
129143
if notEmpty(i.HostEntity) {
130144
i.Entities = append(i.Entities, i.HostEntity)
@@ -159,6 +173,12 @@ func (i *Integration) MarshalJSON() (output []byte, err error) {
159173
return
160174
}
161175

176+
// GetHostID returns HostID or an empty string if not set
177+
func (i *Integration) GetHostID() string {
178+
defaultArgs := args.GetDefaultArgs(i.args)
179+
return defaultArgs.HostID
180+
}
181+
162182
// toJSON serializes integration as JSON. If the pretty attribute is
163183
// set to true, the JSON will be indented for easy reading.
164184
func (i *Integration) toJSON(pretty bool) (output []byte, err error) {
@@ -272,3 +292,9 @@ func (i *Integration) addDefaultAttributes(e *Entity) error {
272292

273293
return nil
274294
}
295+
296+
// ReplaceLocalhost replaces the occurrence of a localhost address with
297+
// the given hostname. This is done to avoid entity identifier collision.
298+
func replaceLocalhost(source, with string) string {
299+
return localhostReplaceRE.ReplaceAllString(source, with)
300+
}

integration/integration_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ import (
1010

1111
"github.com/stretchr/testify/assert"
1212

13+
"github.com/newrelic/infra-integrations-sdk/v4/args"
1314
"github.com/newrelic/infra-integrations-sdk/v4/data/event"
1415
"github.com/newrelic/infra-integrations-sdk/v4/log"
1516
)
1617

1718
var (
1819
integrationName = "TestIntegration"
1920
integrationVersion = "1.0"
21+
arguments args.DefaultArgumentList
2022
)
2123

2224
func Test_Integration_CreateIntegrationInitializesCorrectly(t *testing.T) {
@@ -463,6 +465,98 @@ func Test_Integration_PublishThrowsNoError(t *testing.T) {
463465
assert.Empty(t, i.Entities)
464466
}
465467

468+
func Test_Integration_HostId(t *testing.T) {
469+
w := testWriter{
470+
func(integrationBytes []byte) {
471+
expectedOutputRaw := []byte(`
472+
{
473+
"protocol_version": "4",
474+
"integration": {
475+
"name": "TestIntegration",
476+
"version": "1.0"
477+
},
478+
"data": [
479+
{
480+
"common": {},
481+
"entity": {
482+
"name": "id-1234567890abc:8080",
483+
"displayName": "",
484+
"type": "test",
485+
"metadata": {
486+
"tags.env": "prod"
487+
}
488+
},
489+
"metrics": [],
490+
"inventory": {},
491+
"events": []
492+
},
493+
{
494+
"common": {
495+
"attributes": {
496+
"targetName": "id-1234567890abc"
497+
}
498+
},
499+
"entity": {
500+
"name": "EntityOne",
501+
"displayName": "",
502+
"type": "test",
503+
"metadata": {}
504+
},
505+
"metrics": [
506+
{
507+
"timestamp": 10000000,
508+
"name": "metricOne",
509+
"type": "gauge",
510+
"attributes": {
511+
"processName": "java"
512+
},
513+
"value": 2
514+
}
515+
],
516+
"inventory": {},
517+
"events": []
518+
}
519+
]
520+
}`)
521+
522+
// awful but cannot compare with json.Unmarshal as it's not supported by Integration
523+
assert.Equal(t, stripBlanks(expectedOutputRaw), stripBlanks(integrationBytes))
524+
},
525+
}
526+
527+
_ = os.Setenv("HOST_ID", "id-1234567890abc")
528+
529+
// os.ClearEnv breaks tests in Windows that use the fileStorer because clears the user env vars
530+
defer func() {
531+
_ = os.Unsetenv("HOST_ID")
532+
}()
533+
534+
i, err := New("TestIntegration", "1.0", Logger(log.Discard), Writer(w), Args(&arguments))
535+
assert.NoError(t, err)
536+
537+
e, err := i.NewEntity("localhost:8080", "test", "")
538+
assert.NoError(t, err)
539+
_ = e.AddTag("env", "prod")
540+
i.AddEntity(e)
541+
542+
// add entity
543+
e2, err := i.NewEntity("EntityOne", "test", "")
544+
assert.NoError(t, err)
545+
// add common attributes
546+
e2.AddCommonDimension("targetName", i.GetHostID())
547+
// add metric to entity 2
548+
gauge, _ := Gauge(time.Unix(10000000, 0), "metricOne", 2)
549+
_ = gauge.AddDimension("processName", "java")
550+
e2.AddMetric(gauge)
551+
// add entity 2 to integration
552+
i.AddEntity(e2)
553+
554+
assert.NoError(t, i.Publish())
555+
556+
// check integration was reset
557+
assert.Empty(t, i.Entities)
558+
}
559+
466560
func Test_Integration_FindEntity(t *testing.T) {
467561
i := newTestIntegration(t)
468562

0 commit comments

Comments
 (0)