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

Include full plugin name in reported command #1295

Merged
merged 5 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
26 changes: 17 additions & 9 deletions cmd/botkube-agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import (
"github.com/kubeshop/botkube/pkg/config"
"github.com/kubeshop/botkube/pkg/controller"
"github.com/kubeshop/botkube/pkg/execute"
"github.com/kubeshop/botkube/pkg/maputil"
"github.com/kubeshop/botkube/pkg/multierror"
"github.com/kubeshop/botkube/pkg/notifier"
"github.com/kubeshop/botkube/pkg/sink"
Expand Down Expand Up @@ -239,8 +240,15 @@ func run(ctx context.Context) (err error) {
// For example, if in both communication groups there's a Slack configuration pointing to the same workspace,
// when user executes `kubectl` command, one Bot instance will execute the command and return response,
// and the second "Sorry, this channel is not authorized to execute kubectl command" error.
for commGroupName, commGroupCfg := range conf.Communications {
commKeys := maputil.SortKeys(conf.Communications)
for commGroupIdx, commGroupName := range commKeys {
commGroupCfg := conf.Communications[commGroupName]

commGroupLogger := logger.WithField(commGroupFieldKey, commGroupName)
commGroupMeta := bot.CommGroupMetadata{
Name: commGroupName,
Index: commGroupIdx + 1,
}

scheduleBotNotifier := func(in bot.Bot) {
bots[fmt.Sprintf("%s-%s", commGroupName, in.IntegrationName())] = in
Expand All @@ -252,47 +260,47 @@ func run(ctx context.Context) (err error) {

// Run bots
if commGroupCfg.Slack.Enabled {
sb, err := bot.NewSlack(commGroupLogger.WithField(botLogFieldKey, "Slack"), commGroupName, commGroupCfg.Slack, executorFactory, reporter)
sb, err := bot.NewSlack(commGroupLogger.WithField(botLogFieldKey, "Slack"), commGroupMeta, commGroupCfg.Slack, executorFactory, reporter)
if err != nil {
return reportFatalError("while creating Slack bot", err)
}
scheduleBotNotifier(sb)
}

if commGroupCfg.SocketSlack.Enabled {
sb, err := bot.NewSocketSlack(commGroupLogger.WithField(botLogFieldKey, "SocketSlack"), commGroupName, commGroupCfg.SocketSlack, executorFactory, reporter)
sb, err := bot.NewSocketSlack(commGroupLogger.WithField(botLogFieldKey, "SocketSlack"), commGroupMeta, commGroupCfg.SocketSlack, executorFactory, reporter)
if err != nil {
return reportFatalError("while creating SocketSlack bot", err)
}
scheduleBotNotifier(sb)
}

if commGroupCfg.CloudSlack.Enabled {
sb, err := bot.NewCloudSlack(commGroupLogger.WithField(botLogFieldKey, "CloudSlack"), commGroupName, commGroupCfg.CloudSlack, conf.Settings.ClusterName, executorFactory, reporter)
sb, err := bot.NewCloudSlack(commGroupLogger.WithField(botLogFieldKey, "CloudSlack"), commGroupMeta, commGroupCfg.CloudSlack, conf.Settings.ClusterName, executorFactory, reporter)
if err != nil {
return reportFatalError("while creating CloudSlack bot", err)
}
scheduleBotNotifier(sb)
}

if commGroupCfg.Mattermost.Enabled {
mb, err := bot.NewMattermost(ctx, commGroupLogger.WithField(botLogFieldKey, "Mattermost"), commGroupName, commGroupCfg.Mattermost, executorFactory, reporter)
mb, err := bot.NewMattermost(ctx, commGroupLogger.WithField(botLogFieldKey, "Mattermost"), commGroupMeta, commGroupCfg.Mattermost, executorFactory, reporter)
if err != nil {
return reportFatalError("while creating Mattermost bot", err)
}
scheduleBotNotifier(mb)
}

if commGroupCfg.Teams.Enabled {
tb, err := bot.NewTeams(commGroupLogger.WithField(botLogFieldKey, "MS Teams"), commGroupName, commGroupCfg.Teams, conf.Settings.ClusterName, executorFactory, reporter)
tb, err := bot.NewTeams(commGroupLogger.WithField(botLogFieldKey, "MS Teams"), commGroupMeta, commGroupCfg.Teams, conf.Settings.ClusterName, executorFactory, reporter)
if err != nil {
return reportFatalError("while creating Teams bot", err)
}
scheduleBotNotifier(tb)
}

if commGroupCfg.Discord.Enabled {
db, err := bot.NewDiscord(commGroupLogger.WithField(botLogFieldKey, "Discord"), commGroupName, commGroupCfg.Discord, executorFactory, reporter)
db, err := bot.NewDiscord(commGroupLogger.WithField(botLogFieldKey, "Discord"), commGroupMeta, commGroupCfg.Discord, executorFactory, reporter)
if err != nil {
return reportFatalError("while creating Discord bot", err)
}
Expand All @@ -301,15 +309,15 @@ func run(ctx context.Context) (err error) {

// Run sinks
if commGroupCfg.Elasticsearch.Enabled {
es, err := sink.NewElasticsearch(commGroupLogger.WithField(sinkLogFieldKey, "Elasticsearch"), commGroupCfg.Elasticsearch, reporter)
es, err := sink.NewElasticsearch(commGroupLogger.WithField(sinkLogFieldKey, "Elasticsearch"), commGroupMeta.Index, commGroupCfg.Elasticsearch, reporter)
if err != nil {
return reportFatalError("while creating Elasticsearch sink", err)
}
sinkNotifiers = append(sinkNotifiers, es)
}

if commGroupCfg.Webhook.Enabled {
wh, err := sink.NewWebhook(commGroupLogger.WithField(sinkLogFieldKey, "Webhook"), commGroupCfg.Webhook, reporter)
wh, err := sink.NewWebhook(commGroupLogger.WithField(sinkLogFieldKey, "Webhook"), commGroupMeta.Index, commGroupCfg.Webhook, reporter)
if err != nil {
return reportFatalError("while creating Webhook sink", err)
}
Expand Down
13 changes: 6 additions & 7 deletions internal/analytics/noop_reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"k8s.io/client-go/kubernetes"

"github.com/kubeshop/botkube/pkg/config"
"github.com/kubeshop/botkube/pkg/execute/command"
)

var _ Reporter = &NoopReporter{}
Expand All @@ -24,28 +23,28 @@ func (n NoopReporter) RegisterCurrentIdentity(_ context.Context, _ kubernetes.In
return nil
}

// ReportCommand reports a new executed command. The command should be anonymized before using this method.
func (n NoopReporter) ReportCommand(_ config.CommPlatformIntegration, _ string, _ command.Origin, _ bool) error {
// ReportCommandInput reports a new executed command. The command should be anonymized before using this method.
func (n NoopReporter) ReportCommand(_ ReportCommandInput) error {
return nil
}

// ReportBotEnabled reports an enabled bot.
func (n NoopReporter) ReportBotEnabled(_ config.CommPlatformIntegration) error {
func (n NoopReporter) ReportBotEnabled(_ config.CommPlatformIntegration, _ int) error {
return nil
}

// ReportSinkEnabled reports an enabled sink.
func (n NoopReporter) ReportSinkEnabled(_ config.CommPlatformIntegration) error {
func (n NoopReporter) ReportSinkEnabled(_ config.CommPlatformIntegration, _ int) error {
return nil
}

// ReportHandledEventSuccess reports a successfully handled event using a given communication platform.
func (n NoopReporter) ReportHandledEventSuccess(_ ReportEvent) error {
func (n NoopReporter) ReportHandledEventSuccess(_ ReportEventInput) error {
return nil
}

// ReportHandledEventError reports a failure while handling event using a given communication platform.
func (n NoopReporter) ReportHandledEventError(_ ReportEvent, _ error) error {
func (n NoopReporter) ReportHandledEventError(_ ReportEventInput, _ error) error {
return nil
}

Expand Down
22 changes: 15 additions & 7 deletions internal/analytics/reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ type Reporter interface {
// RegisterCurrentIdentity loads the current anonymous identity and registers it.
RegisterCurrentIdentity(ctx context.Context, k8sCli kubernetes.Interface, deployID string) error

// ReportCommand reports a new executed command. The command should be anonymized before using this method.
ReportCommand(platform config.CommPlatformIntegration, command string, origin command.Origin, withFilter bool) error
// ReportCommandInput reports a new executed command. The command should be anonymized before using this method.
pkosiec marked this conversation as resolved.
Show resolved Hide resolved
ReportCommand(in ReportCommandInput) error

// ReportBotEnabled reports an enabled bot.
ReportBotEnabled(platform config.CommPlatformIntegration) error
ReportBotEnabled(platform config.CommPlatformIntegration, commGroupIdx int) error

// ReportSinkEnabled reports an enabled sink.
ReportSinkEnabled(platform config.CommPlatformIntegration) error
ReportSinkEnabled(platform config.CommPlatformIntegration, commGroupIdx int) error

// ReportHandledEventSuccess reports a successfully handled event using a given integration type, communication platform, and plugin.
ReportHandledEventSuccess(event ReportEvent) error
ReportHandledEventSuccess(event ReportEventInput) error

// ReportHandledEventError reports a failure while handling event using a given integration type, communication platform, and plugin.
ReportHandledEventError(event ReportEvent, err error) error
ReportHandledEventError(event ReportEventInput, err error) error

// ReportFatalError reports a fatal app error.
ReportFatalError(err error) error
Expand All @@ -36,9 +36,17 @@ type Reporter interface {
Close() error
}

type ReportEvent struct {
type ReportEventInput struct {
IntegrationType config.IntegrationType
Platform config.CommPlatformIntegration
PluginName string
AnonymizedEventFields map[string]any
}

type ReportCommandInput struct {
Platform config.CommPlatformIntegration
PluginName string
Command string
Origin command.Origin
WithFilter bool
}
32 changes: 17 additions & 15 deletions internal/analytics/segment_reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"k8s.io/client-go/kubernetes"

"github.com/kubeshop/botkube/pkg/config"
"github.com/kubeshop/botkube/pkg/execute/command"
"github.com/kubeshop/botkube/pkg/version"
)

Expand Down Expand Up @@ -67,38 +66,41 @@ func (r *SegmentReporter) RegisterCurrentIdentity(ctx context.Context, k8sCli ku
return nil
}

// ReportCommand reports a new executed command. The command should be anonymized before using this method.
// ReportCommandInput reports a new executed command. The command should be anonymized before using this method.
// The RegisterCurrentIdentity needs to be called first.
func (r *SegmentReporter) ReportCommand(platform config.CommPlatformIntegration, command string, origin command.Origin, withFilter bool) error {
func (r *SegmentReporter) ReportCommand(in ReportCommandInput) error {
return r.reportEvent("Command executed", map[string]interface{}{
"platform": platform,
"command": command,
"origin": origin,
"filtered": withFilter,
"platform": in.Platform,
"command": in.Command,
"plugin": in.PluginName,
"origin": in.Origin,
"filtered": in.WithFilter,
})
}

// ReportBotEnabled reports an enabled bot.
// The RegisterCurrentIdentity needs to be called first.
func (r *SegmentReporter) ReportBotEnabled(platform config.CommPlatformIntegration) error {
func (r *SegmentReporter) ReportBotEnabled(platform config.CommPlatformIntegration, commGroupIdx int) error {
return r.reportEvent("Integration enabled", map[string]interface{}{
"platform": platform,
"type": config.BotIntegrationType,
"platform": platform,
"type": config.BotIntegrationType,
"communicationGroupID": commGroupIdx,
})
}

// ReportSinkEnabled reports an enabled sink.
// The RegisterCurrentIdentity needs to be called first.
func (r *SegmentReporter) ReportSinkEnabled(platform config.CommPlatformIntegration) error {
func (r *SegmentReporter) ReportSinkEnabled(platform config.CommPlatformIntegration, commGroupIdx int) error {
return r.reportEvent("Integration enabled", map[string]interface{}{
"platform": platform,
"type": config.SinkIntegrationType,
"platform": platform,
"type": config.SinkIntegrationType,
"communicationGroupID": commGroupIdx,
})
}

// ReportHandledEventSuccess reports a successfully handled event using a given communication platform.
// The RegisterCurrentIdentity needs to be called first.
func (r *SegmentReporter) ReportHandledEventSuccess(event ReportEvent) error {
func (r *SegmentReporter) ReportHandledEventSuccess(event ReportEventInput) error {
return r.reportEvent("Event handled", map[string]interface{}{
"platform": event.Platform,
"type": event.IntegrationType,
Expand All @@ -110,7 +112,7 @@ func (r *SegmentReporter) ReportHandledEventSuccess(event ReportEvent) error {

// ReportHandledEventError reports a failure while handling event using a given communication platform.
// The RegisterCurrentIdentity needs to be called first.
func (r *SegmentReporter) ReportHandledEventError(event ReportEvent, err error) error {
func (r *SegmentReporter) ReportHandledEventError(event ReportEventInput, err error) error {
if err == nil {
return nil
}
Expand Down
38 changes: 26 additions & 12 deletions internal/analytics/segment_reporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,27 @@ func TestSegmentReporter_ReportCommand(t *testing.T) {
segmentReporter, segmentCli := fakeSegmentReporterWithIdentity(identity)

// when
err := segmentReporter.ReportCommand(config.DiscordCommPlatformIntegration, "enable notifications", command.TypedOrigin, false)
err := segmentReporter.ReportCommand(analytics.ReportCommandInput{
Platform: config.DiscordCommPlatformIntegration,
Command: "enable notifications",
Origin: command.TypedOrigin,
})
require.NoError(t, err)

err = segmentReporter.ReportCommand(config.SlackCommPlatformIntegration, "get", command.ButtonClickOrigin, false)
err = segmentReporter.ReportCommand(analytics.ReportCommandInput{
Platform: config.SlackCommPlatformIntegration,
PluginName: "botkube/kubectl",
Command: "get",
Origin: command.ButtonClickOrigin,
WithFilter: true,
})
require.NoError(t, err)

err = segmentReporter.ReportCommand(config.TeamsCommPlatformIntegration, "disable notifications", command.SelectValueChangeOrigin, false)
err = segmentReporter.ReportCommand(analytics.ReportCommandInput{
Platform: config.TeamsCommPlatformIntegration,
Command: "disable notifications",
Origin: command.SelectValueChangeOrigin,
})
require.NoError(t, err)

// then
Expand All @@ -120,15 +134,15 @@ func TestSegmentReporter_ReportBotEnabled(t *testing.T) {
segmentReporter, segmentCli := fakeSegmentReporterWithIdentity(identity)

// when
err := segmentReporter.ReportBotEnabled(config.SlackCommPlatformIntegration)
err := segmentReporter.ReportBotEnabled(config.SlackCommPlatformIntegration, 1)
require.NoError(t, err)

// when
err = segmentReporter.ReportBotEnabled(config.DiscordCommPlatformIntegration)
err = segmentReporter.ReportBotEnabled(config.DiscordCommPlatformIntegration, 2)
require.NoError(t, err)

// when
err = segmentReporter.ReportBotEnabled(config.TeamsCommPlatformIntegration)
err = segmentReporter.ReportBotEnabled(config.TeamsCommPlatformIntegration, 1)
require.NoError(t, err)

// then
Expand All @@ -141,11 +155,11 @@ func TestSegmentReporter_ReportSinkEnabled(t *testing.T) {
segmentReporter, segmentCli := fakeSegmentReporterWithIdentity(identity)

// when
err := segmentReporter.ReportSinkEnabled(config.WebhookCommPlatformIntegration)
err := segmentReporter.ReportSinkEnabled(config.WebhookCommPlatformIntegration, 1)
require.NoError(t, err)

// when
err = segmentReporter.ReportSinkEnabled(config.ElasticsearchCommPlatformIntegration)
err = segmentReporter.ReportSinkEnabled(config.ElasticsearchCommPlatformIntegration, 2)
require.NoError(t, err)

// then
Expand All @@ -163,15 +177,15 @@ func TestSegmentReporter_ReportHandledEventSuccess(t *testing.T) {
}

// when
err := segmentReporter.ReportHandledEventSuccess(analytics.ReportEvent{
err := segmentReporter.ReportHandledEventSuccess(analytics.ReportEventInput{
IntegrationType: config.BotIntegrationType,
Platform: config.SlackCommPlatformIntegration,
PluginName: "botkube/kubernetes",
AnonymizedEventFields: eventDetails,
})
require.NoError(t, err)

err = segmentReporter.ReportHandledEventSuccess(analytics.ReportEvent{
err = segmentReporter.ReportHandledEventSuccess(analytics.ReportEventInput{
IntegrationType: config.SinkIntegrationType,
Platform: config.ElasticsearchCommPlatformIntegration,
PluginName: "botkube/kubernetes",
Expand All @@ -195,15 +209,15 @@ func TestSegmentReporter_ReportHandledEventError(t *testing.T) {
sampleErr := errors.New("sample error")

// when
err := segmentReporter.ReportHandledEventError(analytics.ReportEvent{
err := segmentReporter.ReportHandledEventError(analytics.ReportEventInput{
IntegrationType: config.BotIntegrationType,
Platform: config.SlackCommPlatformIntegration,
PluginName: "botkube/kubernetes",
AnonymizedEventFields: eventDetails,
}, sampleErr)
require.NoError(t, err)

err = segmentReporter.ReportHandledEventError(analytics.ReportEvent{
err = segmentReporter.ReportHandledEventError(analytics.ReportEventInput{
IntegrationType: config.SinkIntegrationType,
Platform: config.ElasticsearchCommPlatformIntegration,
PluginName: "botkube/kubernetes",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"event": "Integration enabled",
"timestamp": "2009-11-17T20:34:58.651387237Z",
"properties": {
"communicationGroupID": 1,
"platform": "slack",
"type": "bot"
}
Expand All @@ -17,6 +18,7 @@
"event": "Integration enabled",
"timestamp": "2009-11-17T20:34:58.651387237Z",
"properties": {
"communicationGroupID": 2,
"platform": "discord",
"type": "bot"
}
Expand All @@ -28,6 +30,7 @@
"event": "Integration enabled",
"timestamp": "2009-11-17T20:34:58.651387237Z",
"properties": {
"communicationGroupID": 1,
"platform": "teams",
"type": "bot"
}
Expand Down
Loading
Loading