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

Persist sent help messages #738

Merged
merged 2 commits into from
Sep 20, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
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