Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FA][HA Agent][NDMII-3267] Add config_id metadata #33215

Merged
merged 12 commits into from
Jan 30, 2025
20 changes: 16 additions & 4 deletions comp/haagent/impl/haagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,28 @@ import (

log "github.com/DataDog/datadog-agent/comp/core/log/def"
haagent "github.com/DataDog/datadog-agent/comp/haagent/def"
"github.com/DataDog/datadog-agent/comp/metadata/inventoryagent"
"github.com/DataDog/datadog-agent/pkg/remoteconfig/state"
"github.com/DataDog/datadog-agent/pkg/util/hostname"
"go.uber.org/atomic"
)

type haAgentImpl struct {
log log.Component
inventoryAgent inventoryagent.Component
haAgentConfigs *haAgentConfigs
state *atomic.String
}

func newHaAgentImpl(log log.Component, haAgentConfigs *haAgentConfigs) *haAgentImpl {
return &haAgentImpl{
func newHaAgentImpl(log log.Component, inventoryAgent inventoryagent.Component, haAgentConfigs *haAgentConfigs) *haAgentImpl {
haagentImpl := &haAgentImpl{
log: log,
inventoryAgent: inventoryAgent,
haAgentConfigs: haAgentConfigs,
state: atomic.NewString(string(haagent.Unknown)),
}
haagentImpl.setState(haagent.Unknown)
AlexandreYang marked this conversation as resolved.
Show resolved Hide resolved
return haagentImpl
}

func (h *haAgentImpl) Enabled() bool {
Expand Down Expand Up @@ -60,14 +65,21 @@ func (h *haAgentImpl) SetLeader(leaderAgentHostname string) {

if newState != prevState {
h.log.Infof("agent state switched from %s to %s", prevState, newState)
h.state.Store(string(newState))
h.setState(newState)
} else {
h.log.Debugf("agent state not changed (current state: %s)", prevState)
}
}

func (h *haAgentImpl) setState(newState haagent.State) {
h.state.Store(string(newState))

// Set ha_agent_state Agent Metadata
h.inventoryAgent.Set("ha_agent_state", string(newState))
}

func (h *haAgentImpl) resetAgentState() {
h.state.Store(string(haagent.Unknown))
h.setState(haagent.Unknown)
}

// ShouldRunIntegration return true if the agent integrations should to run.
Expand Down
12 changes: 9 additions & 3 deletions comp/haagent/impl/haagent_comp.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ import (
"github.com/DataDog/datadog-agent/comp/core/config"
log "github.com/DataDog/datadog-agent/comp/core/log/def"
haagent "github.com/DataDog/datadog-agent/comp/haagent/def"
"github.com/DataDog/datadog-agent/comp/metadata/inventoryagent"
rctypes "github.com/DataDog/datadog-agent/comp/remote-config/rcclient/types"
"github.com/DataDog/datadog-agent/pkg/remoteconfig/state"
)

// Requires defines the dependencies for the haagent component
type Requires struct {
Logger log.Component
AgentConfig config.Component
Logger log.Component
AgentConfig config.Component
InventoryAgent inventoryagent.Component
}

// Provides defines the output of the haagent component
Expand All @@ -29,7 +31,11 @@ type Provides struct {
// NewComponent creates a new haagent component
func NewComponent(reqs Requires) (Provides, error) {
haAgentConf := newHaAgentConfigs(reqs.AgentConfig)
haAgent := newHaAgentImpl(reqs.Logger, haAgentConf)
haAgent := newHaAgentImpl(reqs.Logger, reqs.InventoryAgent, haAgentConf)

// Set ha_agent_enabled Agent Metadata
reqs.InventoryAgent.Set("ha_agent_enabled", haAgent.Enabled())

var rcListener rctypes.ListenerProvider
if haAgent.Enabled() {
reqs.Logger.Debug("Add HA Agent RCListener")
Expand Down
36 changes: 19 additions & 17 deletions comp/haagent/impl/haagent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,10 @@ package haagentimpl
import (
"testing"

"github.com/DataDog/datadog-agent/comp/core/config"
logmock "github.com/DataDog/datadog-agent/comp/core/log/mock"
haagent "github.com/DataDog/datadog-agent/comp/haagent/def"
"github.com/DataDog/datadog-agent/pkg/remoteconfig/state"
"github.com/DataDog/datadog-agent/pkg/util/fxutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/fx"
)

var testConfigID = "datadog/2/HA_AGENT/group-62345762794c0c0b/65f17d667fb50f8ae28a3c858bdb1be9ea994f20249c119e007c520ac115c807"
Expand All @@ -30,22 +26,28 @@ func Test_Enabled(t *testing.T) {
{
name: "enabled",
configs: map[string]interface{}{
"ha_agent.enabled": true,
"enable_metadata_collection": true,
"inventories_enabled": true,
"ha_agent.enabled": true,
},
expectedEnabled: true,
},
{
name: "disabled",
configs: map[string]interface{}{
"ha_agent.enabled": false,
"enable_metadata_collection": true,
"inventories_enabled": true,
"ha_agent.enabled": false,
},
expectedEnabled: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
haAgent := newTestHaAgentComponent(t, tt.configs).Comp
provides, deps := newTestHaAgentComponent(t, tt.configs)
haAgent := provides.Comp
assert.Equal(t, tt.expectedEnabled, haAgent.Enabled())
assert.Equal(t, tt.expectedEnabled, deps.InventoryAgent.Get()["ha_agent_enabled"])
})
}
}
Expand All @@ -54,15 +56,17 @@ func Test_GetGroup(t *testing.T) {
agentConfigs := map[string]interface{}{
"ha_agent.group": "my-group-01",
}
haAgent := newTestHaAgentComponent(t, agentConfigs).Comp
provides, _ := newTestHaAgentComponent(t, agentConfigs)
haAgent := provides.Comp
assert.Equal(t, "my-group-01", haAgent.GetGroup())
}

func Test_GetState(t *testing.T) {
agentConfigs := map[string]interface{}{
"hostname": "my-agent-hostname",
}
haAgent := newTestHaAgentComponent(t, agentConfigs).Comp
provides, _ := newTestHaAgentComponent(t, agentConfigs)
haAgent := provides.Comp

assert.Equal(t, haagent.Unknown, haAgent.GetState())

Expand Down Expand Up @@ -96,7 +100,7 @@ func Test_RCListener(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
provides := newTestHaAgentComponent(t, tt.configs)
provides, _ := newTestHaAgentComponent(t, tt.configs)
if tt.expectRCListener {
assert.NotNil(t, provides.RCListener.ListenerProvider)
} else {
Expand Down Expand Up @@ -181,12 +185,9 @@ func Test_haAgentImpl_onHaAgentUpdate(t *testing.T) {
"ha_agent.enabled": true,
"ha_agent.group": testGroup,
}
agentConfigComponent := fxutil.Test[config.Component](t, fx.Options(
config.MockModule(),
fx.Replace(config.MockParams{Overrides: agentConfigs}),
))

h := newHaAgentImpl(logmock.New(t), newHaAgentConfigs(agentConfigComponent))
provides, deps := newTestHaAgentComponent(t, agentConfigs)
h := provides.Comp.(*haAgentImpl)

if tt.initialState != "" {
h.state.Store(string(tt.initialState))
Expand All @@ -202,6 +203,7 @@ func Test_haAgentImpl_onHaAgentUpdate(t *testing.T) {
assert.Equal(t, tt.expectedApplyID, applyID)
assert.Equal(t, tt.expectedApplyStatus, applyStatus)
assert.Equal(t, tt.expectedAgentState, h.GetState())
assert.Equal(t, deps.InventoryAgent.Get()["ha_agent_state"], string(h.GetState()))
})
}
}
Expand Down Expand Up @@ -273,7 +275,7 @@ func Test_haAgentImpl_ShouldRunIntegration(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
haAgent := newTestHaAgentComponent(t, tt.agentConfigs)
haAgent, _ := newTestHaAgentComponent(t, tt.agentConfigs)
haAgent.Comp.SetLeader(tt.leader)

for integrationName, shouldRun := range tt.expectShouldRunIntegration {
Expand All @@ -285,7 +287,7 @@ func Test_haAgentImpl_ShouldRunIntegration(t *testing.T) {

func Test_haAgentImpl_resetAgentState(t *testing.T) {
// GIVEN
haAgent := newTestHaAgentComponent(t, nil)
haAgent, _ := newTestHaAgentComponent(t, nil)
haAgentComp := haAgent.Comp.(*haAgentImpl)
haAgentComp.state.Store(string(haagent.Active))
require.Equal(t, haagent.Active, haAgentComp.GetState())
Expand Down
34 changes: 26 additions & 8 deletions comp/haagent/impl/haagent_testutils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,43 @@ import (
"testing"

"github.com/DataDog/datadog-agent/comp/core/config"
configComponent "github.com/DataDog/datadog-agent/comp/core/config"
AlexandreYang marked this conversation as resolved.
Show resolved Hide resolved
"github.com/DataDog/datadog-agent/comp/core/hostname/hostnameimpl"
log "github.com/DataDog/datadog-agent/comp/core/log/def"
logmock "github.com/DataDog/datadog-agent/comp/core/log/mock"
"github.com/DataDog/datadog-agent/comp/metadata/inventoryagent"
"github.com/DataDog/datadog-agent/comp/metadata/inventoryagent/inventoryagentimpl"
"github.com/DataDog/datadog-agent/pkg/util/fxutil"
"github.com/stretchr/testify/require"
"go.uber.org/fx"
)

func newTestHaAgentComponent(t *testing.T, agentConfigs map[string]interface{}) Provides {
logComponent := logmock.New(t)
agentConfigComponent := fxutil.Test[config.Component](t, fx.Options(
config.MockModule(),
fx.Replace(config.MockParams{Overrides: agentConfigs}),
type testDeps struct {
fx.In

Config config.Component
Log log.Component
InventoryAgent inventoryagent.Component
}

func newTestHaAgentComponent(t *testing.T, agentConfigs map[string]interface{}) (Provides, testDeps) {
deps := fxutil.Test[testDeps](t, fx.Options(
fx.Supply(configComponent.Params{}),
fx.Supply(log.Params{}),
fx.Provide(func() log.Component { return logmock.New(t) }),
configComponent.MockModule(),
hostnameimpl.MockModule(),
fx.Replace(configComponent.MockParams{Overrides: agentConfigs}),
inventoryagentimpl.MockModule(),
))

requires := Requires{
Logger: logComponent,
AgentConfig: agentConfigComponent,
Logger: deps.Log,
AgentConfig: deps.Config,
InventoryAgent: deps.InventoryAgent,
}

provides, err := NewComponent(requires)
require.NoError(t, err)
return provides
return provides, deps
}
2 changes: 1 addition & 1 deletion comp/haagent/mock/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func (m *mockHaAgent) Enabled() bool {
func (m *mockHaAgent) SetLeader(_ string) {
}

func (m *mockHaAgent) GetState() haagent.State { return haagent.Standby }
func (m *mockHaAgent) GetState() haagent.State { return m.state }

func (m *mockHaAgent) SetGroup(group string) {
m.group = group
Expand Down
2 changes: 2 additions & 0 deletions comp/metadata/bundle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
authtokenimpl "github.com/DataDog/datadog-agent/comp/api/authtoken/fetchonlyimpl"
"github.com/DataDog/datadog-agent/comp/collector/collector/collectorimpl"
"github.com/DataDog/datadog-agent/comp/core"
haagentmock "github.com/DataDog/datadog-agent/comp/haagent/mock"
"github.com/DataDog/datadog-agent/comp/logs/agent"
"github.com/DataDog/datadog-agent/comp/metadata/runner/runnerimpl"
"github.com/DataDog/datadog-agent/pkg/serializer"
Expand All @@ -29,6 +30,7 @@ func TestBundleDependencies(t *testing.T) {
return option.None[agent.Component]()
}),
authtokenimpl.Module(),
haagentmock.Module(),
)
}

Expand Down
8 changes: 7 additions & 1 deletion comp/metadata/inventoryagent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ The payload is a JSON dict with the following fields
- `ecs_fargate_task_arn` - **string**: if the Agent runs in ECS Fargate, contains the Agent's Task ARN. Else, is empty.
- `ecs_fargate_cluster_name` - **string**: if the Agent runs in ECS Fargate, contains the Agent's cluster name. Else, is empty.
- `fleet_policies_applied` -- **array of string**: The Fleet Policies that have been applied to the agent, if any. Is empty if no policy is applied.
- `config_id` -- **string**: the Fleet Config ID, the configuration value `config_id`.
- `ha_agent_enabled` -- **bool**: true if HA Agent is enabled (see: `ha_agent.enabled` config option).
- `ha_agent_state` -- **string**: if HA Agent is enabled, contains the HA Agent state.
AlexandreYang marked this conversation as resolved.
Show resolved Hide resolved

("scrubbed" indicates that secrets are removed from the field value just as they are in logs)

Expand Down Expand Up @@ -167,7 +170,10 @@ Here an example of an inventory payload:
"environment_variable_configuration": "api_key: \"***************************aaaaa\"",
"remote_configuration": "log_level: \"debug\"",
"cli_configuration": "log_level: \"warn\"",
"source_local_configuration": ""
"source_local_configuration": "",
"config_id": "my-config",
"ha_agent_enabled": true",
"ha_agent_state": "active"
}
"hostname": "my-host",
"timestamp": 1631281754507358895
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,10 @@ func (ia *inventoryagent) fetchECSFargateAgentMetadata() {
ia.data["ecs_fargate_cluster_name"] = taskMeta.ClusterName
}

func (ia *inventoryagent) fetchFleetMetadata() {
ia.data["config_id"] = ia.conf.GetString("config_id")
}

func (ia *inventoryagent) refreshMetadata() {
// Core Agent / agent
ia.fetchCoreAgentMetadata()
Expand All @@ -368,6 +372,8 @@ func (ia *inventoryagent) refreshMetadata() {
ia.fetchTraceAgentMetadata()
// system-probe ecosystem
ia.fetchSystemProbeMetadata()
// Fleet
ia.fetchFleetMetadata()
}

func (ia *inventoryagent) writePayloadAsJSON(w http.ResponseWriter, _ *http.Request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,14 @@ dynamic_instrumentation:
assert.True(t, ia.data["feature_dynamic_instrumentation_enabled"].(bool))
}

func TestFetchFleet(t *testing.T) {
ia := getTestInventoryPayload(t, map[string]any{
"config_id": "my-config",
}, nil)
ia.fetchFleetMetadata()
assert.Equal(t, "my-config", ia.data["config_id"].(string))
}

func TestGetProvidedConfigurationDisable(t *testing.T) {
ia := getTestInventoryPayload(t, map[string]any{
"inventories_configuration_enabled": false,
Expand Down
14 changes: 10 additions & 4 deletions comp/metadata/inventoryagent/inventoryagentimpl/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,23 @@ type MockProvides struct {
Comp iainterface.Component
}

type inventoryagentMock struct{}
type inventoryagentMock struct {
data agentMetadata
}

func newMock() MockProvides {
ia := &inventoryagentMock{}
ia := &inventoryagentMock{
data: make(agentMetadata),
}
return MockProvides{
Comp: ia,
}
}

// Set is an empty function on this mock
func (m *inventoryagentMock) Set(string, interface{}) {}
func (m *inventoryagentMock) Set(key string, val interface{}) {
m.data[key] = val
}

// GetAsJSON is a mocked function
func (m *inventoryagentMock) GetAsJSON() ([]byte, error) {
Expand All @@ -52,7 +58,7 @@ func (m *inventoryagentMock) GetAsJSON() ([]byte, error) {

// Get is a mocked function
func (m *inventoryagentMock) Get() map[string]interface{} {
return nil
return m.data
}

// Refresh is a mocked function
Expand Down
2 changes: 1 addition & 1 deletion pkg/aggregator/aggregator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ func TestDefaultSeries(t *testing.T) {
}, &metrics.Serie{
Name: fmt.Sprintf("datadog.%s.ha_agent.running", agg.agentName),
Points: []metrics.Point{{Value: float64(1), Ts: float64(start.Unix())}},
Tags: tagset.CompositeTagsFromSlice([]string{"agent_state:standby"}),
Tags: tagset.CompositeTagsFromSlice([]string{"agent_state:active"}),
Host: agg.hostname,
MType: metrics.APIGaugeType,
SourceTypeName: "System",
Expand Down
Loading