Skip to content

Commit 0e48ab3

Browse files
feat: feature flag to clean inventory cache on start
1 parent defe0d3 commit 0e48ab3

File tree

4 files changed

+67
-22
lines changed

4 files changed

+67
-22
lines changed

internal/agent/agent.go

+19
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818

1919
"github.com/newrelic/infrastructure-agent/pkg/sysinfo/hostid"
2020

21+
"github.com/newrelic/infrastructure-agent/internal/agent/cmdchannel/fflag"
2122
"github.com/newrelic/infrastructure-agent/internal/agent/instrumentation"
2223
"github.com/newrelic/infrastructure-agent/internal/agent/inventory"
2324
"github.com/newrelic/infrastructure-agent/internal/agent/types"
@@ -95,6 +96,7 @@ type Agent struct {
9596
agentID *entity.ID // pointer as it's referred from several points
9697
mtx sync.Mutex // Protect plugins
9798
notificationHandler *ctl.NotificationHandlerWithCancellation // Handle ipc messaging.
99+
ffRetriever feature_flags.Retriever
98100
}
99101

100102
type inventoryState struct {
@@ -393,6 +395,7 @@ func NewAgent(
393395
cloudHarvester,
394396
fpHarvester,
395397
notificationHandler,
398+
ffRetriever,
396399
)
397400
}
398401

@@ -410,6 +413,7 @@ func New(
410413
cloudHarvester cloud.Harvester,
411414
fpHarvester fingerprint.Harvester,
412415
notificationHandler *ctl.NotificationHandlerWithCancellation,
416+
ffRetriever feature_flags.Retriever,
413417
) (*Agent, error) {
414418
a := &Agent{
415419
Context: ctx,
@@ -422,6 +426,7 @@ func New(
422426
connectSrv: connectSrv,
423427
provideIDs: provideIDs,
424428
notificationHandler: notificationHandler,
429+
ffRetriever: ffRetriever,
425430
}
426431

427432
a.plugins = make([]Plugin, 0)
@@ -820,6 +825,20 @@ func (a *Agent) Run() (err error) {
820825
close(exit)
821826
}()
822827

828+
// We check FF to delete the whole inventory and trigger the whoel inventory send
829+
// This will bypass the deltas cache and force the inventory to be sent after restarting the Agent
830+
if a.ffRetriever != nil {
831+
alog.Debug("readding FlagFullInventoryDeletion feature flag")
832+
833+
ffFullInventoryDeletionEnabled, ffExists := a.ffRetriever.GetFeatureFlag(fflag.FlagFullInventoryDeletion)
834+
if ffExists && ffFullInventoryDeletionEnabled {
835+
alog.Info("Cleaning inventory cache and forcing full inventory report")
836+
a.store.ResetAllDeltas(a.Context.EntityKey())
837+
}
838+
} else {
839+
alog.Warn("Feature flags retriever is not available")
840+
}
841+
823842
if a.inventoryHandler != nil {
824843
if a.shouldSendInventory() {
825844
a.inventoryHandler.Start()

internal/agent/agent_test.go

+36-16
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
context2 "context"
1010
"encoding/json"
1111
"fmt"
12+
"github.com/newrelic/infrastructure-agent/internal/agent/cmdchannel/fflag"
1213
"io/ioutil"
1314
"net/http"
1415
"net/http/httptest"
@@ -52,7 +53,7 @@ var NilIDLookup host.IDLookup
5253

5354
var matcher = func(interface{}) bool { return true }
5455

55-
func newTesting(cfg *config.Config) *Agent {
56+
func newTesting(cfg *config.Config, ffRetriever feature_flags.Retriever) *Agent {
5657
dataDir, err := ioutil.TempDir("", "prefix")
5758
if err != nil {
5859
panic(err)
@@ -93,6 +94,7 @@ func newTesting(cfg *config.Config) *Agent {
9394
cloudDetector,
9495
fpHarvester,
9596
ctl.NewNotificationHandlerWithCancellation(nil),
97+
ffRetriever,
9698
)
9799
if err != nil {
98100
panic(err)
@@ -113,12 +115,13 @@ func (self *TestAgentData) SortKey() string {
113115
}
114116

115117
func TestIgnoreInventory(t *testing.T) {
118+
ffRetriever := &feature_flags.FeatureFlagRetrieverMock{}
116119
a := newTesting(&config.Config{
117120
IgnoredInventoryPathsMap: map[string]struct{}{
118121
"test/plugin/yum": {},
119122
},
120123
MaxInventorySize: 1024,
121-
})
124+
}, ffRetriever)
122125
defer func() {
123126
_ = os.RemoveAll(a.store.DataDir)
124127
}()
@@ -161,7 +164,8 @@ func TestServicePidMap(t *testing.T) {
161164
}
162165

163166
func TestSetAgentKeysDisplayInstance(t *testing.T) {
164-
a := newTesting(nil)
167+
ffRetriever := &feature_flags.FeatureFlagRetrieverMock{}
168+
a := newTesting(nil, ffRetriever)
165169
defer os.RemoveAll(a.store.DataDir)
166170

167171
idMap := host.IDLookup{
@@ -176,7 +180,8 @@ func TestSetAgentKeysDisplayInstance(t *testing.T) {
176180

177181
// Test that empty strings in the identity map are properly ignored in favor of non-empty ones
178182
func TestSetAgentKeysInstanceEmptyString(t *testing.T) {
179-
a := newTesting(nil)
183+
ffRetriever := &feature_flags.FeatureFlagRetrieverMock{}
184+
a := newTesting(nil, ffRetriever)
180185
defer os.RemoveAll(a.store.DataDir)
181186

182187
keys := host.IDLookup{
@@ -190,7 +195,8 @@ func TestSetAgentKeysInstanceEmptyString(t *testing.T) {
190195
}
191196

192197
func TestSetAgentKeysDisplayNameMatchesHostName(t *testing.T) {
193-
a := newTesting(nil)
198+
ffRetriever := &feature_flags.FeatureFlagRetrieverMock{}
199+
a := newTesting(nil, ffRetriever)
194200
defer os.RemoveAll(a.store.DataDir)
195201

196202
keyMap := host.IDLookup{
@@ -203,14 +209,16 @@ func TestSetAgentKeysDisplayNameMatchesHostName(t *testing.T) {
203209
}
204210

205211
func TestSetAgentKeysNoValues(t *testing.T) {
206-
a := newTesting(nil)
212+
ffRetriever := &feature_flags.FeatureFlagRetrieverMock{}
213+
a := newTesting(nil, ffRetriever)
207214
defer os.RemoveAll(a.store.DataDir)
208215

209216
assert.Error(t, a.setAgentKey(host.IDLookup{}))
210217
}
211218

212219
func TestUpdateIDLookupTable(t *testing.T) {
213-
a := newTesting(nil)
220+
ffRetriever := &feature_flags.FeatureFlagRetrieverMock{}
221+
a := newTesting(nil, ffRetriever)
214222
defer os.RemoveAll(a.store.DataDir)
215223

216224
dataset := agentTypes.PluginInventoryDataset{}
@@ -319,7 +327,8 @@ func TestRemoveOutdatedEntities(t *testing.T) {
319327
const anotherPlugin = "anotherPlugin"
320328

321329
// Given an agent
322-
agent := newTesting(nil)
330+
ffRetriever := &feature_flags.FeatureFlagRetrieverMock{}
331+
agent := newTesting(nil, ffRetriever)
323332
defer os.RemoveAll(agent.store.DataDir)
324333
agent.inventories = map[string]*inventoryEntity{}
325334

@@ -377,7 +386,8 @@ func TestRemoveOutdatedEntities(t *testing.T) {
377386

378387
func TestReconnectablePlugins(t *testing.T) {
379388
// Given an agent
380-
a := newTesting(nil)
389+
ffRetriever := &feature_flags.FeatureFlagRetrieverMock{}
390+
a := newTesting(nil, ffRetriever)
381391
defer os.RemoveAll(a.store.DataDir)
382392

383393
wg := sync.WaitGroup{}
@@ -487,7 +497,8 @@ func (killingPlugin) IsExternal() bool { return false }
487497
func (killingPlugin) GetExternalPluginName() string { return "" }
488498

489499
func TestTerminate(t *testing.T) {
490-
a := newTesting(nil)
500+
ffRetriever := &feature_flags.FeatureFlagRetrieverMock{}
501+
a := newTesting(nil, ffRetriever)
491502
defer func() {
492503
_ = os.RemoveAll(a.store.DataDir)
493504
}()
@@ -506,7 +517,9 @@ func TestStopByCancelFn_UsedBySignalHandler(t *testing.T) {
506517
wg := sync.WaitGroup{}
507518
wg.Add(1)
508519

509-
a := newTesting(nil)
520+
ffRetriever := &feature_flags.FeatureFlagRetrieverMock{}
521+
ffRetriever.ShouldGetFeatureFlag(fflag.FlagFullInventoryDeletion, false, false)
522+
a := newTesting(nil, ffRetriever)
510523

511524
defer func() {
512525
_ = os.RemoveAll(a.store.DataDir)
@@ -587,7 +600,10 @@ func TestAgent_Run_DontSendInventoryIfFwdOnly(t *testing.T) {
587600
FirstReapInterval: tt.firstReapInterval,
588601
SendInterval: tt.sendInterval,
589602
}
590-
a := newTesting(cfg)
603+
604+
ffRetriever := &feature_flags.FeatureFlagRetrieverMock{}
605+
ffRetriever.ShouldGetFeatureFlag(fflag.FlagFullInventoryDeletion, false, false)
606+
a := newTesting(cfg, ffRetriever)
591607
// Give time to at least send one request
592608
ctxTimeout, _ := context2.WithTimeout(a.Context.Ctx, time.Millisecond*10)
593609
a.Context.Ctx = ctxTimeout
@@ -719,7 +735,8 @@ func (self *testAgentNullableData) SortKey() string {
719735
}
720736

721737
func TestStorePluginOutput(t *testing.T) {
722-
a := newTesting(nil)
738+
ffRetriever := &feature_flags.FeatureFlagRetrieverMock{}
739+
a := newTesting(nil, ffRetriever)
723740
defer os.RemoveAll(a.store.DataDir)
724741
aV := "aValue"
725742
bV := "bValue"
@@ -769,7 +786,8 @@ func (self mockHostinfoData) SortKey() string {
769786
}
770787

771788
func BenchmarkStorePluginOutput(b *testing.B) {
772-
a := newTesting(&config.Config{MaxInventorySize: 1000 * 1000})
789+
ffRetriever := &feature_flags.FeatureFlagRetrieverMock{}
790+
a := newTesting(&config.Config{MaxInventorySize: 1000 * 1000}, ffRetriever)
773791
defer os.RemoveAll(a.store.DataDir)
774792

775793
distroName := "Fedora 29 (Cloud Edition)"
@@ -1166,11 +1184,12 @@ func TestRunsWithCloudProvider(t *testing.T) {
11661184
t.Run(testCase.name, func(t *testing.T) {
11671185
t.Parallel()
11681186

1187+
ffRetriever := &feature_flags.FeatureFlagRetrieverMock{}
11691188
//nolint:exhaustruct
11701189
agt := newTesting(&config.Config{
11711190
CloudProvider: testCase.cloudProvider,
11721191
CloudMaxRetryCount: testCase.retries,
1173-
})
1192+
}, ffRetriever)
11741193

11751194
err := agt.Run()
11761195

@@ -1224,7 +1243,8 @@ func TestAgent_checkInstanceIDRetry(t *testing.T) {
12241243
t.Run(testCase.name, func(t *testing.T) {
12251244
t.Parallel()
12261245

1227-
a := newTesting(nil)
1246+
ffRetriever := &feature_flags.FeatureFlagRetrieverMock{}
1247+
a := newTesting(nil, ffRetriever)
12281248
a.cloudHarvester = testCase.cloudHarvester
12291249

12301250
if err := a.checkInstanceIDRetry(testCase.args.maxRetries, testCase.args.backoffTime); (err != nil) != testCase.wantErr {

internal/agent/cmdchannel/fflag/ffhandler.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ const (
2323
FlagParallelizeInventory = "parallelize_inventory_enabled"
2424
FlagAsyncInventoryHandler = "async_inventory_handler_enabled"
2525

26-
FlagProtocolV4 = "protocol_v4_enabled"
27-
FlagFullProcess = "full_process_sampling"
28-
FlagDmRegisterDeprecated = "dm_register_deprecated"
29-
FlagFluentBit19 = "fluent_bit_19_win"
26+
FlagProtocolV4 = "protocol_v4_enabled"
27+
FlagFullProcess = "full_process_sampling"
28+
FlagDmRegisterDeprecated = "dm_register_deprecated"
29+
FlagFluentBit19 = "fluent_bit_19_win"
30+
FlagFullInventoryDeletion = "full_inventory_deletion"
3031
// Config
3132
CfgYmlRegisterEnabled = "register_enabled"
3233
CfgYmlParallelizeInventory = "inventory_queue_len"
@@ -183,7 +184,8 @@ func (h *handler) Handle(ctx context.Context, c commandapi.Command, isInitialFet
183184
func isBasicFeatureFlag(flag string) bool {
184185
return flag == FlagProtocolV4 ||
185186
flag == FlagFullProcess ||
186-
flag == FlagDmRegisterDeprecated
187+
flag == FlagDmRegisterDeprecated ||
188+
flag == FlagFullInventoryDeletion
187189
}
188190

189191
func (h *handler) setFFConfig(ff string, enabled bool) {

test/infra/agent.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ package infra
44

55
import (
66
"compress/gzip"
7+
"context"
8+
79
"io/ioutil"
810
"net/http"
911
"path/filepath"
@@ -17,6 +19,7 @@ import (
1719

1820
"github.com/newrelic/infrastructure-agent/internal/agent"
1921
"github.com/newrelic/infrastructure-agent/internal/agent/delta"
22+
"github.com/newrelic/infrastructure-agent/internal/feature_flags"
2023
"github.com/newrelic/infrastructure-agent/internal/testhelpers"
2124
backendhttp "github.com/newrelic/infrastructure-agent/pkg/backend/http"
2225
"github.com/newrelic/infrastructure-agent/pkg/backend/identityapi"
@@ -112,7 +115,8 @@ func NewAgentWithConnectClientAndConfig(connectClient *http.Client, dataClient b
112115
transport := backendhttp.BuildTransport(cfg, backendhttp.ClientTimeout)
113116
transport = backendhttp.NewRequestDecoratorTransport(cfg, transport)
114117
dataClient = backendhttp.NewRequestDecoratorTransport(cfg, infra.ToRoundTripper(dataClient)).RoundTrip
115-
a, err := agent.New(cfg, ctx, "user-agent", lookups, st, connectSrv, provideIDs, dataClient, transport, cloudDetector, fingerprintHarvester, ctl.NewNotificationHandlerWithCancellation(nil))
118+
ffRetriever := feature_flags.NewManager(map[string]bool{})
119+
a, err := agent.New(cfg, ctx, "user-agent", lookups, st, connectSrv, provideIDs, dataClient, transport, cloudDetector, fingerprintHarvester, ctl.NewNotificationHandlerWithCancellation(context.TODO()), ffRetriever)
116120
if err != nil {
117121
panic(err)
118122
}

0 commit comments

Comments
 (0)