Skip to content

Commit

Permalink
Persist sent help messages (#738)
Browse files Browse the repository at this point in the history
  • Loading branch information
mszostok authored Sep 20, 2022
1 parent 50223db commit 14dc934
Show file tree
Hide file tree
Showing 12 changed files with 277 additions and 83 deletions.
68 changes: 32 additions & 36 deletions cmd/botkube/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/manager/signals"

"github.com/kubeshop/botkube/internal/analytics"
"github.com/kubeshop/botkube/internal/storage"
"github.com/kubeshop/botkube/pkg/bot"
"github.com/kubeshop/botkube/pkg/bot/interactive"
"github.com/kubeshop/botkube/pkg/config"
Expand Down Expand Up @@ -159,7 +160,7 @@ func run() error {
commCfg := conf.Communications
var (
notifiers []controller.Notifier
bots []bot.Bot
bots = map[string]bot.Bot{}
)

// TODO: Current limitation: Communication platform config should be separate inside every group:
Expand All @@ -169,66 +170,48 @@ func run() error {
for commGroupName, commGroupCfg := range commCfg {
commGroupLogger := logger.WithField(commGroupFieldKey, commGroupName)

router.AddAnyBindingsByName(commGroupCfg.Slack.Channels)
router.AddAnyBindingsByName(commGroupCfg.Mattermost.Channels)
router.AddAnyBindings(commGroupCfg.Teams.Bindings)
router.AddAnyBindingsByID(commGroupCfg.Discord.Channels)
for _, index := range commGroupCfg.Elasticsearch.Indices {
router.AddAnySinkBindings(index.Bindings)
router.AddCommunicationsBindings(commGroupCfg)

scheduleBot := func(in bot.Bot) {
notifiers = append(notifiers, in)
bots[fmt.Sprintf("%s-%s", commGroupName, in.IntegrationName())] = in
errGroup.Go(func() error {
defer analytics.ReportPanicIfOccurs(commGroupLogger, reporter)
return in.Start(ctx)
})
}
router.AddAnySinkBindings(commGroupCfg.Webhook.Bindings)

// Run bots
if commGroupCfg.Slack.Enabled {
sb, err := bot.NewSlack(commGroupLogger.WithField(botLogFieldKey, "Slack"), commGroupName, commGroupCfg.Slack, executorFactory, reporter)
if err != nil {
return reportFatalError("while creating Slack bot", err)
}
notifiers = append(notifiers, sb)
bots = append(bots, sb)
errGroup.Go(func() error {
defer analytics.ReportPanicIfOccurs(commGroupLogger, reporter)
return sb.Start(ctx)
})
scheduleBot(sb)
}

if commGroupCfg.Mattermost.Enabled {
mb, err := bot.NewMattermost(commGroupLogger.WithField(botLogFieldKey, "Mattermost"), commGroupName, commGroupCfg.Mattermost, executorFactory, reporter)
if err != nil {
return reportFatalError("while creating Mattermost bot", err)
}
notifiers = append(notifiers, mb)
bots = append(bots, mb)
errGroup.Go(func() error {
defer analytics.ReportPanicIfOccurs(commGroupLogger, reporter)
return mb.Start(ctx)
})
scheduleBot(mb)
}

if commGroupCfg.Teams.Enabled {
tb, err := bot.NewTeams(commGroupLogger.WithField(botLogFieldKey, "MS Teams"), commGroupName, commGroupCfg.Teams, conf.Settings.ClusterName, executorFactory, reporter)
if err != nil {
return reportFatalError("while creating Teams bot", err)
}
notifiers = append(notifiers, tb)
bots = append(bots, tb)
errGroup.Go(func() error {
defer analytics.ReportPanicIfOccurs(commGroupLogger, reporter)
return tb.Start(ctx)
})
scheduleBot(tb)
}

if commGroupCfg.Discord.Enabled {
db, err := bot.NewDiscord(commGroupLogger.WithField(botLogFieldKey, "Discord"), commGroupName, commGroupCfg.Discord, executorFactory, reporter)
if err != nil {
return reportFatalError("while creating Discord bot", err)
}
notifiers = append(notifiers, db)
bots = append(bots, db)
errGroup.Go(func() error {
defer analytics.ReportPanicIfOccurs(commGroupLogger, reporter)
return db.Start(ctx)
})
scheduleBot(db)
}

// Run sinks
Expand All @@ -251,7 +234,8 @@ func run() error {
}

// Send help message
err = sendHelp(ctx, conf.Settings.ClusterName, bots)
helpDB := storage.NewForHelp(conf.Settings.SystemConfigMap.Namespace, conf.Settings.SystemConfigMap.Name, k8sCli)
err = sendHelp(ctx, helpDB, conf.Settings.ClusterName, bots)
if err != nil {
return fmt.Errorf("while sending initial help message: %w", err)
}
Expand Down Expand Up @@ -397,14 +381,26 @@ func reportFatalErrFn(logger logrus.FieldLogger, reporter analytics.Reporter) fu
}

// sendHelp sends the help message to all interactive bots.
func sendHelp(ctx context.Context, clusterName string, notifiers []bot.Bot) error {
for _, notifier := range notifiers {
func sendHelp(ctx context.Context, s *storage.Help, clusterName string, notifiers map[string]bot.Bot) error {
alreadySentHelp, err := s.GetSentHelpDetails(ctx)
if err != nil {
return fmt.Errorf("while getting the help data: %w", err)
}

var sent []string

for key, notifier := range notifiers {
if alreadySentHelp[key] {
continue
}

help := interactive.Help(notifier.IntegrationName(), clusterName, notifier.BotName())
err := notifier.SendMessage(ctx, help)
if err != nil {
return fmt.Errorf("while sending help message for %s: %w", notifier.IntegrationName(), err)
}
sent = append(sent, key)
}

return nil
return s.MarkHelpAsSent(ctx, sent)
}
83 changes: 42 additions & 41 deletions helm/botkube/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,47 +111,48 @@ Controller for the BotKube Slack app which helps you monitor your Kubernetes clu
| [settings.upgradeNotifier](./values.yaml#L458) | bool | `true` | If true, notifies about new BotKube releases. |
| [settings.log.level](./values.yaml#L462) | string | `"info"` | Sets one of the log levels. Allowed values: `info`, `warn`, `debug`, `error`, `fatal`, `panic`. |
| [settings.log.disableColors](./values.yaml#L464) | bool | `false` | If true, disable ANSI colors in logging. |
| [ssl.enabled](./values.yaml#L469) | bool | `false` | If true, specify cert path in `config.ssl.cert` property or K8s Secret in `config.ssl.existingSecretName`. |
| [ssl.existingSecretName](./values.yaml#L475) | string | `""` | Using existing SSL Secret. It MUST be in `botkube` Namespace. |
| [ssl.cert](./values.yaml#L478) | string | `""` | SSL Certificate file e.g certs/my-cert.crt. |
| [service](./values.yaml#L481) | object | `{"name":"metrics","port":2112,"targetPort":2112}` | Configures Service settings for ServiceMonitor CR. |
| [ingress](./values.yaml#L488) | object | `{"annotations":{"kubernetes.io/ingress.class":"nginx"},"create":false,"host":"HOST","tls":{"enabled":false,"secretName":""}}` | Configures Ingress settings that exposes MS Teams endpoint. [Ref doc](https://kubernetes.io/docs/concepts/services-networking/ingress/#the-ingress-resource). |
| [serviceMonitor](./values.yaml#L499) | object | `{"enabled":false,"interval":"10s","labels":{},"path":"/metrics","port":"metrics"}` | Configures ServiceMonitor settings. [Ref doc](https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#servicemonitor). |
| [deployment.annotations](./values.yaml#L509) | object | `{}` | Extra annotations to pass to the BotKube Deployment. |
| [extraAnnotations](./values.yaml#L516) | object | `{}` | Extra annotations to pass to the BotKube Pod. |
| [extraLabels](./values.yaml#L518) | object | `{}` | Extra labels to pass to the BotKube Pod. |
| [priorityClassName](./values.yaml#L520) | string | `""` | Priority class name for the BotKube Pod. |
| [nameOverride](./values.yaml#L523) | string | `""` | Fully override "botkube.name" template. |
| [fullnameOverride](./values.yaml#L525) | string | `""` | Fully override "botkube.fullname" template. |
| [resources](./values.yaml#L531) | object | `{}` | The BotKube Pod resource request and limits. We usually recommend not to specify default resources and to leave this as a conscious choice for the user. This also increases chances charts run on environments with little resources, such as Minikube. [Ref docs](https://kubernetes.io/docs/user-guide/compute-resources/) |
| [extraEnv](./values.yaml#L543) | list | `[]` | Extra environment variables to pass to the BotKube container. [Ref docs](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#environment-variables). |
| [extraVolumes](./values.yaml#L555) | list | `[]` | Extra volumes to pass to the BotKube container. Mount it later with extraVolumeMounts. [Ref docs](https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/volume/#Volume). |
| [extraVolumeMounts](./values.yaml#L570) | list | `[]` | Extra volume mounts to pass to the BotKube container. [Ref docs](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#volumes-1). |
| [nodeSelector](./values.yaml#L588) | object | `{}` | Node labels for BotKube Pod assignment. [Ref doc](https://kubernetes.io/docs/user-guide/node-selection/). |
| [tolerations](./values.yaml#L592) | list | `[]` | Tolerations for BotKube Pod assignment. [Ref doc](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/). |
| [affinity](./values.yaml#L596) | object | `{}` | Affinity for BotKube Pod assignment. [Ref doc](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity). |
| [rbac](./values.yaml#L600) | object | `{"create":true,"rules":[{"apiGroups":["*"],"resources":["*"],"verbs":["get","watch","list"]}]}` | Role Based Access for BotKube Pod. [Ref doc](https://kubernetes.io/docs/admin/authorization/rbac/). |
| [serviceAccount.create](./values.yaml#L609) | bool | `true` | If true, a ServiceAccount is automatically created. |
| [serviceAccount.name](./values.yaml#L612) | string | `""` | The name of the service account to use. If not set, a name is generated using the fullname template. |
| [serviceAccount.annotations](./values.yaml#L614) | object | `{}` | Extra annotations for the ServiceAccount. |
| [extraObjects](./values.yaml#L617) | list | `[]` | Extra Kubernetes resources to create. Helm templating is allowed as it is evaluated before creating the resources. |
| [analytics.disable](./values.yaml#L645) | bool | `false` | If true, sending anonymous analytics is disabled. To learn what date we collect, see [Privacy Policy](https://botkube.io/privacy#privacy-policy). |
| [e2eTest.image.registry](./values.yaml#L651) | string | `"ghcr.io"` | Test runner image registry. |
| [e2eTest.image.repository](./values.yaml#L653) | string | `"kubeshop/botkube-test"` | Test runner image repository. |
| [e2eTest.image.pullPolicy](./values.yaml#L655) | string | `"IfNotPresent"` | Test runner image pull policy. |
| [e2eTest.image.tag](./values.yaml#L657) | string | `"v9.99.9-dev"` | Test runner image tag. Default tag is `appVersion` from Chart.yaml. |
| [e2eTest.deployment](./values.yaml#L659) | object | `{"waitTimeout":"3m"}` | Configures BotKube Deployment related data. |
| [e2eTest.slack.botName](./values.yaml#L664) | string | `"botkube"` | Name of the BotKube bot to interact with during the e2e tests. |
| [e2eTest.slack.testerName](./values.yaml#L666) | string | `"botkube_tester"` | Name of the BotKube Tester bot that sends messages during the e2e tests. |
| [e2eTest.slack.testerAppToken](./values.yaml#L668) | string | `""` | Slack tester application token that interacts with BotKube bot. |
| [e2eTest.slack.additionalContextMessage](./values.yaml#L670) | string | `""` | Additional message that is sent by Tester. You can pass e.g. pull request number or source link where these tests are run from. |
| [e2eTest.slack.messageWaitTimeout](./values.yaml#L672) | string | `"1m"` | Message wait timeout. It defines how long we wait to ensure that notification were not sent when disabled. |
| [e2eTest.discord.botName](./values.yaml#L675) | string | `"botkube"` | Name of the BotKube bot to interact with during the e2e tests. |
| [e2eTest.discord.testerName](./values.yaml#L677) | string | `"botkube_tester"` | Name of the BotKube Tester bot that sends messages during the e2e tests. |
| [e2eTest.discord.guildID](./values.yaml#L679) | string | `""` | Discord Guild ID (discord server ID) used to run e2e tests |
| [e2eTest.discord.testerAppToken](./values.yaml#L681) | string | `""` | Discord tester application token that interacts with BotKube bot. |
| [e2eTest.discord.additionalContextMessage](./values.yaml#L683) | string | `""` | Additional message that is sent by Tester. You can pass e.g. pull request number or source link where these tests are run from. |
| [e2eTest.discord.messageWaitTimeout](./values.yaml#L685) | string | `"1m"` | Message wait timeout. It defines how long we wait to ensure that notification were not sent when disabled. |
| [settings.systemConfigMap](./values.yaml#L467) | object | `{"name":"botkube-system"}` | BotKube's system ConfigMap where internal data is stored. |
| [ssl.enabled](./values.yaml#L473) | bool | `false` | If true, specify cert path in `config.ssl.cert` property or K8s Secret in `config.ssl.existingSecretName`. |
| [ssl.existingSecretName](./values.yaml#L479) | string | `""` | Using existing SSL Secret. It MUST be in `botkube` Namespace. |
| [ssl.cert](./values.yaml#L482) | string | `""` | SSL Certificate file e.g certs/my-cert.crt. |
| [service](./values.yaml#L485) | object | `{"name":"metrics","port":2112,"targetPort":2112}` | Configures Service settings for ServiceMonitor CR. |
| [ingress](./values.yaml#L492) | object | `{"annotations":{"kubernetes.io/ingress.class":"nginx"},"create":false,"host":"HOST","tls":{"enabled":false,"secretName":""}}` | Configures Ingress settings that exposes MS Teams endpoint. [Ref doc](https://kubernetes.io/docs/concepts/services-networking/ingress/#the-ingress-resource). |
| [serviceMonitor](./values.yaml#L503) | object | `{"enabled":false,"interval":"10s","labels":{},"path":"/metrics","port":"metrics"}` | Configures ServiceMonitor settings. [Ref doc](https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#servicemonitor). |
| [deployment.annotations](./values.yaml#L513) | object | `{}` | Extra annotations to pass to the BotKube Deployment. |
| [extraAnnotations](./values.yaml#L520) | object | `{}` | Extra annotations to pass to the BotKube Pod. |
| [extraLabels](./values.yaml#L522) | object | `{}` | Extra labels to pass to the BotKube Pod. |
| [priorityClassName](./values.yaml#L524) | string | `""` | Priority class name for the BotKube Pod. |
| [nameOverride](./values.yaml#L527) | string | `""` | Fully override "botkube.name" template. |
| [fullnameOverride](./values.yaml#L529) | string | `""` | Fully override "botkube.fullname" template. |
| [resources](./values.yaml#L535) | object | `{}` | The BotKube Pod resource request and limits. We usually recommend not to specify default resources and to leave this as a conscious choice for the user. This also increases chances charts run on environments with little resources, such as Minikube. [Ref docs](https://kubernetes.io/docs/user-guide/compute-resources/) |
| [extraEnv](./values.yaml#L547) | list | `[]` | Extra environment variables to pass to the BotKube container. [Ref docs](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#environment-variables). |
| [extraVolumes](./values.yaml#L559) | list | `[]` | Extra volumes to pass to the BotKube container. Mount it later with extraVolumeMounts. [Ref docs](https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/volume/#Volume). |
| [extraVolumeMounts](./values.yaml#L574) | list | `[]` | Extra volume mounts to pass to the BotKube container. [Ref docs](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#volumes-1). |
| [nodeSelector](./values.yaml#L592) | object | `{}` | Node labels for BotKube Pod assignment. [Ref doc](https://kubernetes.io/docs/user-guide/node-selection/). |
| [tolerations](./values.yaml#L596) | list | `[]` | Tolerations for BotKube Pod assignment. [Ref doc](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/). |
| [affinity](./values.yaml#L600) | object | `{}` | Affinity for BotKube Pod assignment. [Ref doc](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity). |
| [rbac](./values.yaml#L604) | object | `{"create":true,"rules":[{"apiGroups":["*"],"resources":["*"],"verbs":["get","watch","list"]}]}` | Role Based Access for BotKube Pod. [Ref doc](https://kubernetes.io/docs/admin/authorization/rbac/). |
| [serviceAccount.create](./values.yaml#L613) | bool | `true` | If true, a ServiceAccount is automatically created. |
| [serviceAccount.name](./values.yaml#L616) | string | `""` | The name of the service account to use. If not set, a name is generated using the fullname template. |
| [serviceAccount.annotations](./values.yaml#L618) | object | `{}` | Extra annotations for the ServiceAccount. |
| [extraObjects](./values.yaml#L621) | list | `[]` | Extra Kubernetes resources to create. Helm templating is allowed as it is evaluated before creating the resources. |
| [analytics.disable](./values.yaml#L649) | bool | `false` | If true, sending anonymous analytics is disabled. To learn what date we collect, see [Privacy Policy](https://botkube.io/privacy#privacy-policy). |
| [e2eTest.image.registry](./values.yaml#L655) | string | `"ghcr.io"` | Test runner image registry. |
| [e2eTest.image.repository](./values.yaml#L657) | string | `"kubeshop/botkube-test"` | Test runner image repository. |
| [e2eTest.image.pullPolicy](./values.yaml#L659) | string | `"IfNotPresent"` | Test runner image pull policy. |
| [e2eTest.image.tag](./values.yaml#L661) | string | `"v9.99.9-dev"` | Test runner image tag. Default tag is `appVersion` from Chart.yaml. |
| [e2eTest.deployment](./values.yaml#L663) | object | `{"waitTimeout":"3m"}` | Configures BotKube Deployment related data. |
| [e2eTest.slack.botName](./values.yaml#L668) | string | `"botkube"` | Name of the BotKube bot to interact with during the e2e tests. |
| [e2eTest.slack.testerName](./values.yaml#L670) | string | `"botkube_tester"` | Name of the BotKube Tester bot that sends messages during the e2e tests. |
| [e2eTest.slack.testerAppToken](./values.yaml#L672) | string | `""` | Slack tester application token that interacts with BotKube bot. |
| [e2eTest.slack.additionalContextMessage](./values.yaml#L674) | string | `""` | Additional message that is sent by Tester. You can pass e.g. pull request number or source link where these tests are run from. |
| [e2eTest.slack.messageWaitTimeout](./values.yaml#L676) | string | `"1m"` | Message wait timeout. It defines how long we wait to ensure that notification were not sent when disabled. |
| [e2eTest.discord.botName](./values.yaml#L679) | string | `"botkube"` | Name of the BotKube bot to interact with during the e2e tests. |
| [e2eTest.discord.testerName](./values.yaml#L681) | string | `"botkube_tester"` | Name of the BotKube Tester bot that sends messages during the e2e tests. |
| [e2eTest.discord.guildID](./values.yaml#L683) | string | `""` | Discord Guild ID (discord server ID) used to run e2e tests |
| [e2eTest.discord.testerAppToken](./values.yaml#L685) | string | `""` | Discord tester application token that interacts with BotKube bot. |
| [e2eTest.discord.additionalContextMessage](./values.yaml#L687) | string | `""` | Additional message that is sent by Tester. You can pass e.g. pull request number or source link where these tests are run from. |
| [e2eTest.discord.messageWaitTimeout](./values.yaml#L689) | string | `"1m"` | Message wait timeout. It defines how long we wait to ensure that notification were not sent when disabled. |

### AWS IRSA on EKS support

Expand Down
9 changes: 9 additions & 0 deletions helm/botkube/templates/botkube-cm.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Values.settings.systemConfigMap.name }}
labels:
app.kubernetes.io/name: {{ include "botkube.name" . }}
helm.sh/chart: {{ include "botkube.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
2 changes: 2 additions & 0 deletions helm/botkube/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ spec:
- name: BOTKUBE_SETTINGS_KUBECONFIG
value: "/.kube/config"
{{- end }}
- name: BOTKUBE_SETTINGS_SYSTEM__CONFIG__MAP_NAMESPACE
value: "{{.Release.Namespace}}"
{{- with .Values.extraEnv }}
{{ toYaml . | nindent 12 }}
{{- end }}
Expand Down
Loading

0 comments on commit 14dc934

Please sign in to comment.