Skip to content

Commit

Permalink
More Data added to OHI Inventory (#2007)
Browse files Browse the repository at this point in the history
* ohi inventory
integrationVersion, integrationName, reportingAgent added
custom attributes from infra agent config are added with prefix labels/
labels from ohi config file are added with prefix labels/

* Linting and type checks

* Linting issue

* Added unit tests for the function building inventory

* Testing at the Plugin output level as well

* license and only using assert

* linting issues

* Tests fail when label is not found
  • Loading branch information
rahulreddy15 authored Feb 7, 2025
1 parent 1765d1f commit 1366f44
Show file tree
Hide file tree
Showing 7 changed files with 354 additions and 32 deletions.
4 changes: 3 additions & 1 deletion pkg/integrations/legacy/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -707,9 +707,11 @@ func EmitDataSet(
if err != nil {
return fmt.Errorf("couldn't determine a unique entity Key: %s", err.Error())
}
// Custom attributes are from infra agent config
customAttr := ctx.Config().CustomAttributes.DataMap()

if len(dataSet.Inventory) > 0 {
inventoryDataSet := BuildInventoryDataSet(elog, dataSet.Inventory, labels, integrationUser, pluginName, entityKey.String())
inventoryDataSet := BuildInventoryDataSet(elog, dataSet.Inventory, labels, customAttr, integrationUser, pluginName, pluginVersion, agentIdentifier, entityKey.String())
emitter.EmitInventory(inventoryDataSet, entity.NewWithoutID(entityKey))
}

Expand Down
101 changes: 85 additions & 16 deletions pkg/integrations/legacy/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ func newFakePlugin(ctx customContext, pluginVersion int) externalPlugin {
Context: ctx,
},
pluginInstance: &PluginV1Instance{
Labels: map[string]string{"role": "fileserver", "environment": "development"},
Labels: map[string]string{"role": "fileserver", "environment": "development", "agent_role": "overwrite agent role"},
plugin: &Plugin{
Name: "new-plugin",
ProtocolVersion: pluginVersion,
Expand Down Expand Up @@ -573,7 +573,7 @@ func newFakePluginWithEnvVars(pluginVersion int) externalPlugin {
Context: ctx,
},
pluginInstance: &PluginV1Instance{
Labels: map[string]string{"role": "fileserver", "environment": "development"},
Labels: map[string]string{"role": "fileserver", "environment": "development", "agent_role": "overwrite agent role"},
plugin: &Plugin{
Name: "new-plugin",
ProtocolVersion: pluginVersion,
Expand Down Expand Up @@ -676,22 +676,36 @@ func (rs *RunnerSuite) TestPluginHandleOutputV1(c *C) {
c.Assert(err, IsNil)

c.Assert(rd, NotNil)
c.Assert(len(rd.Data), Equals, 4)
c.Assert(rd.Data[0].SortKey(), Equals, "first")

invData := rd.Data[3].(protocol.InventoryData)
c.Assert(invData["id"], Equals, "integrationUser")
c.Assert(invData["value"], Equals, "test")

c.Assert(event, NotNil)
c.Assert(event["event_type"], Equals, "LoadBalancerSample")
c.Assert(event["id"], Equals, "first")
c.Assert(event["value"], Equals, "random")
c.Assert(event["integrationUser"], Equals, "test")

for _, labelKey := range labelKeys {
if rd.Data[1].SortKey() != labelKey && rd.Data[2].SortKey() != labelKey {
c.Errorf("There isn't label '%s'' in the inventory", labelKey)
expectedLabelValues := map[string]string{
"first": "fake",
"labels/my_group": "test group",
"labels/role": "fileserver",
"labels/environment": "development",
"labels/agent_role": "overwrite agent role",
"integrationUser": "test",
"integrationName": "test",
"integrationVersion": "1.0.0",
}

c.Assert(len(rd.Data), Equals, 8)

for _, item := range rd.Data {
if invData, ok := item.(protocol.InventoryData); ok {
id, _ := invData["id"].(string)
value, _ := invData["value"].(string)

if expectedValue, exists := expectedLabelValues[id]; exists {
c.Assert(value, Equals, expectedValue)
} else {
c.Fatalf("Expected label: %v not found in Inventory", id)
}
}
}
}
Expand Down Expand Up @@ -726,6 +740,7 @@ func (rs *RunnerSuite) TestPluginHandleOutputEventsV1(c *C) {
// labels from pluginInstance
c.Assert(event["label.environment"], Equals, "development")
c.Assert(event["label.role"], Equals, "fileserver")
c.Assert(event["label.agent_role"], Equals, "overwrite agent role")

// labels from databind
c.Assert(event["label.expected"], Equals, "extra label")
Expand Down Expand Up @@ -831,7 +846,7 @@ func (rs *RunnerSuite) TestEventsPluginRunV1(c *C) {
c.Assert(err, IsNil)

c.Assert(rd, NotNil)
c.Assert(len(rd.Data), Equals, 3)
c.Assert(len(rd.Data), Equals, 7)
c.Assert(rd.Data[0].SortKey(), Equals, "first")

c.Assert(event, NotNil)
Expand Down Expand Up @@ -1030,7 +1045,7 @@ func (rs *RunnerSuite) TestHandleOutputV1(c *C) {
c.Assert(err, IsNil)

// labels are added as inventory
c.Assert(len(rd.Data)-len(labelKeys), Equals, 3)
c.Assert(len(rd.Data)-len(labelKeys), Equals, 7)

firstData := rd.Data[0]
inv := firstData.(protocol.InventoryData)
Expand Down Expand Up @@ -1545,12 +1560,16 @@ func TestParsePayloadV3(t *testing.T) {
type fakeEmitter struct {
lastEventData map[string]interface{}
lastEntityKey string
inventory types.PluginInventoryDataset
}

func (f *fakeEmitter) EmitInventoryWithPluginId(data types.PluginInventoryDataset, entityKey string, pluginId ids.PluginID) {
f.inventory = data
}

func (f *fakeEmitter) EmitInventory(data types.PluginInventoryDataset, entity entity.Entity) {}
func (f *fakeEmitter) EmitInventory(data types.PluginInventoryDataset, entity entity.Entity) {

Check failure on line 1570 in pkg/integrations/legacy/runner_test.go

View workflow job for this annotation

GitHub Actions / linter-linux / Run Linter

unused-parameter: parameter 'entity' seems to be unused, consider removing or renaming it as _ (revive)

Check failure on line 1570 in pkg/integrations/legacy/runner_test.go

View workflow job for this annotation

GitHub Actions / linter-macos / Lint tests

unused-parameter: parameter 'entity' seems to be unused, consider removing or renaming it as _ (revive)

Check failure on line 1570 in pkg/integrations/legacy/runner_test.go

View workflow job for this annotation

GitHub Actions / linter-windows / Lint tests

unused-parameter: parameter 'entity' seems to be unused, consider removing or renaming it as _ (revive)
f.inventory = data
}

func (f *fakeEmitter) EmitEvent(eventData map[string]interface{}, entityKey entity.Key) {
f.lastEventData = eventData
Expand Down Expand Up @@ -1586,6 +1605,29 @@ func TestEmitPayloadV2NoDisplayNameNoEntityName(t *testing.T) {
assert.EqualValues(t, "Motorbike", emitter.lastEventData["entityName"])
assert.EqualValues(t, "motorbike:street_hawk", emitter.lastEventData["entityKey"])

expectedLabelValues := map[string]string{
"integrationUser": "testuser",
"integrationName": "test/test",
"integrationVersion": "x.y.z",
"reportingAgent": "my-agent-id",
"motor": "", // we are type converting to string so motor does not have a value
}

assert.EqualValues(t, len(emitter.inventory), 5)

for _, item := range emitter.inventory {
if invData, ok := item.(protocol.InventoryData); ok {
id, _ := invData["id"].(string)
value, _ := invData["value"].(string)

if expectedValue, exists := expectedLabelValues[id]; exists {
assert.EqualValues(t, value, expectedValue)
} else {
assert.Fail(t, "Expected label not found in Inventory", "Label: %s not found in inventory", id)
}
}
}

// Local entity, no displayName, no entityName
assert.NoError(t, EmitDataSet(ctx, &emitter, "test/test", "x.y.z", "testuser", rd.DataSets[2], extraAnnotations, labels, entityRewrite, version))
_, ok := emitter.lastEventData["displayName"]
Expand All @@ -1594,6 +1636,27 @@ func TestEmitPayloadV2NoDisplayNameNoEntityName(t *testing.T) {
assert.False(t, ok)
// but entityKey is the agent key
assert.EqualValues(t, "my-agent-id", emitter.lastEventData["entityKey"])

// Check inventory data
assert.EqualValues(t, len(emitter.inventory), 5)

for _, item := range emitter.inventory {
if invData, ok := item.(protocol.InventoryData); ok {
id, _ := invData["id"].(string)
value, _ := invData["value"].(string)

if expectedValue, exists := expectedLabelValues[id]; exists {
assert.EqualValues(t, value, expectedValue)
} else {
assert.Fail(t, "Expected label not found in Inventory", "Label: %s not found in inventory", id)
}
}
}
}

func createMockConfigWithDataMap(attrs map[string]interface{}) *config.Config {
customAttrs := config.CustomAttributeMap(attrs)
return &config.Config{CustomAttributes: customAttrs} //nolint:all
}

func TestEmitDataSet_OnAddHostnameDecoratesWithHostname(t *testing.T) {
Expand Down Expand Up @@ -1626,6 +1689,7 @@ func TestEmitDataSet_OnAddHostnameDecoratesWithHostname(t *testing.T) {
ctx.On("EntityKey").Return(agentIdentifier)
ctx.On("HostnameResolver").Return(newFixedHostnameResolver(hn, "short"))
ctx.On("IDLookup").Return(newFixedIDLookup())
ctx.On("Config").Return(createMockConfigWithDataMap(make(map[string]interface{})))

em := &fakeEmitter{}
extraAnnotations := map[string]string{}
Expand Down Expand Up @@ -1677,6 +1741,7 @@ func TestEmitDataSet_EntityNameLocalhostIsNotReplacedWithHostnameV2(t *testing.T
ctx.On("EntityKey").Return(agID)
ctx.On("HostnameResolver").Return(newFixedHostnameResolver("foo.bar", "short"))
ctx.On("IDLookup").Return(newFixedIDLookup())
ctx.On("Config").Return(createMockConfigWithDataMap(make(map[string]interface{})))

em := &fakeEmitter{}
extraAnnotations := map[string]string{}
Expand Down Expand Up @@ -1724,6 +1789,7 @@ func TestEmitDataSet_EntityNameLocalhostIsReplacedWithHostnameV3(t *testing.T) {
ctx.On("EntityKey").Return(agID)
ctx.On("HostnameResolver").Return(newFixedHostnameResolver("foo.bar", "short"))
ctx.On("IDLookup").Return(newFixedIDLookup())
ctx.On("Config").Return(createMockConfigWithDataMap(make(map[string]interface{})))

em := &fakeEmitter{}
extraAnnotations := map[string]string{}
Expand Down Expand Up @@ -1772,6 +1838,7 @@ func TestEmitDataSet_MetricHostnameIsReplacedIfLocalhostV3(t *testing.T) {
ctx.On("EntityKey").Return(agID)
ctx.On("HostnameResolver").Return(newFixedHostnameResolver("foo.bar", "short"))
ctx.On("IDLookup").Return(newFixedIDLookup())
ctx.On("Config").Return(createMockConfigWithDataMap(make(map[string]interface{})))

em := &fakeEmitter{}
extraAnnotations := map[string]string{}
Expand Down Expand Up @@ -1821,6 +1888,7 @@ func TestEmitDataSet_ReportingFieldsAreReplacedIfLocalhostV3(t *testing.T) {
ctx.On("EntityKey").Return(agID)
ctx.On("HostnameResolver").Return(newFixedHostnameResolver("foo.bar", "short"))
ctx.On("IDLookup").Return(newFixedIDLookup())
ctx.On("Config").Return(createMockConfigWithDataMap(make(map[string]interface{})))

em := &fakeEmitter{}
extraAnnotations := map[string]string{}
Expand Down Expand Up @@ -1869,6 +1937,7 @@ func TestEmitDataSet_LogsEntityViolationsOncePerEntity(t *testing.T) {
ctx.On("EntityKey").Return(agID)
ctx.On("HostnameResolver").Return(newFixedHostnameResolver("foo.bar", "short"))
ctx.On("IDLookup").Return(newFixedIDLookup())
ctx.On("Config").Return(createMockConfigWithDataMap(make(map[string]interface{})))

em := &fakeEmitter{}
extraAnnotations := map[string]string{}
Expand Down Expand Up @@ -1906,6 +1975,7 @@ func TestEmitDataSet_DoNotOverrideExistingMetrics(t *testing.T) {
ctx.On("EntityKey").Return("agent-id")
ctx.On("HostnameResolver").Return(newFixedHostnameResolver("long", "short"))
ctx.On("IDLookup").Return(newFixedIDLookup())
ctx.On("Config").Return(createMockConfigWithDataMap(make(map[string]interface{})))
em := &fakeEmitter{}
extraAnnotations := map[string]string{
"cluster_name": "K8sDiscoveredCluster",
Expand Down Expand Up @@ -2207,8 +2277,7 @@ func TestLogFields(t *testing.T) {
"TEMP_DIR": "a/path",
})
assert.Equal(t, fields["labels"], map[string]string{
"role": "fileserver",
"environment": "development",
"agent_role": "overwrite agent role", "environment": "development", "role": "fileserver",
})

if runtime.GOOS == "windows" {
Expand Down
44 changes: 37 additions & 7 deletions pkg/integrations/legacy/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package legacy

import (
"fmt"

"github.com/newrelic/infrastructure-agent/internal/agent/types"

event2 "github.com/newrelic/infrastructure-agent/pkg/event"
Expand All @@ -12,12 +13,22 @@ import (
"github.com/sirupsen/logrus"
)

const (
integrationUserID = "integrationUser"
integrationNameID = "integrationName"
integrationVersionID = "integrationVersion"
reportingAgentID = "reportingAgent"
)

func BuildInventoryDataSet(
entryLog log.Entry,
inventoryData map[string]protocol.InventoryData,
labels map[string]string,
customAttr map[string]string,
integrationUser string,
pluginName string,
pluginVersion string,
reportingAgent string,
entityKey string) types.PluginInventoryDataset {
var inventoryDataSet types.PluginInventoryDataset

Expand All @@ -31,20 +42,39 @@ func BuildInventoryDataSet(
}
}

for key, value := range labels {
addLabeledData := func(id string, value string) {
inventoryDataSet = append(inventoryDataSet, protocol.InventoryData{
"id": fmt.Sprintf("labels/%s", key),
"id": id,
"value": value,
"entityKey": entityKey,
})
}

for key, value := range customAttr {
// Do not set in the case of duplicate key
if _, exists := labels[key]; !exists {
addLabeledData(fmt.Sprintf("labels/%s", key), value)
}
}

for key, value := range labels {
addLabeledData(fmt.Sprintf("labels/%s", key), value)
}

if integrationUser != "" {
inventoryDataSet = append(inventoryDataSet, protocol.InventoryData{
"id": "integrationUser",
"value": integrationUser,
"entityKey": entityKey,
})
addLabeledData(integrationUserID, integrationUser)
}

if pluginName != "" {
addLabeledData(integrationNameID, pluginName)
}

if pluginVersion != "" {
addLabeledData(integrationVersionID, pluginVersion)
}

if reportingAgent != "" {
addLabeledData(reportingAgentID, reportingAgent)
}

return inventoryDataSet
Expand Down
Loading

0 comments on commit 1366f44

Please sign in to comment.