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

[ha-agent] Consume RC HA_AGENT #31172

Merged
merged 3 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions comp/haagent/impl/haagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ package haagentimpl

import (
"context"
"encoding/json"

log "github.com/DataDog/datadog-agent/comp/core/log/def"
"github.com/DataDog/datadog-agent/pkg/remoteconfig/state"
"github.com/DataDog/datadog-agent/pkg/util/hostname"
"go.uber.org/atomic"
)
Expand Down Expand Up @@ -47,3 +49,38 @@ func (h *haAgentImpl) SetLeader(leaderAgentHostname string) {
}
h.isLeader.Store(agentHostname == leaderAgentHostname)
}

func (h *haAgentImpl) onHaAgentUpdate(updates map[string]state.RawConfig, applyStateCallback func(string, state.ApplyStatus)) {
h.log.Debugf("Updates received: count=%d", len(updates))

for configPath, rawConfig := range updates {
h.log.Debugf("Received config %s: %s", configPath, string(rawConfig.Config))
haAgentMsg := haAgentConfig{}
err := json.Unmarshal(rawConfig.Config, &haAgentMsg)
if err != nil {
h.log.Warnf("Skipping invalid HA_AGENT update %s: %v", configPath, err)
applyStateCallback(configPath, state.ApplyStatus{
State: state.ApplyStateError,
Error: "error unmarshalling payload",
})
continue
}
if haAgentMsg.Group != h.GetGroup() {
h.log.Warnf("Skipping invalid HA_AGENT update %s: expected group %s, got %s",
configPath, h.GetGroup(), haAgentMsg.Group)
applyStateCallback(configPath, state.ApplyStatus{
State: state.ApplyStateError,
Error: "group does not match",
})
continue
}

h.SetLeader(haAgentMsg.Leader)
AlexandreYang marked this conversation as resolved.
Show resolved Hide resolved

h.log.Debugf("Processed config %s: %v", configPath, haAgentMsg)

applyStateCallback(configPath, state.ApplyStatus{
State: state.ApplyStateUnacknowledged,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be ApplyStateAcknowledged

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 91ee48a

})
}
}
17 changes: 15 additions & 2 deletions comp/haagent/impl/haagent_comp.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ 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"
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
Expand All @@ -20,14 +22,25 @@ type Requires struct {

// Provides defines the output of the haagent component
type Provides struct {
Comp haagent.Component
Comp haagent.Component
RCListener rctypes.ListenerProvider
}

// NewComponent creates a new haagent component
func NewComponent(reqs Requires) (Provides, error) {
haAgentConfigs := newHaAgentConfigs(reqs.AgentConfig)
haAgent := newHaAgentImpl(reqs.Logger, haAgentConfigs)
var rcListener rctypes.ListenerProvider
if haAgent.Enabled() {
reqs.Logger.Debug("Add onHaAgentUpdate RCListener")
rcListener.ListenerProvider = rctypes.RCListener{
state.ProductHaAgent: haAgent.onHaAgentUpdate,
}
}

provides := Provides{
Comp: newHaAgentImpl(reqs.Logger, haAgentConfigs),
Comp: haAgent,
RCListener: rcListener,
}
return provides, nil
}
115 changes: 112 additions & 3 deletions comp/haagent/impl/haagent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,17 @@ package haagentimpl
import (
"testing"

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

var testConfigID = "datadog/2/HA_AGENT/group-62345762794c0c0b/65f17d667fb50f8ae28a3c858bdb1be9ea994f20249c119e007c520ac115c807"
var testGroup = "testGroup01"

func Test_Enabled(t *testing.T) {
tests := []struct {
name string
Expand All @@ -34,7 +42,7 @@ func Test_Enabled(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
haAgent := newTestHaAgentComponent(t, tt.configs)
haAgent := newTestHaAgentComponent(t, tt.configs).Comp
assert.Equal(t, tt.expectedEnabled, haAgent.Enabled())
})
}
Expand All @@ -44,19 +52,120 @@ func Test_GetGroup(t *testing.T) {
agentConfigs := map[string]interface{}{
"ha_agent.group": "my-group-01",
}
haAgent := newTestHaAgentComponent(t, agentConfigs)
haAgent := newTestHaAgentComponent(t, agentConfigs).Comp
assert.Equal(t, "my-group-01", haAgent.GetGroup())
}

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

haAgent.SetLeader("another-agent")
assert.False(t, haAgent.IsLeader())

haAgent.SetLeader("my-agent-hostname")
assert.True(t, haAgent.IsLeader())
}

func Test_RCListener(t *testing.T) {
tests := []struct {
name string
configs map[string]interface{}
expectRCListener bool
}{
{
name: "enabled",
configs: map[string]interface{}{
"ha_agent.enabled": true,
},
expectRCListener: true,
},
{
name: "disabled",
configs: map[string]interface{}{
"ha_agent.enabled": false,
},
expectRCListener: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
provides := newTestHaAgentComponent(t, tt.configs)
if tt.expectRCListener {
assert.NotNil(t, provides.RCListener.ListenerProvider)
} else {
assert.Nil(t, provides.RCListener.ListenerProvider)
}
})
}
}

func Test_haAgentImpl_onHaAgentUpdate(t *testing.T) {

tests := []struct {
name string
updates map[string]state.RawConfig
expectedApplyID string
expectedApplyStatus state.ApplyStatus
}{
{
name: "successful update",
updates: map[string]state.RawConfig{
testConfigID: {Config: []byte(`{"group":"testGroup01","leader":"ha-agent1"}`)},
},
expectedApplyID: testConfigID,
expectedApplyStatus: state.ApplyStatus{
State: state.ApplyStateUnacknowledged,
},
},
{
name: "invalid payload",
updates: map[string]state.RawConfig{
testConfigID: {Config: []byte(`invalid-json`)},
},
expectedApplyID: testConfigID,
expectedApplyStatus: state.ApplyStatus{
State: state.ApplyStateError,
Error: "error unmarshalling payload",
},
},
{
name: "invalid group",
updates: map[string]state.RawConfig{
testConfigID: {Config: []byte(`{"group":"invalidGroup","leader":"ha-agent1"}`)},
},
expectedApplyID: testConfigID,
expectedApplyStatus: state.ApplyStatus{
State: state.ApplyStateError,
Error: "group does not match",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
agentConfigs := map[string]interface{}{
"hostname": "my-agent-hostname",
"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))

var applyID string
var applyStatus state.ApplyStatus
applyFunc := func(id string, status state.ApplyStatus) {
applyID = id
applyStatus = status
}
h.onHaAgentUpdate(tt.updates, applyFunc)
assert.Equal(t, tt.expectedApplyID, applyID)
assert.Equal(t, tt.expectedApplyStatus, applyStatus)
})
}
}
8 changes: 2 additions & 6 deletions comp/haagent/impl/haagent_testutils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@ import (

"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/util/fxutil"
"github.com/stretchr/testify/require"
"go.uber.org/fx"
)

func newTestHaAgentComponent(t *testing.T, agentConfigs map[string]interface{}) haagent.Component {
func newTestHaAgentComponent(t *testing.T, agentConfigs map[string]interface{}) Provides {
logComponent := logmock.New(t)
agentConfigComponent := fxutil.Test[config.Component](t, fx.Options(
config.MockModule(),
Expand All @@ -30,8 +29,5 @@ func newTestHaAgentComponent(t *testing.T, agentConfigs map[string]interface{})

provides, err := NewComponent(requires)
require.NoError(t, err)

comp := provides.Comp
require.NotNil(t, comp)
return comp
return provides
}
11 changes: 11 additions & 0 deletions comp/haagent/impl/rcpayload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2024-present Datadog, Inc.

package haagentimpl

type haAgentConfig struct {
Group string `json:"group"`
Leader string `json:"leader"`
}
3 changes: 3 additions & 0 deletions pkg/remoteconfig/state/products.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ var validProducts = map[string]struct{}{
ProductTesting1: {},
ProductTesting2: {},
ProductOrchestratorK8sCRDs: {},
ProductHaAgent: {},
}

const (
Expand Down Expand Up @@ -87,4 +88,6 @@ const (
ProductTesting2 = "TESTING2"
// ProductOrchestratorK8sCRDs receives values for k8s crds
ProductOrchestratorK8sCRDs = "ORCHESTRATOR_K8S_CRDS"
// ProductHaAgent is the HA Agent product
ProductHaAgent = "HA_AGENT"
)
Loading