From 8702229be181e868ae29cadeaef5b1adb0bc4f14 Mon Sep 17 00:00:00 2001 From: Marcos Date: Tue, 5 Sep 2023 19:16:54 -0300 Subject: [PATCH 1/8] added bot logger --- services/components/components.go | 16 ++- services/components/lifecycle/bot_logger.go | 116 ++++++++++++++++++ .../components/lifecycle/bot_logger_test.go | 3 + services/components/lifecycle/bot_pool.go | 8 +- services/supervisor/agent_logs.go | 66 +--------- services/supervisor/services.go | 2 +- 6 files changed, 142 insertions(+), 69 deletions(-) create mode 100644 services/components/lifecycle/bot_logger.go create mode 100644 services/components/lifecycle/bot_logger_test.go diff --git a/services/components/components.go b/services/components/components.go index d43704de..6c37c7c0 100644 --- a/services/components/components.go +++ b/services/components/components.go @@ -9,6 +9,7 @@ import ( "github.com/forta-network/forta-node/clients" "github.com/forta-network/forta-node/clients/agentgrpc" "github.com/forta-network/forta-node/clients/docker" + "github.com/forta-network/forta-core-go/clients/agentlogs" "github.com/forta-network/forta-node/config" "github.com/forta-network/forta-node/services/components/botio" "github.com/forta-network/forta-node/services/components/botio/botreq" @@ -79,10 +80,16 @@ type BotLifecycle struct { BotManager lifecycle.BotLifecycleManager BotClient containers.BotClient ImageCleanup containers.ImageCleanup + BotLogger lifecycle.BotLogger } // GetBotLifecycleComponents returns the bot lifecycle management components. -func GetBotLifecycleComponents(ctx context.Context, botLifeConfig BotLifecycleConfig) (BotLifecycle, error) { +func GetBotLifecycleComponents( + ctx context.Context, + botLifeConfig BotLifecycleConfig, + supervisorConfig lifecycle.SupervisorServiceConfig, + sendAgentLogs func(agents agentlogs.Agents, authToken string) error, +) (BotLifecycle, error) { cfg := botLifeConfig.Config // bot image client is helpful for loading local mode agents from a restricted container registry var ( @@ -120,10 +127,17 @@ func GetBotLifecycleComponents(ctx context.Context, botLifeConfig BotLifecycleCo lifecycleMetrics, botMonitor, ) imageCleanup := containers.NewImageCleanup(dockerClient, botLifeConfig.BotRegistry) + botLogger := lifecycle.NewBotLogger( + botClient, + dockerClient, + supervisorConfig, + sendAgentLogs, + ) return BotLifecycle{ BotManager: botManager, BotClient: botClient, ImageCleanup: imageCleanup, + BotLogger: botLogger, }, nil } diff --git a/services/components/lifecycle/bot_logger.go b/services/components/lifecycle/bot_logger.go new file mode 100644 index 00000000..4ae1e75d --- /dev/null +++ b/services/components/lifecycle/bot_logger.go @@ -0,0 +1,116 @@ +package lifecycle + +import ( + "context" + "fmt" + "strconv" + "time" + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/forta-network/forta-core-go/security" + "github.com/forta-network/forta-core-go/clients/agentlogs" + "github.com/forta-network/forta-node/clients" + "github.com/forta-network/forta-node/clients/docker" + "github.com/forta-network/forta-node/services/components/containers" + log "github.com/sirupsen/logrus" +) + +// BotLogger manages bots logging. +type BotLogger interface { + SendBotLogs(ctx context.Context) error +} + +type SupervisorServiceConfig struct { + Key *keystore.Key +} + +type botLogger struct { + botClient containers.BotClient + dockerClient clients.DockerClient + supervisorConfig SupervisorServiceConfig + prevAgentLogs agentlogs.Agents + + sendAgentLogs func(agents agentlogs.Agents, authToken string) error +} + +var _ BotLogger = &botLogger{} + +func NewBotLogger( + botClient containers.BotClient, + dockerClient clients.DockerClient, + supervisorConfig SupervisorServiceConfig, + sendAgentLogs func(agents agentlogs.Agents, authToken string) error, +) *botLogger { + return &botLogger{ + botClient: botClient, + dockerClient: dockerClient, + supervisorConfig: supervisorConfig, + sendAgentLogs: sendAgentLogs, + } +} + +// adjust these better with auto-upgrade later +const ( + defaultAgentLogSendInterval = time.Minute + defaultAgentLogTailLines = 50 + defaultAgentLogAvgMaxCharsPerLine = 200 +) + +func (bl *botLogger) SendBotLogs(ctx context.Context) error { + var ( + sendLogs agentlogs.Agents + keepLogs agentlogs.Agents + ) + + botContainers, err := bl.botClient.LoadBotContainers(ctx) + if err != nil { + return fmt.Errorf("failed to load the bot containers: %v", err) + } + + for _, container := range botContainers { + if container.Labels[docker.LabelFortaSettingsAgentLogsEnable] != "true" { + continue + } + logs, err := bl.dockerClient.GetContainerLogs( + ctx, container.ID, + strconv.Itoa(defaultAgentLogTailLines), + defaultAgentLogAvgMaxCharsPerLine*defaultAgentLogTailLines, + ) + if err != nil { + log.WithError(err).Warn("failed to get agent container logs") + continue + } + + agent := &agentlogs.Agent{ + ID: container.Labels[docker.LabelFortaBotID], + Logs: logs, + } + // don't send if it's the same with previous logs but keep it for next time + // so we can check + keepLogs = append(keepLogs, agent) + if !bl.prevAgentLogs.Has(agent.ID, logs) { + log.WithField("agent", agent.ID).Debug("new agent logs found") + sendLogs = append(sendLogs, agent) + } else { + log.WithField("agent", agent.ID).Debug("no new agent logs") + } + } + + if len(sendLogs) > 0 { + // TODO: check if possible to set bot_logger as access to create JWT. + scannerJwt, err := security.CreateScannerJWT(bl.supervisorConfig.Key, map[string]interface{}{ + "access": "bot_logger", + }) + if err != nil { + return fmt.Errorf("failed to create scanner token: %v", err) + } + if err := bl.sendAgentLogs(sendLogs, scannerJwt); err != nil { + return fmt.Errorf("failed to send agent logs: %v", err) + } + log.WithField("count", len(sendLogs)).Debug("successfully sent new agent logs") + } else { + log.Debug("no new agent logs were found - not sending") + } + + bl.prevAgentLogs = keepLogs + return nil +} diff --git a/services/components/lifecycle/bot_logger_test.go b/services/components/lifecycle/bot_logger_test.go new file mode 100644 index 00000000..1793f629 --- /dev/null +++ b/services/components/lifecycle/bot_logger_test.go @@ -0,0 +1,3 @@ +package lifecycle + +// TODO: add tests. \ No newline at end of file diff --git a/services/components/lifecycle/bot_pool.go b/services/components/lifecycle/bot_pool.go index df2f3314..fe130940 100644 --- a/services/components/lifecycle/bot_pool.go +++ b/services/components/lifecycle/bot_pool.go @@ -91,7 +91,7 @@ func (bp *botPool) UpdateBotsWithLatestConfigs(latestConfigs messaging.AgentPayl // add new bots var latestBotClients []botio.BotClient for _, botConfig := range latestConfigs { - logger := botLogger(botConfig) + logger := botLog(botConfig) botClient, ok := bp.getBotClient(botConfig.ContainerName()) if ok && !botClient.IsClosed() { logger.Debug("bot client already exists - skipping update") @@ -112,7 +112,7 @@ func (bp *botPool) UpdateBotsWithLatestConfigs(latestConfigs messaging.AgentPayl // updated the config of the bots that have different config updatedBotConfigs := FindUpdatedBots(bp.getConfigsUnsafe(), latestConfigs) for _, updatedBotConfig := range updatedBotConfigs { - logger := botLogger(updatedBotConfig) + logger := botLog(updatedBotConfig) botClient, ok := bp.getBotClient(updatedBotConfig.ContainerName()) if !ok { logger.Info("could not find the updated bot! skipping") @@ -152,7 +152,7 @@ func (bp *botPool) RemoveBotsWithConfigs(removedBotConfigs messaging.AgentPayloa // close and discard the removed bots for _, removedBotConfig := range removedBotConfigs { - logger := botLogger(removedBotConfig) + logger := botLog(removedBotConfig) botClient, ok := bp.getBotClient(removedBotConfig.ContainerName()) if !ok { logger.Info("could not find the removed bot! skipping") @@ -212,7 +212,7 @@ func (bp *botPool) WaitForAll() { bp.botWg.Wait() } -func botLogger(botConfig config.AgentConfig) *log.Entry { +func botLog(botConfig config.AgentConfig) *log.Entry { return log.WithField("bot", botConfig.ID).WithField("container", botConfig.ContainerName()) } diff --git a/services/supervisor/agent_logs.go b/services/supervisor/agent_logs.go index cc307fb7..9b211c2f 100644 --- a/services/supervisor/agent_logs.go +++ b/services/supervisor/agent_logs.go @@ -5,19 +5,9 @@ import ( "strconv" "time" - "github.com/forta-network/forta-core-go/clients/agentlogs" - "github.com/forta-network/forta-core-go/security" - "github.com/forta-network/forta-node/clients/docker" log "github.com/sirupsen/logrus" ) -// adjust these better with auto-upgrade later -const ( - defaultAgentLogSendInterval = time.Minute - defaultAgentLogTailLines = 50 - defaultAgentLogAvgMaxCharsPerLine = 200 -) - func (sup *SupervisorService) syncAgentLogs() { interval := time.Duration(sup.botLifecycleConfig.Config.AgentLogsConfig.SendIntervalSeconds) * time.Second ticker := time.NewTicker(interval) @@ -35,60 +25,10 @@ func (sup *SupervisorService) doSyncAgentLogs() error { sup.mu.RLock() defer sup.mu.RUnlock() - var ( - sendLogs agentlogs.Agents - keepLogs agentlogs.Agents - ) - - botContainers, err := sup.botLifecycle.BotClient.LoadBotContainers(sup.ctx) - if err != nil { - return fmt.Errorf("failed to load the bot containers: %v", err) - } - - for _, container := range botContainers { - if container.Labels[docker.LabelFortaSettingsAgentLogsEnable] != "true" { - continue - } - logs, err := sup.client.GetContainerLogs( - sup.ctx, container.ID, - strconv.Itoa(defaultAgentLogTailLines), - defaultAgentLogAvgMaxCharsPerLine*defaultAgentLogTailLines, - ) - if err != nil { - log.WithError(err).Warn("failed to get agent container logs") - continue - } - - agent := &agentlogs.Agent{ - ID: container.Labels[docker.LabelFortaBotID], - Logs: logs, - } - // don't send if it's the same with previous logs but keep it for next time - // so we can check - keepLogs = append(keepLogs, agent) - if !sup.prevAgentLogs.Has(agent.ID, logs) { - log.WithField("agent", agent.ID).Debug("new agent logs found") - sendLogs = append(sendLogs, agent) - } else { - log.WithField("agent", agent.ID).Debug("no new agent logs") - } - } - - if len(sendLogs) > 0 { - scannerJwt, err := security.CreateScannerJWT(sup.config.Key, map[string]interface{}{ - "access": "agent_logs", - }) - if err != nil { - return fmt.Errorf("failed to create scanner token: %v", err) - } - if err := sup.sendAgentLogs(sendLogs, scannerJwt); err != nil { - return fmt.Errorf("failed to send agent logs: %v", err) - } - log.WithField("count", len(sendLogs)).Debug("successfully sent new agent logs") - } else { - log.Debug("no new agent logs were found - not sending") + if err := sup.botLifecycle.botLogger.SendBotLogs(sup.ctx); err != nil { + log.WithError(err).Error("error while sending bot logs") + return err } - sup.prevAgentLogs = keepLogs return nil } diff --git a/services/supervisor/services.go b/services/supervisor/services.go index dbfb2287..ab9f81e2 100644 --- a/services/supervisor/services.go +++ b/services/supervisor/services.go @@ -238,7 +238,7 @@ func (sup *SupervisorService) start() error { sup.msgClient = messaging.NewClient("supervisor", fmt.Sprintf("%s:%s", config.DockerNatsContainerName, config.DefaultNatsPort)) } sup.botLifecycleConfig.MessageClient = sup.msgClient // we are able to set this dependency only here - sup.botLifecycle, err = components.GetBotLifecycleComponents(sup.ctx, sup.botLifecycleConfig) + sup.botLifecycle, err = components.GetBotLifecycleComponents(sup.ctx, sup.botLifecycleConfig, sup.config, sup.sendAgentLogs) if err != nil { return fmt.Errorf("failed to get bot lifecycle components: %v", err) } From 5260dda4ec6085616d1be34854ef76a9997be2b4 Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 7 Sep 2023 15:40:04 -0300 Subject: [PATCH 2/8] fix build issues + code refactor --- services/components/components.go | 14 ++++----- services/components/lifecycle/bot_logger.go | 33 ++++++++++----------- services/supervisor/agent_logs.go | 16 +--------- services/supervisor/services.go | 8 ++--- services/supervisor/services_test.go | 14 --------- 5 files changed, 25 insertions(+), 60 deletions(-) diff --git a/services/components/components.go b/services/components/components.go index 6c37c7c0..3b977890 100644 --- a/services/components/components.go +++ b/services/components/components.go @@ -4,12 +4,13 @@ import ( "context" "fmt" + "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" + "github.com/forta-network/forta-core-go/clients/agentlogs" "github.com/forta-network/forta-core-go/utils" "github.com/forta-network/forta-node/clients" "github.com/forta-network/forta-node/clients/agentgrpc" "github.com/forta-network/forta-node/clients/docker" - "github.com/forta-network/forta-core-go/clients/agentlogs" "github.com/forta-network/forta-node/config" "github.com/forta-network/forta-node/services/components/botio" "github.com/forta-network/forta-node/services/components/botio/botreq" @@ -73,6 +74,7 @@ type BotLifecycleConfig struct { ScannerAddress common.Address MessageClient clients.MessageClient BotRegistry registry.BotRegistry + Key *keystore.Key } // BotLifecycle contains the bot lifecycle components. @@ -85,10 +87,8 @@ type BotLifecycle struct { // GetBotLifecycleComponents returns the bot lifecycle management components. func GetBotLifecycleComponents( - ctx context.Context, + ctx context.Context, botLifeConfig BotLifecycleConfig, - supervisorConfig lifecycle.SupervisorServiceConfig, - sendAgentLogs func(agents agentlogs.Agents, authToken string) error, ) (BotLifecycle, error) { cfg := botLifeConfig.Config // bot image client is helpful for loading local mode agents from a restricted container registry @@ -130,14 +130,14 @@ func GetBotLifecycleComponents( botLogger := lifecycle.NewBotLogger( botClient, dockerClient, - supervisorConfig, - sendAgentLogs, + botLifeConfig.Key, + agentlogs.NewClient("").SendLogs, ) return BotLifecycle{ BotManager: botManager, BotClient: botClient, ImageCleanup: imageCleanup, - BotLogger: botLogger, + BotLogger: botLogger, }, nil } diff --git a/services/components/lifecycle/bot_logger.go b/services/components/lifecycle/bot_logger.go index 4ae1e75d..b6d9fccb 100644 --- a/services/components/lifecycle/bot_logger.go +++ b/services/components/lifecycle/bot_logger.go @@ -5,9 +5,10 @@ import ( "fmt" "strconv" "time" + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/forta-network/forta-core-go/clients/agentlogs" "github.com/forta-network/forta-core-go/security" - "github.com/forta-network/forta-core-go/clients/agentlogs" "github.com/forta-network/forta-node/clients" "github.com/forta-network/forta-node/clients/docker" "github.com/forta-network/forta-node/services/components/containers" @@ -19,15 +20,11 @@ type BotLogger interface { SendBotLogs(ctx context.Context) error } -type SupervisorServiceConfig struct { - Key *keystore.Key -} - type botLogger struct { - botClient containers.BotClient - dockerClient clients.DockerClient - supervisorConfig SupervisorServiceConfig - prevAgentLogs agentlogs.Agents + botClient containers.BotClient + dockerClient clients.DockerClient + key *keystore.Key + prevAgentLogs agentlogs.Agents sendAgentLogs func(agents agentlogs.Agents, authToken string) error } @@ -35,16 +32,16 @@ type botLogger struct { var _ BotLogger = &botLogger{} func NewBotLogger( - botClient containers.BotClient, - dockerClient clients.DockerClient, - supervisorConfig SupervisorServiceConfig, - sendAgentLogs func(agents agentlogs.Agents, authToken string) error, + botClient containers.BotClient, + dockerClient clients.DockerClient, + key *keystore.Key, + sendAgentLogs func(agents agentlogs.Agents, authToken string) error, ) *botLogger { return &botLogger{ - botClient: botClient, - dockerClient: dockerClient, - supervisorConfig: supervisorConfig, - sendAgentLogs: sendAgentLogs, + botClient: botClient, + dockerClient: dockerClient, + key: key, + sendAgentLogs: sendAgentLogs, } } @@ -97,7 +94,7 @@ func (bl *botLogger) SendBotLogs(ctx context.Context) error { if len(sendLogs) > 0 { // TODO: check if possible to set bot_logger as access to create JWT. - scannerJwt, err := security.CreateScannerJWT(bl.supervisorConfig.Key, map[string]interface{}{ + scannerJwt, err := security.CreateScannerJWT(bl.key, map[string]interface{}{ "access": "bot_logger", }) if err != nil { diff --git a/services/supervisor/agent_logs.go b/services/supervisor/agent_logs.go index 9b211c2f..27352884 100644 --- a/services/supervisor/agent_logs.go +++ b/services/supervisor/agent_logs.go @@ -1,8 +1,6 @@ package supervisor import ( - "fmt" - "strconv" "time" log "github.com/sirupsen/logrus" @@ -12,7 +10,7 @@ func (sup *SupervisorService) syncAgentLogs() { interval := time.Duration(sup.botLifecycleConfig.Config.AgentLogsConfig.SendIntervalSeconds) * time.Second ticker := time.NewTicker(interval) for range ticker.C { - err := sup.doSyncAgentLogs() + err := sup.botLifecycle.BotLogger.SendBotLogs(sup.ctx) sup.lastAgentLogsRequest.Set() sup.lastAgentLogsRequestError.Set(err) if err != nil { @@ -20,15 +18,3 @@ func (sup *SupervisorService) syncAgentLogs() { } } } - -func (sup *SupervisorService) doSyncAgentLogs() error { - sup.mu.RLock() - defer sup.mu.RUnlock() - - if err := sup.botLifecycle.botLogger.SendBotLogs(sup.ctx); err != nil { - log.WithError(err).Error("error while sending bot logs") - return err - } - - return nil -} diff --git a/services/supervisor/services.go b/services/supervisor/services.go index ab9f81e2..1d19566e 100644 --- a/services/supervisor/services.go +++ b/services/supervisor/services.go @@ -11,7 +11,6 @@ import ( "time" "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/forta-network/forta-core-go/clients/agentlogs" "github.com/forta-network/forta-core-go/clients/health" "github.com/forta-network/forta-core-go/manifest" "github.com/forta-network/forta-core-go/protocol" @@ -78,9 +77,7 @@ type SupervisorService struct { healthClient health.HealthClient - sendAgentLogs func(agents agentlogs.Agents, authToken string) error - prevAgentLogs agentlogs.Agents - inspectionCh chan *protocol.InspectionResults + inspectionCh chan *protocol.InspectionResults } type SupervisorServiceConfig struct { @@ -238,7 +235,7 @@ func (sup *SupervisorService) start() error { sup.msgClient = messaging.NewClient("supervisor", fmt.Sprintf("%s:%s", config.DockerNatsContainerName, config.DefaultNatsPort)) } sup.botLifecycleConfig.MessageClient = sup.msgClient // we are able to set this dependency only here - sup.botLifecycle, err = components.GetBotLifecycleComponents(sup.ctx, sup.botLifecycleConfig, sup.config, sup.sendAgentLogs) + sup.botLifecycle, err = components.GetBotLifecycleComponents(sup.ctx, sup.botLifecycleConfig) if err != nil { return fmt.Errorf("failed to get bot lifecycle components: %v", err) } @@ -777,7 +774,6 @@ func NewSupervisorService(ctx context.Context, cfg SupervisorServiceConfig) (*Su botLifecycleConfig: cfg.BotLifecycleConfig, config: cfg, healthClient: health.NewClient(), - sendAgentLogs: agentlogs.NewClient(cfg.Config.AgentLogsConfig.URL).SendLogs, inspectionCh: make(chan *protocol.InspectionResults), } sup.autoUpdatesDisabled.Set(strconv.FormatBool(cfg.Config.AutoUpdate.Disable)) diff --git a/services/supervisor/services_test.go b/services/supervisor/services_test.go index 395db990..ebc923c6 100644 --- a/services/supervisor/services_test.go +++ b/services/supervisor/services_test.go @@ -7,7 +7,6 @@ import ( "testing" "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/forta-network/forta-core-go/clients/agentlogs" "github.com/forta-network/forta-core-go/release" "github.com/forta-network/forta-core-go/security" "github.com/forta-network/forta-core-go/utils" @@ -251,19 +250,6 @@ func (s *Suite) TestStartServices() { s.r.NoError(s.supervisor.start()) } -func (s *Suite) TestDoSyncAgentLogs() { - s.botClient.EXPECT().LoadBotContainers(gomock.Any()).Return([]types.Container{{ - Labels: map[string]string{ - docker.LabelFortaSettingsAgentLogsEnable: "true", - }, - }}, nil) - s.dockerClient.EXPECT().GetContainerLogs(gomock.Any(), gomock.Any(), "50", 10000) - s.supervisor.sendAgentLogs = func(agents agentlogs.Agents, authToken string) error { - return nil - } - s.r.NoError(s.supervisor.doSyncAgentLogs()) -} - func (s *Suite) TestDoHealthCheck() { s.r.NoError(s.supervisor.doHealthCheck()) } From 8a519f7fab303407273302049f69ca2d60e73848 Mon Sep 17 00:00:00 2001 From: Marcos Date: Sat, 16 Sep 2023 13:26:04 -0300 Subject: [PATCH 3/8] remove TODO comment --- services/components/lifecycle/bot_logger.go | 1 - 1 file changed, 1 deletion(-) diff --git a/services/components/lifecycle/bot_logger.go b/services/components/lifecycle/bot_logger.go index b6d9fccb..da7620ac 100644 --- a/services/components/lifecycle/bot_logger.go +++ b/services/components/lifecycle/bot_logger.go @@ -93,7 +93,6 @@ func (bl *botLogger) SendBotLogs(ctx context.Context) error { } if len(sendLogs) > 0 { - // TODO: check if possible to set bot_logger as access to create JWT. scannerJwt, err := security.CreateScannerJWT(bl.key, map[string]interface{}{ "access": "bot_logger", }) From 75aebca6c3b2ab061fdef7cf51785287f1593e70 Mon Sep 17 00:00:00 2001 From: Marcos Date: Sat, 16 Sep 2023 15:43:25 -0300 Subject: [PATCH 4/8] add test new bot logger --- .../components/lifecycle/bot_logger_test.go | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/services/components/lifecycle/bot_logger_test.go b/services/components/lifecycle/bot_logger_test.go index 1793f629..20509cbe 100644 --- a/services/components/lifecycle/bot_logger_test.go +++ b/services/components/lifecycle/bot_logger_test.go @@ -1,3 +1,38 @@ package lifecycle -// TODO: add tests. \ No newline at end of file +import ( + "testing" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/forta-network/forta-core-go/clients/agentlogs" + "github.com/forta-network/forta-core-go/security" + mock_clients "github.com/forta-network/forta-node/clients/mocks" + mock_containers "github.com/forta-network/forta-node/services/components/containers/mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +func sendLogsMock(agents agentlogs.Agents, authToken string) error { + return nil +} + +func TestNewBotLogger(t *testing.T) { + ctrl := gomock.NewController(t) + + r := require.New(t) + + botClient := mock_containers.NewMockBotClient(ctrl) + dockerClient := mock_clients.NewMockDockerClient(ctrl) + + dir := t.TempDir() + ks := keystore.NewKeyStore(dir, keystore.StandardScryptN, keystore.StandardScryptP) + + _, err := ks.NewAccount("Forta123") + r.NoError(err) + + key, err := security.LoadKeyWithPassphrase(dir, "Forta123") + r.NoError(err) + + botLogger := NewBotLogger(botClient, dockerClient, key, sendLogsMock) + r.NotNil(botLogger) +} From 6e686403fd89ce907101c0ce71ab5ee8355d05eb Mon Sep 17 00:00:00 2001 From: Marcos Date: Tue, 19 Sep 2023 18:30:12 -0300 Subject: [PATCH 5/8] refactor: setup tests --- .../components/lifecycle/bot_logger_test.go | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/services/components/lifecycle/bot_logger_test.go b/services/components/lifecycle/bot_logger_test.go index 20509cbe..cd6daf32 100644 --- a/services/components/lifecycle/bot_logger_test.go +++ b/services/components/lifecycle/bot_logger_test.go @@ -10,16 +10,24 @@ import ( mock_containers "github.com/forta-network/forta-node/services/components/containers/mocks" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" ) -func sendLogsMock(agents agentlogs.Agents, authToken string) error { - return nil +func TestSendBotLogsSuite(t *testing.T) { + suite.Run(t, &BotLoggerSuite{}) } -func TestNewBotLogger(t *testing.T) { - ctrl := gomock.NewController(t) +type BotLoggerSuite struct { + r *require.Assertions - r := require.New(t) + botLogger *botLogger + suite.Suite +} + +func (s *BotLoggerSuite) SetupTest() { + t := s.T() + ctrl := gomock.NewController(s.T()) + r := s.Require() botClient := mock_containers.NewMockBotClient(ctrl) dockerClient := mock_clients.NewMockDockerClient(ctrl) @@ -33,6 +41,16 @@ func TestNewBotLogger(t *testing.T) { key, err := security.LoadKeyWithPassphrase(dir, "Forta123") r.NoError(err) - botLogger := NewBotLogger(botClient, dockerClient, key, sendLogsMock) - r.NotNil(botLogger) + sendLogsMockFn := func(agents agentlogs.Agents, authToken string) error { + return nil + } + + botLogger := NewBotLogger(botClient, dockerClient, key, sendLogsMockFn) + + s.botLogger = botLogger + s.r = r +} + +func (s *BotLoggerSuite) TestSendBotLogs() { + s.r.NotNil(s.botLogger) } From 96baf470d1226eb9ea8f66e28492d46318bb3539 Mon Sep 17 00:00:00 2001 From: Marcos Date: Tue, 19 Sep 2023 19:15:57 -0300 Subject: [PATCH 6/8] add initial test --- services/components/components.go | 2 +- .../components/lifecycle/bot_logger_test.go | 43 +++++++++++++++---- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/services/components/components.go b/services/components/components.go index 3b977890..4bc6c0f6 100644 --- a/services/components/components.go +++ b/services/components/components.go @@ -131,7 +131,7 @@ func GetBotLifecycleComponents( botClient, dockerClient, botLifeConfig.Key, - agentlogs.NewClient("").SendLogs, + agentlogs.NewClient(botLifeConfig.Config.AgentLogsConfig.URL).SendLogs, ) return BotLifecycle{ diff --git a/services/components/lifecycle/bot_logger_test.go b/services/components/lifecycle/bot_logger_test.go index cd6daf32..614ea741 100644 --- a/services/components/lifecycle/bot_logger_test.go +++ b/services/components/lifecycle/bot_logger_test.go @@ -1,8 +1,10 @@ package lifecycle import ( + "context" "testing" + "github.com/docker/docker/api/types" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/forta-network/forta-core-go/clients/agentlogs" "github.com/forta-network/forta-core-go/security" @@ -20,7 +22,10 @@ func TestSendBotLogsSuite(t *testing.T) { type BotLoggerSuite struct { r *require.Assertions - botLogger *botLogger + botLogger *botLogger + botClient *mock_containers.MockBotClient + dockerClient *mock_clients.MockDockerClient + key *keystore.Key suite.Suite } @@ -41,16 +46,36 @@ func (s *BotLoggerSuite) SetupTest() { key, err := security.LoadKeyWithPassphrase(dir, "Forta123") r.NoError(err) - sendLogsMockFn := func(agents agentlogs.Agents, authToken string) error { - return nil - } - - botLogger := NewBotLogger(botClient, dockerClient, key, sendLogsMockFn) - - s.botLogger = botLogger + s.botClient = botClient + s.dockerClient = dockerClient + s.key = key s.r = r } func (s *BotLoggerSuite) TestSendBotLogs() { - s.r.NotNil(s.botLogger) + botLogger := NewBotLogger( + s.botClient, s.dockerClient, s.key, + func(agents agentlogs.Agents, authToken string) error { + s.r.Equal(2, len(agents)) + s.r.Equal("bot1", agents[0].ID) + s.r.Equal("bot2", agents[1].ID) + // TODO: test sendLogs = 1 and sendLogs = 0 conditions + return nil + }, + ) + ctx := context.Background() + + mockContainers := []types.Container{ + { + ID: "bot1", + Image: "forta/bot:latest", + }, + { + ID: "bot2", + Image: "forta/bot:latest", + }, + } + s.botClient.EXPECT().LoadBotContainers(ctx).Return(mockContainers, nil) + + botLogger.SendBotLogs(ctx) } From 555828ba4045856654f8f0aa9093ae3169a50d98 Mon Sep 17 00:00:00 2001 From: Marcos Date: Tue, 19 Sep 2023 22:26:43 -0300 Subject: [PATCH 7/8] added tests --- .../components/lifecycle/bot_logger_test.go | 125 +++++++++++++++++- 1 file changed, 122 insertions(+), 3 deletions(-) diff --git a/services/components/lifecycle/bot_logger_test.go b/services/components/lifecycle/bot_logger_test.go index 614ea741..778622cb 100644 --- a/services/components/lifecycle/bot_logger_test.go +++ b/services/components/lifecycle/bot_logger_test.go @@ -2,12 +2,15 @@ package lifecycle import ( "context" + "errors" + "strconv" "testing" "github.com/docker/docker/api/types" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/forta-network/forta-core-go/clients/agentlogs" "github.com/forta-network/forta-core-go/security" + "github.com/forta-network/forta-node/clients/docker" mock_clients "github.com/forta-network/forta-node/clients/mocks" mock_containers "github.com/forta-network/forta-node/services/components/containers/mocks" "github.com/golang/mock/gomock" @@ -57,9 +60,8 @@ func (s *BotLoggerSuite) TestSendBotLogs() { s.botClient, s.dockerClient, s.key, func(agents agentlogs.Agents, authToken string) error { s.r.Equal(2, len(agents)) - s.r.Equal("bot1", agents[0].ID) - s.r.Equal("bot2", agents[1].ID) - // TODO: test sendLogs = 1 and sendLogs = 0 conditions + s.r.Equal("bot1ID", agents[0].ID) + s.r.Equal("bot2ID", agents[1].ID) return nil }, ) @@ -69,13 +71,130 @@ func (s *BotLoggerSuite) TestSendBotLogs() { { ID: "bot1", Image: "forta/bot:latest", + Labels: map[string]string{ + docker.LabelFortaSettingsAgentLogsEnable: "true", + docker.LabelFortaBotID: "bot1ID", + }, }, { ID: "bot2", Image: "forta/bot:latest", + Labels: map[string]string{ + docker.LabelFortaSettingsAgentLogsEnable: "true", + docker.LabelFortaBotID: "bot2ID", + }, }, } + s.dockerClient.EXPECT().GetContainerLogs( + ctx, "bot1", + strconv.Itoa(defaultAgentLogTailLines), + defaultAgentLogAvgMaxCharsPerLine*defaultAgentLogTailLines, + ).Return("some log", nil).Times(1) + + s.dockerClient.EXPECT().GetContainerLogs( + ctx, "bot2", + strconv.Itoa(defaultAgentLogTailLines), + defaultAgentLogAvgMaxCharsPerLine*defaultAgentLogTailLines, + ).Return("some log", nil).Times(1) + s.botClient.EXPECT().LoadBotContainers(ctx).Return(mockContainers, nil) + botLogger.SendBotLogs(ctx) +} + +// should fail if there is an error loading +// bot containers +func (s *BotLoggerSuite) TestLoadBotContainersError() { + botLogger := NewBotLogger( + s.botClient, s.dockerClient, s.key, + func(agents agentlogs.Agents, authToken string) error { + return nil + }, + ) + ctx := context.Background() + + mockContainers := []types.Container{} + + s.botClient.EXPECT().LoadBotContainers(ctx).Return(mockContainers, errors.New("test")) + s.r.Error(botLogger.SendBotLogs(ctx), "failed to load the bot containers: test") +} + +// Should not send agent logs if fails +// to get container logs but continue processing +func (s *BotLoggerSuite) TestGetContainerLogsError() { + botLogger := NewBotLogger( + s.botClient, s.dockerClient, s.key, + func(agents agentlogs.Agents, authToken string) error { + s.r.Equal(1, len(agents)) + s.r.Equal("bot2ID", agents[0].ID) + s.r.Equal("some log", agents[0].Logs) + return nil + }, + ) + ctx := context.Background() + + mockContainers := []types.Container{ + { + ID: "bot1", + Image: "forta/bot:latest", + Labels: map[string]string{ + docker.LabelFortaSettingsAgentLogsEnable: "true", + }, + }, + { + ID: "bot2", + Image: "forta/bot:latest", + Labels: map[string]string{ + docker.LabelFortaSettingsAgentLogsEnable: "true", + docker.LabelFortaBotID: "bot2ID", + }, + }, + } + + s.botClient.EXPECT().LoadBotContainers(ctx).Return(mockContainers, nil) + + s.dockerClient.EXPECT().GetContainerLogs( + ctx, "bot1", + strconv.Itoa(defaultAgentLogTailLines), + defaultAgentLogAvgMaxCharsPerLine*defaultAgentLogTailLines, + ).Return("", errors.New("test")).Times(1) + + s.dockerClient.EXPECT().GetContainerLogs( + ctx, "bot2", + strconv.Itoa(defaultAgentLogTailLines), + defaultAgentLogAvgMaxCharsPerLine*defaultAgentLogTailLines, + ).Return("some log", nil).Times(1) botLogger.SendBotLogs(ctx) } + +// Fails sending agent logs +func (s *BotLoggerSuite) TestFailsToSendLogs() { + botLogger := NewBotLogger( + s.botClient, s.dockerClient, s.key, + func(agents agentlogs.Agents, authToken string) error { + return errors.New("test") + }, + ) + ctx := context.Background() + + mockContainers := []types.Container{ + { + ID: "bot1", + Image: "forta/bot:latest", + Labels: map[string]string{ + docker.LabelFortaSettingsAgentLogsEnable: "true", + docker.LabelFortaBotID: "bot1ID", + }, + }, + } + + s.botClient.EXPECT().LoadBotContainers(ctx).Return(mockContainers, nil) + + s.dockerClient.EXPECT().GetContainerLogs( + ctx, "bot1", + strconv.Itoa(defaultAgentLogTailLines), + defaultAgentLogAvgMaxCharsPerLine*defaultAgentLogTailLines, + ).Return("some log", nil).Times(1) + + s.r.Error(botLogger.SendBotLogs(ctx), "failed to send agent logs: test") +} From 6db683752666e790b25103a85bcaffa88f021cb4 Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 20 Sep 2023 20:12:38 -0300 Subject: [PATCH 8/8] tests improvements --- services/components/lifecycle/bot_logger_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/components/lifecycle/bot_logger_test.go b/services/components/lifecycle/bot_logger_test.go index 778622cb..55e84946 100644 --- a/services/components/lifecycle/bot_logger_test.go +++ b/services/components/lifecycle/bot_logger_test.go @@ -98,7 +98,7 @@ func (s *BotLoggerSuite) TestSendBotLogs() { ).Return("some log", nil).Times(1) s.botClient.EXPECT().LoadBotContainers(ctx).Return(mockContainers, nil) - botLogger.SendBotLogs(ctx) + s.r.NoError(botLogger.SendBotLogs(ctx)) } // should fail if there is an error loading @@ -115,7 +115,7 @@ func (s *BotLoggerSuite) TestLoadBotContainersError() { mockContainers := []types.Container{} s.botClient.EXPECT().LoadBotContainers(ctx).Return(mockContainers, errors.New("test")) - s.r.Error(botLogger.SendBotLogs(ctx), "failed to load the bot containers: test") + s.r.EqualError(botLogger.SendBotLogs(ctx), "failed to load the bot containers: test") } // Should not send agent logs if fails @@ -164,7 +164,7 @@ func (s *BotLoggerSuite) TestGetContainerLogsError() { defaultAgentLogAvgMaxCharsPerLine*defaultAgentLogTailLines, ).Return("some log", nil).Times(1) - botLogger.SendBotLogs(ctx) + s.r.NoError(botLogger.SendBotLogs(ctx)) } // Fails sending agent logs @@ -196,5 +196,5 @@ func (s *BotLoggerSuite) TestFailsToSendLogs() { defaultAgentLogAvgMaxCharsPerLine*defaultAgentLogTailLines, ).Return("some log", nil).Times(1) - s.r.Error(botLogger.SendBotLogs(ctx), "failed to send agent logs: test") + s.r.EqualError(botLogger.SendBotLogs(ctx), "failed to send agent logs: test") }