From e82a1d7f97a7b4b90ef4a390bca8775a706d61f0 Mon Sep 17 00:00:00 2001 From: mugdha-adhav Date: Wed, 20 Feb 2019 13:24:55 +0530 Subject: [PATCH 1/7] Issue #37: Added multi-cluster support for displaying cluster specific information. Issue #16: Added cluster-name in kubectl, notifier and ping messages from botkube. --- config.yaml | 2 - design/multi-cluster.md | 51 +++++++ design/workflow.png | Bin 0 -> 80257 bytes helm/botkube/values.yaml | 2 - pkg/config/config.go | 1 - pkg/controller/controller.go | 13 +- pkg/execute/executor.go | 254 ++++++++++++++++++++++++----------- pkg/slack/slack.go | 21 +-- 8 files changed, 249 insertions(+), 95 deletions(-) create mode 100644 design/multi-cluster.md create mode 100644 design/workflow.png diff --git a/config.yaml b/config.yaml index 080388777..09a913c1c 100644 --- a/config.yaml +++ b/config.yaml @@ -125,5 +125,3 @@ settings: clustername: not-configured # Set false to disable kubectl commands execution allowkubectl: false - # Set true only respond to channel in config - checkchannel: false \ No newline at end of file diff --git a/design/multi-cluster.md b/design/multi-cluster.md new file mode 100644 index 000000000..8337f640f --- /dev/null +++ b/design/multi-cluster.md @@ -0,0 +1,51 @@ +# Multi-cluster Support + +#### Assumptions +`@botkube` commands refer to all the commands in the slack bot which currently supports: +- kubectl +- notifier +- ping + + +### Summary +Add Multi-cluster support for Botkube, where a single bot can monitor multiple clusters and respond to `@botkube` commands with cluster specific results. + +### Motivation +Currently in multi-cluster scenario, a Slack bot authenticates all the clusters with a same authentication token. Thus running `@botkube` command returns response from all the configured clusters, irrespective of the slack channel or group. For `@botkube` command execution, we need a particular cluster specific output. + +### Design + +This design approach adds a flag `--cluster-name` to all `@botkube` commands. Use of that flag is optional in a cluster specific channel. + +Botkube `Notifier` commands are restricted to a dedicated channel for a cluster only and `--cluster-name` flag is ignored. + +Botkube `ping` command with the `--cluster-name` flag returns `pong` response from the cluster specified in the flag, else you get response from all the clusters. `Ping` command without --cluster-name flag can be used to list all the configured clusters in the slack bot and identify you cluster's name among them. + +For `kubectl` commands in a dedicated channel to a cluster, if `--cluster-name` flag is used, it responds with the output for the cluster specified in flag, else it checks if the channel in the request matches the `config.Communications.Slack.Channel` and responds if true else ignores. + +For `kubectl` commands in a group, Direct message or channel not dedicated to any cluster, the `--cluster-name` flag is mandatory. The executor checks if the `--cluster-name` flag is present in the request. If yes, it gets the cluster's name from the flag and compares with `c.Settings.ClusterName` from the config file, if it matches then it responds with the required output to the slack bot and if it doesn't match, it ignores the request. And if the `--cluster-name` flag is absent for kubectl commands, it responds to the slack bot saying 'Please specify the cluster-name'. + +For example - +```sh +@Botkube get pods --cluster-name={CLUSTER_NAME} +``` +where, +`CLUSTER_NAME` is the name of the cluster you want to query. + +To get the list of all clusters configured in the slack, you can run the following command in slack. + +```sh +@Botkube ping +``` + +##### Workflow + +![Multi_Cluster_Design](workflow.png) + + +### Drawbacks +The `--cluster-name` flag is mandated for kubectl and notifier commands resulting additional overhead. + +### Alternatives +We can add channel specific authentication token or completely dedicate a channel to a particular cluster which requires changes in the slack code. + diff --git a/design/workflow.png b/design/workflow.png new file mode 100644 index 0000000000000000000000000000000000000000..9db49de49e71ed3ba7889b92c88a5e97f3e9b664 GIT binary patch literal 80257 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 ZDE#|v=EqtjoQqzvl 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
=oXKcMoH}2n(Dl0>7N!l)HQZ|vuuc*I1j3M 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 literal 0 HcmV?d00001 diff --git a/helm/botkube/values.yaml b/helm/botkube/values.yaml index 97c801790..b92b74e2a 100644 --- a/helm/botkube/values.yaml +++ b/helm/botkube/values.yaml @@ -140,8 +140,6 @@ config: clustername: not-configured # Set false to disable kubectl commands execution allowkubectl: false - # Set true only respond to channel in config - checkchannel: false resources: {} # We usually recommend not to specify default resources and to leave this as a conscious diff --git a/pkg/config/config.go b/pkg/config/config.go index 128d00b2c..5abd8d988 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -49,7 +49,6 @@ type Slack struct { type Settings struct { ClusterName string AllowKubectl bool - CheckChannel bool } // New returns new Config diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 2c15e2505..f41f6093d 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -1,6 +1,7 @@ package controller import ( + "fmt" "os" "os/signal" "strconv" @@ -20,13 +21,13 @@ import ( "k8s.io/client-go/tools/cache" ) -var startTime time.Time - const ( - controllerStartMsg = "...and now my watch begins! :crossed_swords:" - controllerStopMsg = "my watch has ended!" + controllerStartMsg = "...and now my watch begins for cluster '%s'! :crossed_swords:" + controllerStopMsg = "my watch has ended for cluster '%s'!" ) +var startTime time.Time + func findNamespace(ns string) string { if ns == "all" { return apiV1.NamespaceAll @@ -39,7 +40,7 @@ func findNamespace(ns string) string { // RegisterInformers creates new informer controllers to watch k8s resources func RegisterInformers(c *config.Config) { - sendMessage(controllerStartMsg) + sendMessage(fmt.Sprintf(controllerStartMsg, c.Settings.ClusterName)) startTime = time.Now().Local() // Get resync period @@ -127,7 +128,7 @@ func RegisterInformers(c *config.Config) { signal.Notify(sigterm, syscall.SIGTERM) signal.Notify(sigterm, syscall.SIGINT) <-sigterm - sendMessage(controllerStopMsg) + sendMessage(fmt.Sprintf(controllerStopMsg, c.Settings.ClusterName)) } func registerEventHandlers(resourceType string, events []string) (handlerFns cache.ResourceEventHandlerFuncs) { diff --git a/pkg/execute/executor.go b/pkg/execute/executor.go index 06a2a5f64..91e3f20d1 100644 --- a/pkg/execute/executor.go +++ b/pkg/execute/executor.go @@ -1,6 +1,7 @@ package execute import ( + "fmt" "io/ioutil" "os" "os/exec" @@ -25,19 +26,23 @@ var validKubectlCommands = map[string]bool{ "auth": true, } -var validNotifierCommands = map[string]bool{ +var validNotifierCommand = map[string]bool{ "notifier": true, - "help": true, - "ping": true, +} +var validPingCommand = map[string]bool{ + "ping": true, +} +var validHelpCommand = map[string]bool{ + "help": true, } var kubectlBinary = "/usr/local/bin/kubectl" const ( - notifierStartMsg = "Brace yourselves, notifications are coming." - notifierStopMsg = "Sure! I won't send you notifications anymore." + notifierStartMsg = "Brace yourselves, notifications are coming from cluster '%s'." + notifierStopMsg = "Sure! I won't send you notifications from cluster '%s' anymore." unsupportedCmdMsg = "Command not supported. Please run '@BotKube help' to see supported commands." - kubectlDisabledMsg = "Sorry, the admin hasn't given me the permission to execute kubectl command." + kubectlDisabledMsg = "Sorry, the admin hasn't given me the permission to execute kubectl command on cluster '%s'." ) // Executor is an interface for processes to execute commands @@ -47,15 +52,52 @@ type Executor interface { // DefaultExecutor is a default implementations of Executor type DefaultExecutor struct { - Message string - AllowKubectl bool + Message string + AllowKubectl bool + ClusterName string + ChannelName string + IsAuthChannel bool +} + +// NotifierAction creates custom type for notifier actions +type NotifierAction string + +// Defines constants for notifier actions +const ( + Start NotifierAction = "start" + Stop NotifierAction = "stop" + Status NotifierAction = "status" + ShowConfig NotifierAction = "showconfig" +) + +func (action NotifierAction) String() string { + return string(action) +} + +// CommandFlags creates custom type for flags in botkube +type CommandFlags string + +// Defines botkube flags +const ( + ClusterFlag CommandFlags = "--cluster-name" + FollowFlag CommandFlags = "--follow" + AbbrFollowFlag CommandFlags = "-f" + WatchFlag CommandFlags = "--watch" + AbbrWatchFlag CommandFlags = "-w" +) + +func (flag CommandFlags) String() string { + return string(flag) } // NewDefaultExecutor returns new Executor object -func NewDefaultExecutor(msg string, allowkubectl bool) Executor { +func NewDefaultExecutor(msg string, allowkubectl bool, clusterName, channelName string, isAuthChannel bool) Executor { return &DefaultExecutor{ - Message: msg, - AllowKubectl: allowkubectl, + Message: msg, + AllowKubectl: allowkubectl, + ClusterName: clusterName, + ChannelName: channelName, + IsAuthChannel: isAuthChannel, } } @@ -64,110 +106,170 @@ func (e *DefaultExecutor) Execute() string { args := strings.Split(e.Message, " ") if validKubectlCommands[args[0]] { if !e.AllowKubectl { - return kubectlDisabledMsg + return fmt.Sprintf(kubectlDisabledMsg, e.ClusterName) } - return runKubectlCommand(args) + return runKubectlCommand(args, e.ClusterName, e.IsAuthChannel) } - if validNotifierCommands[args[0]] { - return runNotifierCommand(args) + if validNotifierCommand[args[0]] { + return runNotifierCommand(args, e.ClusterName, e.IsAuthChannel) + } + if validPingCommand[args[0]] { + return runPingCommand(args, e.ClusterName) + } + if validHelpCommand[args[0]] { + return printHelp(e.ChannelName) } return unsupportedCmdMsg } -func printHelp() string { - allowedKubectl := "" - for k := range validKubectlCommands { - allowedKubectl = allowedKubectl + k + ", " +func printHelp(channelName string) string { + kubecltCmdKeys := make([]string, 0, len(validKubectlCommands)) + for cmd := range validKubectlCommands { + kubecltCmdKeys = append(kubecltCmdKeys, cmd) } - helpMsg := "BotKube executes kubectl commands on k8s cluster and returns output.\n" + - "Usages:\n" + - " @BotKube \n" + - "e.g:\n" + - " @BotKube get pods\n" + - " @BotKube logs podname -n namespace\n" + - "Allowed kubectl commands:\n" + - " " + allowedKubectl + "\n\n" + - "Commands to manage notifier:\n" + - "notifier stop Stop sending k8s event notifications to Slack (started by default)\n" + - "notifier start Start sending k8s event notifications to Slack\n" + - "notifier status Show running status of event notifier\n" + - "notifier showconfig Show BotKube configuration for event notifier\n\n" + - "Other Commands:\n" + - "help Show help\n" + - "ping Check connection health\n" - return helpMsg + allowedKubectl := strings.Join(kubecltCmdKeys, ", ") + helpMsg := ` +BotKube Help + +Usage: + @BotKube [--cluster-name ] + @BotKube notifier [stop|start|status|showconfig] + @BotKube ping [--cluster-name ] + +Description: + +Kubectl commands: + - Executes kubectl commands on k8s cluster and returns output. + + Example: + @BotKube get pods + @BotKube logs podname -n namespace + @BotKube get deployment --cluster-name cluster_name + + Allowed kubectl commands: + %s + +Cluster Status: + - List all available Kubernetes Clusters and check connection health. + - If flag specified, gives response from the specified cluster. + Example: + @BotKube ping + @BotKube ping --cluster-name mycluster + +Notifier commands: + - Commands to manage notifier (Runs only on configured channel %s). + + Example: + @BotKube notifier stop Stop sending k8s event notifications to Slack + @BotKube notifier start Start sending k8s event notifications to Slack + @BotKube notifier status Show running status of event notifier + @BotKube notifier showconfig Show BotKube configuration for event notifier + +Options: + --cluster-name Get cluster specific response +` + return fmt.Sprintf(helpMsg, allowedKubectl, channelName) } func printDefaultMsg() string { return unsupportedCmdMsg } -func runKubectlCommand(args []string) string { +func runKubectlCommand(args []string, clusterName string, isAuthChannel bool) string { // Use 'default' as a default namespace args = append([]string{"-n", "default"}, args...) // Remove unnecessary flags finalArgs := []string{} - for _, a := range args { - if a == "-f" || strings.HasPrefix(a, "--follow") { + checkFlag := false + for _, arg := range args { + if checkFlag { + if arg != clusterName { + return "" + } + checkFlag = false + continue + } + if arg == AbbrFollowFlag.String() || strings.HasPrefix(arg, FollowFlag.String()) { continue } - if a == "-w" || strings.HasPrefix(a, "--watch") { + if arg == AbbrWatchFlag.String() || strings.HasPrefix(arg, WatchFlag.String()) { continue } - finalArgs = append(finalArgs, a) + if strings.HasPrefix(arg, ClusterFlag.String()) { + if arg == ClusterFlag.String() { + checkFlag = true + } else if strings.SplitAfterN(arg, ClusterFlag.String()+"=", 2)[1] != clusterName { + return "" + } + isAuthChannel = true + continue + } + finalArgs = append(finalArgs, arg) + } + if isAuthChannel == false { + return "" } - cmd := exec.Command(kubectlBinary, finalArgs...) out, err := cmd.CombinedOutput() if err != nil { log.Logger.Error("Error in executing kubectl command: ", err) - return string(out) + err.Error() + return fmt.Sprintf("Cluster: %s\n%s", clusterName, string(out)+err.Error()) } - return string(out) + return fmt.Sprintf("Cluster: %s\n%s", clusterName, string(out)) } // TODO: Have a seperate cli which runs bot commands -func runNotifierCommand(args []string) string { - switch len(args) { - case 1: - if strings.ToLower(args[0]) == "help" { - return printHelp() - } - if strings.ToLower(args[0]) == "ping" { - return "pong" - } - case 2: - if args[0] != "notifier" { - return printDefaultMsg() - } - if args[1] == "start" { - config.Notify = true - log.Logger.Info("Notifier enabled") - return notifierStartMsg +func runNotifierCommand(args []string, clusterName string, isAuthChannel bool) string { + if isAuthChannel == false { + return "" + } + switch args[1] { + case Start.String(): + config.Notify = true + log.Logger.Info("Notifier enabled") + return fmt.Sprintf(notifierStartMsg, clusterName) + case Stop.String(): + config.Notify = false + log.Logger.Info("Notifier disabled") + return fmt.Sprintf(notifierStopMsg, clusterName) + case Status.String(): + if config.Notify == false { + return fmt.Sprintf("Notifications are off for cluster '%s'", clusterName) } - if args[1] == "stop" { - config.Notify = false - log.Logger.Info("Notifier disabled") - return notifierStopMsg + return fmt.Sprintf("Notifications are on for cluster '%s'", clusterName) + case ShowConfig.String(): + out, err := showControllerConfig() + if err != nil { + log.Logger.Error("Error in executing showconfig command: ", err) + return "Error in getting configuration!" } - if args[1] == "status" { - if config.Notify == false { - return "stopped" + return fmt.Sprintf("Showing config for cluster '%s'\n\n%s", clusterName, out) + } + return printDefaultMsg() +} + +func runPingCommand(args []string, clusterName string) string { + checkFlag := false + for _, arg := range args { + if checkFlag { + if arg != clusterName { + return "" } - return "running" + checkFlag = false + continue } - if args[1] == "showconfig" { - out, err := showControllerConfig() - if err != nil { - log.Logger.Error("Error in executing showconfig command: ", err) - return "Error in getting configuration!" + if strings.HasPrefix(arg, ClusterFlag.String()) { + if arg == ClusterFlag.String() { + checkFlag = true + } else if strings.SplitAfterN(arg, ClusterFlag.String()+"=", 2)[1] != clusterName { + return "" } - return out + continue } } - return printDefaultMsg() + return fmt.Sprintf("pong from cluster '%s'", clusterName) } func showControllerConfig() (string, error) { diff --git a/pkg/slack/slack.go b/pkg/slack/slack.go index af6434131..7e949a8d4 100644 --- a/pkg/slack/slack.go +++ b/pkg/slack/slack.go @@ -14,14 +14,15 @@ import ( type Bot struct { Token string AllowKubectl bool + ClusterName string ChannelName string - CheckChannel bool } // slackMessage contains message details to execute command and send back the result type slackMessage struct { ChannelID string BotID string + MessageType string InMessage string OutMessage string OutMsgLength int @@ -37,8 +38,8 @@ func NewSlackBot() *Bot { return &Bot{ Token: c.Communications.Slack.Token, AllowKubectl: c.Settings.AllowKubectl, + ClusterName: c.Settings.ClusterName, ChannelName: c.Communications.Slack.Channel, - CheckChannel: c.Settings.CheckChannel, } } @@ -55,6 +56,7 @@ func (b *Bot) Start() { go rtm.ManageConnection() for msg := range rtm.IncomingEvents { + isAuthChannel := false switch ev := msg.Data.(type) { case *slack.ConnectedEvent: logging.Logger.Debug("Connection Info: ", ev.Info) @@ -73,10 +75,9 @@ func (b *Bot) Start() { if !strings.HasPrefix(ev.Text, "<@"+botID+"> ") { continue } - // if config.settings.checkChannel is true // Serve only if current channel is in config - if b.CheckChannel && (b.ChannelName != info.Name) { - continue + if b.ChannelName == info.Name { + isAuthChannel = true } } } @@ -95,7 +96,7 @@ func (b *Bot) Start() { InMessage: inMessage, RTM: rtm, } - sm.HandleMessage(b.AllowKubectl) + sm.HandleMessage(b.AllowKubectl, b.ClusterName, b.ChannelName, isAuthChannel) case *slack.RTMError: logging.Logger.Errorf("Slack RMT error: %+v", ev.Error()) @@ -108,8 +109,8 @@ func (b *Bot) Start() { } } -func (sm *slackMessage) HandleMessage(allowkubectl bool) { - e := execute.NewDefaultExecutor(sm.InMessage, allowkubectl) +func (sm *slackMessage) HandleMessage(allowkubectl bool, clusterName, channelName string, isAuthChannel bool) { + e := execute.NewDefaultExecutor(sm.InMessage, allowkubectl, clusterName, channelName, isAuthChannel) sm.OutMessage = e.Execute() sm.OutMsgLength = len(sm.OutMessage) sm.Send() @@ -141,7 +142,11 @@ func (sm slackMessage) Send() { logging.Logger.Error("Error in uploading file:", err) } return + } else if sm.OutMsgLength == 0 { + logging.Logger.Info("Invalid request. Dumping the response") + return } + params := slack.PostMessageParameters{ AsUser: true, } From b97fec03177cb3b203825bfc774066ed8b612ad5 Mon Sep 17 00:00:00 2001 From: mugdha-adhav Date: Fri, 1 Mar 2019 14:32:17 +0530 Subject: [PATCH 2/7] Updated Dockerfile for multi-stage build --- build/Dockerfile | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/build/Dockerfile b/build/Dockerfile index 7231203c7..c9060ec0d 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -1,9 +1,5 @@ -FROM golang:1.11-alpine - -RUN mkdir -p /go/src/app -WORKDIR /go/src/app - -CMD ["/botkube"] +# Development image +FROM golang:alpine AS BUILD-ENV RUN mkdir -p /go/src/github.com/infracloudio/botkube/vendor && \ mkdir -p /go/src/github.com/infracloudio/botkube/cmd && \ @@ -14,11 +10,19 @@ COPY cmd/ /go/src/github.com/infracloudio/botkube/cmd COPY pkg/ /go/src/github.com/infracloudio/botkube/pkg RUN cd /go/src/github.com/infracloudio/botkube/cmd/botkube && \ - go build && \ - cp /go/src/github.com/infracloudio/botkube/cmd/botkube/botkube /botkube + GOOS=linux GOARCH=amd64 go build -o /go/bin/botkube -# Install kubectl ENV KUBE_LATEST_VERSION="v1.13.0" + RUN apk add --no-cache ca-certificates bash git \ && wget -q https://storage.googleapis.com/kubernetes-release/release/${KUBE_LATEST_VERSION}/bin/linux/amd64/kubectl -O /usr/local/bin/kubectl \ - && chmod +x /usr/local/bin/kubectl \ + && chmod +x /usr/local/bin/kubectl + +# Production image +FROM alpine + +COPY --from=BUILD-ENV /go/bin/botkube /go/bin/botkube +COPY --from=BUILD-ENV /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +COPY --from=BUILD-ENV /usr/local/bin/kubectl /usr/local/bin/kubectl + +ENTRYPOINT /go/bin/botkube From cd00a912fbc4775d3b84cf3c7310d04efb650511 Mon Sep 17 00:00:00 2001 From: mugdha-adhav Date: Mon, 4 Mar 2019 16:42:38 +0530 Subject: [PATCH 3/7] Modified build to get GOOS and GOARCH values for command line arguments. --- build/Dockerfile | 5 ++++- build/docker.sh | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/build/Dockerfile b/build/Dockerfile index c9060ec0d..61d05af32 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -1,6 +1,9 @@ # Development image FROM golang:alpine AS BUILD-ENV +ARG GOOS_VAL +ARG GOARCH_VAL + RUN mkdir -p /go/src/github.com/infracloudio/botkube/vendor && \ mkdir -p /go/src/github.com/infracloudio/botkube/cmd && \ mkdir -p /go/src/github.com/infracloudio/botkube/pkg @@ -10,7 +13,7 @@ COPY cmd/ /go/src/github.com/infracloudio/botkube/cmd COPY pkg/ /go/src/github.com/infracloudio/botkube/pkg RUN cd /go/src/github.com/infracloudio/botkube/cmd/botkube && \ - GOOS=linux GOARCH=amd64 go build -o /go/bin/botkube + GOOS=${GOOS_VAL} GOARCH=${GOARCH_VAL} go build -o /go/bin/botkube ENV KUBE_LATEST_VERSION="v1.13.0" diff --git a/build/docker.sh b/build/docker.sh index 08b5aed5a..ad162bfc9 100755 --- a/build/docker.sh +++ b/build/docker.sh @@ -4,6 +4,10 @@ BUILD_ROOT=$(dirname $0) IMAGE_REPO=${1:-infracloud/botkube} IMAGE_TAG=${2:-latest} +[ ! -z $(go env GOOS) ] && [ ! -z $(go env GOARCH) ] && \ + GOOS=$(go env GOOS) && GOARCH=$(go env GOARCH) || \ + echo "Couldn't determine the system architecture." + pushd ${BUILD_ROOT}/.. -docker build -t $IMAGE_REPO:$IMAGE_TAG -f ${BUILD_ROOT}/Dockerfile --no-cache . +docker build --build-arg GOOS_VAL=${GOOS} --build-arg GOARCH_VAL=${GOARCH} -t $IMAGE_REPO:$IMAGE_TAG -f ${BUILD_ROOT}/Dockerfile --no-cache . popd From 7a2ef32becc59bd45f2e8d57f11c3d23412eed8a Mon Sep 17 00:00:00 2001 From: mugdha-adhav Date: Wed, 20 Feb 2019 13:24:55 +0530 Subject: [PATCH 4/7] Issue #37: Added multi-cluster support for displaying cluster specific information. Issue #16: Added cluster-name in kubectl, notifier and ping messages from botkube. --- config.yaml | 2 - design/multi-cluster.md | 51 +++++++ design/workflow.png | Bin 0 -> 80257 bytes helm/botkube/values.yaml | 2 - pkg/config/config.go | 1 - pkg/controller/controller.go | 13 +- pkg/execute/executor.go | 254 ++++++++++++++++++++++++----------- pkg/slack/slack.go | 21 +-- 8 files changed, 249 insertions(+), 95 deletions(-) create mode 100644 design/multi-cluster.md create mode 100644 design/workflow.png diff --git a/config.yaml b/config.yaml index 080388777..09a913c1c 100644 --- a/config.yaml +++ b/config.yaml @@ -125,5 +125,3 @@ settings: clustername: not-configured # Set false to disable kubectl commands execution allowkubectl: false - # Set true only respond to channel in config - checkchannel: false \ No newline at end of file diff --git a/design/multi-cluster.md b/design/multi-cluster.md new file mode 100644 index 000000000..8337f640f --- /dev/null +++ b/design/multi-cluster.md @@ -0,0 +1,51 @@ +# Multi-cluster Support + +#### Assumptions +`@botkube` commands refer to all the commands in the slack bot which currently supports: +- kubectl +- notifier +- ping + + +### Summary +Add Multi-cluster support for Botkube, where a single bot can monitor multiple clusters and respond to `@botkube` commands with cluster specific results. + +### Motivation +Currently in multi-cluster scenario, a Slack bot authenticates all the clusters with a same authentication token. Thus running `@botkube` command returns response from all the configured clusters, irrespective of the slack channel or group. For `@botkube` command execution, we need a particular cluster specific output. + +### Design + +This design approach adds a flag `--cluster-name` to all `@botkube` commands. Use of that flag is optional in a cluster specific channel. + +Botkube `Notifier` commands are restricted to a dedicated channel for a cluster only and `--cluster-name` flag is ignored. + +Botkube `ping` command with the `--cluster-name` flag returns `pong` response from the cluster specified in the flag, else you get response from all the clusters. `Ping` command without --cluster-name flag can be used to list all the configured clusters in the slack bot and identify you cluster's name among them. + +For `kubectl` commands in a dedicated channel to a cluster, if `--cluster-name` flag is used, it responds with the output for the cluster specified in flag, else it checks if the channel in the request matches the `config.Communications.Slack.Channel` and responds if true else ignores. + +For `kubectl` commands in a group, Direct message or channel not dedicated to any cluster, the `--cluster-name` flag is mandatory. The executor checks if the `--cluster-name` flag is present in the request. If yes, it gets the cluster's name from the flag and compares with `c.Settings.ClusterName` from the config file, if it matches then it responds with the required output to the slack bot and if it doesn't match, it ignores the request. And if the `--cluster-name` flag is absent for kubectl commands, it responds to the slack bot saying 'Please specify the cluster-name'. + +For example - +```sh +@Botkube get pods --cluster-name={CLUSTER_NAME} +``` +where, +`CLUSTER_NAME` is the name of the cluster you want to query. + +To get the list of all clusters configured in the slack, you can run the following command in slack. + +```sh +@Botkube ping +``` + +##### Workflow + +![Multi_Cluster_Design](workflow.png) + + +### Drawbacks +The `--cluster-name` flag is mandated for kubectl and notifier commands resulting additional overhead. + +### Alternatives +We can add channel specific authentication token or completely dedicate a channel to a particular cluster which requires changes in the slack code. + diff --git a/design/workflow.png b/design/workflow.png new file mode 100644 index 0000000000000000000000000000000000000000..9db49de49e71ed3ba7889b92c88a5e97f3e9b664 GIT binary patch literal 80257 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 ZDE#|v=EqtjoQqzvl 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
=oXKcMoH}2n(Dl0>7N!l)HQZ|vuuc*I1j3M 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 literal 0 HcmV?d00001 diff --git a/helm/botkube/values.yaml b/helm/botkube/values.yaml index 97c801790..b92b74e2a 100644 --- a/helm/botkube/values.yaml +++ b/helm/botkube/values.yaml @@ -140,8 +140,6 @@ config: clustername: not-configured # Set false to disable kubectl commands execution allowkubectl: false - # Set true only respond to channel in config - checkchannel: false resources: {} # We usually recommend not to specify default resources and to leave this as a conscious diff --git a/pkg/config/config.go b/pkg/config/config.go index 128d00b2c..5abd8d988 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -49,7 +49,6 @@ type Slack struct { type Settings struct { ClusterName string AllowKubectl bool - CheckChannel bool } // New returns new Config diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 2c15e2505..f41f6093d 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -1,6 +1,7 @@ package controller import ( + "fmt" "os" "os/signal" "strconv" @@ -20,13 +21,13 @@ import ( "k8s.io/client-go/tools/cache" ) -var startTime time.Time - const ( - controllerStartMsg = "...and now my watch begins! :crossed_swords:" - controllerStopMsg = "my watch has ended!" + controllerStartMsg = "...and now my watch begins for cluster '%s'! :crossed_swords:" + controllerStopMsg = "my watch has ended for cluster '%s'!" ) +var startTime time.Time + func findNamespace(ns string) string { if ns == "all" { return apiV1.NamespaceAll @@ -39,7 +40,7 @@ func findNamespace(ns string) string { // RegisterInformers creates new informer controllers to watch k8s resources func RegisterInformers(c *config.Config) { - sendMessage(controllerStartMsg) + sendMessage(fmt.Sprintf(controllerStartMsg, c.Settings.ClusterName)) startTime = time.Now().Local() // Get resync period @@ -127,7 +128,7 @@ func RegisterInformers(c *config.Config) { signal.Notify(sigterm, syscall.SIGTERM) signal.Notify(sigterm, syscall.SIGINT) <-sigterm - sendMessage(controllerStopMsg) + sendMessage(fmt.Sprintf(controllerStopMsg, c.Settings.ClusterName)) } func registerEventHandlers(resourceType string, events []string) (handlerFns cache.ResourceEventHandlerFuncs) { diff --git a/pkg/execute/executor.go b/pkg/execute/executor.go index 06a2a5f64..91e3f20d1 100644 --- a/pkg/execute/executor.go +++ b/pkg/execute/executor.go @@ -1,6 +1,7 @@ package execute import ( + "fmt" "io/ioutil" "os" "os/exec" @@ -25,19 +26,23 @@ var validKubectlCommands = map[string]bool{ "auth": true, } -var validNotifierCommands = map[string]bool{ +var validNotifierCommand = map[string]bool{ "notifier": true, - "help": true, - "ping": true, +} +var validPingCommand = map[string]bool{ + "ping": true, +} +var validHelpCommand = map[string]bool{ + "help": true, } var kubectlBinary = "/usr/local/bin/kubectl" const ( - notifierStartMsg = "Brace yourselves, notifications are coming." - notifierStopMsg = "Sure! I won't send you notifications anymore." + notifierStartMsg = "Brace yourselves, notifications are coming from cluster '%s'." + notifierStopMsg = "Sure! I won't send you notifications from cluster '%s' anymore." unsupportedCmdMsg = "Command not supported. Please run '@BotKube help' to see supported commands." - kubectlDisabledMsg = "Sorry, the admin hasn't given me the permission to execute kubectl command." + kubectlDisabledMsg = "Sorry, the admin hasn't given me the permission to execute kubectl command on cluster '%s'." ) // Executor is an interface for processes to execute commands @@ -47,15 +52,52 @@ type Executor interface { // DefaultExecutor is a default implementations of Executor type DefaultExecutor struct { - Message string - AllowKubectl bool + Message string + AllowKubectl bool + ClusterName string + ChannelName string + IsAuthChannel bool +} + +// NotifierAction creates custom type for notifier actions +type NotifierAction string + +// Defines constants for notifier actions +const ( + Start NotifierAction = "start" + Stop NotifierAction = "stop" + Status NotifierAction = "status" + ShowConfig NotifierAction = "showconfig" +) + +func (action NotifierAction) String() string { + return string(action) +} + +// CommandFlags creates custom type for flags in botkube +type CommandFlags string + +// Defines botkube flags +const ( + ClusterFlag CommandFlags = "--cluster-name" + FollowFlag CommandFlags = "--follow" + AbbrFollowFlag CommandFlags = "-f" + WatchFlag CommandFlags = "--watch" + AbbrWatchFlag CommandFlags = "-w" +) + +func (flag CommandFlags) String() string { + return string(flag) } // NewDefaultExecutor returns new Executor object -func NewDefaultExecutor(msg string, allowkubectl bool) Executor { +func NewDefaultExecutor(msg string, allowkubectl bool, clusterName, channelName string, isAuthChannel bool) Executor { return &DefaultExecutor{ - Message: msg, - AllowKubectl: allowkubectl, + Message: msg, + AllowKubectl: allowkubectl, + ClusterName: clusterName, + ChannelName: channelName, + IsAuthChannel: isAuthChannel, } } @@ -64,110 +106,170 @@ func (e *DefaultExecutor) Execute() string { args := strings.Split(e.Message, " ") if validKubectlCommands[args[0]] { if !e.AllowKubectl { - return kubectlDisabledMsg + return fmt.Sprintf(kubectlDisabledMsg, e.ClusterName) } - return runKubectlCommand(args) + return runKubectlCommand(args, e.ClusterName, e.IsAuthChannel) } - if validNotifierCommands[args[0]] { - return runNotifierCommand(args) + if validNotifierCommand[args[0]] { + return runNotifierCommand(args, e.ClusterName, e.IsAuthChannel) + } + if validPingCommand[args[0]] { + return runPingCommand(args, e.ClusterName) + } + if validHelpCommand[args[0]] { + return printHelp(e.ChannelName) } return unsupportedCmdMsg } -func printHelp() string { - allowedKubectl := "" - for k := range validKubectlCommands { - allowedKubectl = allowedKubectl + k + ", " +func printHelp(channelName string) string { + kubecltCmdKeys := make([]string, 0, len(validKubectlCommands)) + for cmd := range validKubectlCommands { + kubecltCmdKeys = append(kubecltCmdKeys, cmd) } - helpMsg := "BotKube executes kubectl commands on k8s cluster and returns output.\n" + - "Usages:\n" + - " @BotKube \n" + - "e.g:\n" + - " @BotKube get pods\n" + - " @BotKube logs podname -n namespace\n" + - "Allowed kubectl commands:\n" + - " " + allowedKubectl + "\n\n" + - "Commands to manage notifier:\n" + - "notifier stop Stop sending k8s event notifications to Slack (started by default)\n" + - "notifier start Start sending k8s event notifications to Slack\n" + - "notifier status Show running status of event notifier\n" + - "notifier showconfig Show BotKube configuration for event notifier\n\n" + - "Other Commands:\n" + - "help Show help\n" + - "ping Check connection health\n" - return helpMsg + allowedKubectl := strings.Join(kubecltCmdKeys, ", ") + helpMsg := ` +BotKube Help + +Usage: + @BotKube [--cluster-name ] + @BotKube notifier [stop|start|status|showconfig] + @BotKube ping [--cluster-name ] + +Description: + +Kubectl commands: + - Executes kubectl commands on k8s cluster and returns output. + + Example: + @BotKube get pods + @BotKube logs podname -n namespace + @BotKube get deployment --cluster-name cluster_name + + Allowed kubectl commands: + %s + +Cluster Status: + - List all available Kubernetes Clusters and check connection health. + - If flag specified, gives response from the specified cluster. + Example: + @BotKube ping + @BotKube ping --cluster-name mycluster + +Notifier commands: + - Commands to manage notifier (Runs only on configured channel %s). + + Example: + @BotKube notifier stop Stop sending k8s event notifications to Slack + @BotKube notifier start Start sending k8s event notifications to Slack + @BotKube notifier status Show running status of event notifier + @BotKube notifier showconfig Show BotKube configuration for event notifier + +Options: + --cluster-name Get cluster specific response +` + return fmt.Sprintf(helpMsg, allowedKubectl, channelName) } func printDefaultMsg() string { return unsupportedCmdMsg } -func runKubectlCommand(args []string) string { +func runKubectlCommand(args []string, clusterName string, isAuthChannel bool) string { // Use 'default' as a default namespace args = append([]string{"-n", "default"}, args...) // Remove unnecessary flags finalArgs := []string{} - for _, a := range args { - if a == "-f" || strings.HasPrefix(a, "--follow") { + checkFlag := false + for _, arg := range args { + if checkFlag { + if arg != clusterName { + return "" + } + checkFlag = false + continue + } + if arg == AbbrFollowFlag.String() || strings.HasPrefix(arg, FollowFlag.String()) { continue } - if a == "-w" || strings.HasPrefix(a, "--watch") { + if arg == AbbrWatchFlag.String() || strings.HasPrefix(arg, WatchFlag.String()) { continue } - finalArgs = append(finalArgs, a) + if strings.HasPrefix(arg, ClusterFlag.String()) { + if arg == ClusterFlag.String() { + checkFlag = true + } else if strings.SplitAfterN(arg, ClusterFlag.String()+"=", 2)[1] != clusterName { + return "" + } + isAuthChannel = true + continue + } + finalArgs = append(finalArgs, arg) + } + if isAuthChannel == false { + return "" } - cmd := exec.Command(kubectlBinary, finalArgs...) out, err := cmd.CombinedOutput() if err != nil { log.Logger.Error("Error in executing kubectl command: ", err) - return string(out) + err.Error() + return fmt.Sprintf("Cluster: %s\n%s", clusterName, string(out)+err.Error()) } - return string(out) + return fmt.Sprintf("Cluster: %s\n%s", clusterName, string(out)) } // TODO: Have a seperate cli which runs bot commands -func runNotifierCommand(args []string) string { - switch len(args) { - case 1: - if strings.ToLower(args[0]) == "help" { - return printHelp() - } - if strings.ToLower(args[0]) == "ping" { - return "pong" - } - case 2: - if args[0] != "notifier" { - return printDefaultMsg() - } - if args[1] == "start" { - config.Notify = true - log.Logger.Info("Notifier enabled") - return notifierStartMsg +func runNotifierCommand(args []string, clusterName string, isAuthChannel bool) string { + if isAuthChannel == false { + return "" + } + switch args[1] { + case Start.String(): + config.Notify = true + log.Logger.Info("Notifier enabled") + return fmt.Sprintf(notifierStartMsg, clusterName) + case Stop.String(): + config.Notify = false + log.Logger.Info("Notifier disabled") + return fmt.Sprintf(notifierStopMsg, clusterName) + case Status.String(): + if config.Notify == false { + return fmt.Sprintf("Notifications are off for cluster '%s'", clusterName) } - if args[1] == "stop" { - config.Notify = false - log.Logger.Info("Notifier disabled") - return notifierStopMsg + return fmt.Sprintf("Notifications are on for cluster '%s'", clusterName) + case ShowConfig.String(): + out, err := showControllerConfig() + if err != nil { + log.Logger.Error("Error in executing showconfig command: ", err) + return "Error in getting configuration!" } - if args[1] == "status" { - if config.Notify == false { - return "stopped" + return fmt.Sprintf("Showing config for cluster '%s'\n\n%s", clusterName, out) + } + return printDefaultMsg() +} + +func runPingCommand(args []string, clusterName string) string { + checkFlag := false + for _, arg := range args { + if checkFlag { + if arg != clusterName { + return "" } - return "running" + checkFlag = false + continue } - if args[1] == "showconfig" { - out, err := showControllerConfig() - if err != nil { - log.Logger.Error("Error in executing showconfig command: ", err) - return "Error in getting configuration!" + if strings.HasPrefix(arg, ClusterFlag.String()) { + if arg == ClusterFlag.String() { + checkFlag = true + } else if strings.SplitAfterN(arg, ClusterFlag.String()+"=", 2)[1] != clusterName { + return "" } - return out + continue } } - return printDefaultMsg() + return fmt.Sprintf("pong from cluster '%s'", clusterName) } func showControllerConfig() (string, error) { diff --git a/pkg/slack/slack.go b/pkg/slack/slack.go index af6434131..7e949a8d4 100644 --- a/pkg/slack/slack.go +++ b/pkg/slack/slack.go @@ -14,14 +14,15 @@ import ( type Bot struct { Token string AllowKubectl bool + ClusterName string ChannelName string - CheckChannel bool } // slackMessage contains message details to execute command and send back the result type slackMessage struct { ChannelID string BotID string + MessageType string InMessage string OutMessage string OutMsgLength int @@ -37,8 +38,8 @@ func NewSlackBot() *Bot { return &Bot{ Token: c.Communications.Slack.Token, AllowKubectl: c.Settings.AllowKubectl, + ClusterName: c.Settings.ClusterName, ChannelName: c.Communications.Slack.Channel, - CheckChannel: c.Settings.CheckChannel, } } @@ -55,6 +56,7 @@ func (b *Bot) Start() { go rtm.ManageConnection() for msg := range rtm.IncomingEvents { + isAuthChannel := false switch ev := msg.Data.(type) { case *slack.ConnectedEvent: logging.Logger.Debug("Connection Info: ", ev.Info) @@ -73,10 +75,9 @@ func (b *Bot) Start() { if !strings.HasPrefix(ev.Text, "<@"+botID+"> ") { continue } - // if config.settings.checkChannel is true // Serve only if current channel is in config - if b.CheckChannel && (b.ChannelName != info.Name) { - continue + if b.ChannelName == info.Name { + isAuthChannel = true } } } @@ -95,7 +96,7 @@ func (b *Bot) Start() { InMessage: inMessage, RTM: rtm, } - sm.HandleMessage(b.AllowKubectl) + sm.HandleMessage(b.AllowKubectl, b.ClusterName, b.ChannelName, isAuthChannel) case *slack.RTMError: logging.Logger.Errorf("Slack RMT error: %+v", ev.Error()) @@ -108,8 +109,8 @@ func (b *Bot) Start() { } } -func (sm *slackMessage) HandleMessage(allowkubectl bool) { - e := execute.NewDefaultExecutor(sm.InMessage, allowkubectl) +func (sm *slackMessage) HandleMessage(allowkubectl bool, clusterName, channelName string, isAuthChannel bool) { + e := execute.NewDefaultExecutor(sm.InMessage, allowkubectl, clusterName, channelName, isAuthChannel) sm.OutMessage = e.Execute() sm.OutMsgLength = len(sm.OutMessage) sm.Send() @@ -141,7 +142,11 @@ func (sm slackMessage) Send() { logging.Logger.Error("Error in uploading file:", err) } return + } else if sm.OutMsgLength == 0 { + logging.Logger.Info("Invalid request. Dumping the response") + return } + params := slack.PostMessageParameters{ AsUser: true, } From 289f407dfeb62eb03e09013a25ce272204bbb4b7 Mon Sep 17 00:00:00 2001 From: mugdha-adhav Date: Fri, 1 Mar 2019 14:32:17 +0530 Subject: [PATCH 5/7] Updated Dockerfile for multi-stage build --- build/Dockerfile | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/build/Dockerfile b/build/Dockerfile index 7231203c7..c9060ec0d 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -1,9 +1,5 @@ -FROM golang:1.11-alpine - -RUN mkdir -p /go/src/app -WORKDIR /go/src/app - -CMD ["/botkube"] +# Development image +FROM golang:alpine AS BUILD-ENV RUN mkdir -p /go/src/github.com/infracloudio/botkube/vendor && \ mkdir -p /go/src/github.com/infracloudio/botkube/cmd && \ @@ -14,11 +10,19 @@ COPY cmd/ /go/src/github.com/infracloudio/botkube/cmd COPY pkg/ /go/src/github.com/infracloudio/botkube/pkg RUN cd /go/src/github.com/infracloudio/botkube/cmd/botkube && \ - go build && \ - cp /go/src/github.com/infracloudio/botkube/cmd/botkube/botkube /botkube + GOOS=linux GOARCH=amd64 go build -o /go/bin/botkube -# Install kubectl ENV KUBE_LATEST_VERSION="v1.13.0" + RUN apk add --no-cache ca-certificates bash git \ && wget -q https://storage.googleapis.com/kubernetes-release/release/${KUBE_LATEST_VERSION}/bin/linux/amd64/kubectl -O /usr/local/bin/kubectl \ - && chmod +x /usr/local/bin/kubectl \ + && chmod +x /usr/local/bin/kubectl + +# Production image +FROM alpine + +COPY --from=BUILD-ENV /go/bin/botkube /go/bin/botkube +COPY --from=BUILD-ENV /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +COPY --from=BUILD-ENV /usr/local/bin/kubectl /usr/local/bin/kubectl + +ENTRYPOINT /go/bin/botkube From 4d9e2278f9418e07939d1dd21100e5a18da1c842 Mon Sep 17 00:00:00 2001 From: mugdha-adhav Date: Mon, 4 Mar 2019 16:42:38 +0530 Subject: [PATCH 6/7] Modified build to get GOOS and GOARCH values for command line arguments. --- build/Dockerfile | 5 ++++- build/docker.sh | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/build/Dockerfile b/build/Dockerfile index c9060ec0d..61d05af32 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -1,6 +1,9 @@ # Development image FROM golang:alpine AS BUILD-ENV +ARG GOOS_VAL +ARG GOARCH_VAL + RUN mkdir -p /go/src/github.com/infracloudio/botkube/vendor && \ mkdir -p /go/src/github.com/infracloudio/botkube/cmd && \ mkdir -p /go/src/github.com/infracloudio/botkube/pkg @@ -10,7 +13,7 @@ COPY cmd/ /go/src/github.com/infracloudio/botkube/cmd COPY pkg/ /go/src/github.com/infracloudio/botkube/pkg RUN cd /go/src/github.com/infracloudio/botkube/cmd/botkube && \ - GOOS=linux GOARCH=amd64 go build -o /go/bin/botkube + GOOS=${GOOS_VAL} GOARCH=${GOARCH_VAL} go build -o /go/bin/botkube ENV KUBE_LATEST_VERSION="v1.13.0" diff --git a/build/docker.sh b/build/docker.sh index 08b5aed5a..ad162bfc9 100755 --- a/build/docker.sh +++ b/build/docker.sh @@ -4,6 +4,10 @@ BUILD_ROOT=$(dirname $0) IMAGE_REPO=${1:-infracloud/botkube} IMAGE_TAG=${2:-latest} +[ ! -z $(go env GOOS) ] && [ ! -z $(go env GOARCH) ] && \ + GOOS=$(go env GOOS) && GOARCH=$(go env GOARCH) || \ + echo "Couldn't determine the system architecture." + pushd ${BUILD_ROOT}/.. -docker build -t $IMAGE_REPO:$IMAGE_TAG -f ${BUILD_ROOT}/Dockerfile --no-cache . +docker build --build-arg GOOS_VAL=${GOOS} --build-arg GOARCH_VAL=${GOARCH} -t $IMAGE_REPO:$IMAGE_TAG -f ${BUILD_ROOT}/Dockerfile --no-cache . popd From 661083777c4bd9ddbd5f476da740ec6f0796b24c Mon Sep 17 00:00:00 2001 From: mugdha-adhav Date: Tue, 5 Mar 2019 19:19:27 +0530 Subject: [PATCH 7/7] Added git tag and docker images versioning support with Makefile. Updated travis to use Makefile. Removed @botkube help commands. --- .release | 1 + .travis.yml | 4 +-- Makefile | 51 +++++++++++++++++++++++++++++++++ build/docker.sh | 13 --------- helm/botkube/values.yaml | 2 +- pkg/execute/executor.go | 61 +++------------------------------------- 6 files changed, 59 insertions(+), 73 deletions(-) create mode 100644 .release create mode 100644 Makefile delete mode 100755 build/docker.sh diff --git a/.release b/.release new file mode 100644 index 000000000..e2ed7558e --- /dev/null +++ b/.release @@ -0,0 +1 @@ +release=1.0.0 diff --git a/.travis.yml b/.travis.yml index 6bf432437..7a40f26ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ install: - go get -u golang.org/x/lint/golint - curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get > get_helm.sh - chmod +x get_helm.sh - - sudo ./get_helm.sh + - ./get_helm.sh before_script: - hack/verify-gofmt.sh @@ -21,4 +21,4 @@ before_script: - helm lint helm/botkube script: - - build/docker.sh infracloudio/botkube latest + - make diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..61f9e5139 --- /dev/null +++ b/Makefile @@ -0,0 +1,51 @@ +IMAGE_REPO=infracloud/botkube +TAG=$(shell cut -d'=' -f2- .release) + +.DEFAULT_GOAL := build +.PHONY: release git-tag check-git-status build pre-build tag-image publish + +#Docker Tasks +#Make a release +release: check-git-status build tag-image publish git-tag + @echo "Successfully released version $(TAG)" + +#Create a git tag +git-tag: + @echo "Creating a git tag" + @git add . + @git commit -m "Bumped to version $(TAG)" ; + @git tag $(TAG) ; + @git push --tags origin master; + @echo 'Git tag pushed successfully' ; + +#Check git status +check-git-status: + @echo "Checking git status" + @if [ -n "$(shell git tag | grep $(TAG))" ] ; then echo 'Tag already exists' && exit 1 ; fi + @if [ -z "$(shell git remote -v)" ] ; then echo 'No remote to push tags to' && exit 1 ; fi + @if [ -z "$(shell git config user.email)" ] ; then echo 'Unable to detect git credentials' && exit 1 ; fi + +#Build the image +build: pre-build + @echo "Building docker image" + @docker build --build-arg GOOS_VAL=$(shell go env GOOS) --build-arg GOARCH_VAL=$(shell go env GOARCH) -t $(IMAGE_REPO) -f build/Dockerfile --no-cache . + @echo "Docker image build successfully" + +#Pre-build checks +pre-build: + @echo "Checking system information" + @if [ -z "$(shell go env GOOS)" ] || [ -z "$(shell go env GOARCH)" ] ; then echo 'Could not determine the system architecture.' && exit 1 ; fi + + +#Tag images +tag-image: + @echo 'Tagging image' + @docker tag $(IMAGE_REPO) $(IMAGE_REPO):$(TAG) + @docker tag $(IMAGE_REPO) $(IMAGE_REPO):latest + +#Docker push image +publish: + @echo "Pushing docker image to repository" + @docker login + @docker push $(IMAGE_REPO):$(TAG) + @docker push $(IMAGE_REPO):latest diff --git a/build/docker.sh b/build/docker.sh deleted file mode 100755 index ad162bfc9..000000000 --- a/build/docker.sh +++ /dev/null @@ -1,13 +0,0 @@ -set +x - -BUILD_ROOT=$(dirname $0) -IMAGE_REPO=${1:-infracloud/botkube} -IMAGE_TAG=${2:-latest} - -[ ! -z $(go env GOOS) ] && [ ! -z $(go env GOARCH) ] && \ - GOOS=$(go env GOOS) && GOARCH=$(go env GOARCH) || \ - echo "Couldn't determine the system architecture." - -pushd ${BUILD_ROOT}/.. -docker build --build-arg GOOS_VAL=${GOOS} --build-arg GOARCH_VAL=${GOARCH} -t $IMAGE_REPO:$IMAGE_TAG -f ${BUILD_ROOT}/Dockerfile --no-cache . -popd diff --git a/helm/botkube/values.yaml b/helm/botkube/values.yaml index b92b74e2a..f44ea9541 100644 --- a/helm/botkube/values.yaml +++ b/helm/botkube/values.yaml @@ -6,7 +6,7 @@ replicaCount: 1 image: repository: infracloud/botkube - tag: "0.4" + tag: "latest" pullPolicy: Always nameOverride: "" diff --git a/pkg/execute/executor.go b/pkg/execute/executor.go index 91e3f20d1..d9996f3df 100644 --- a/pkg/execute/executor.go +++ b/pkg/execute/executor.go @@ -32,16 +32,13 @@ var validNotifierCommand = map[string]bool{ var validPingCommand = map[string]bool{ "ping": true, } -var validHelpCommand = map[string]bool{ - "help": true, -} var kubectlBinary = "/usr/local/bin/kubectl" const ( notifierStartMsg = "Brace yourselves, notifications are coming from cluster '%s'." notifierStopMsg = "Sure! I won't send you notifications from cluster '%s' anymore." - unsupportedCmdMsg = "Command not supported. Please run '@BotKube help' to see supported commands." + unsupportedCmdMsg = "Command not supported. Please run /botkubehelp to see supported commands." kubectlDisabledMsg = "Sorry, the admin hasn't given me the permission to execute kubectl command on cluster '%s'." ) @@ -116,60 +113,10 @@ func (e *DefaultExecutor) Execute() string { if validPingCommand[args[0]] { return runPingCommand(args, e.ClusterName) } - if validHelpCommand[args[0]] { - return printHelp(e.ChannelName) - } - return unsupportedCmdMsg -} - -func printHelp(channelName string) string { - kubecltCmdKeys := make([]string, 0, len(validKubectlCommands)) - for cmd := range validKubectlCommands { - kubecltCmdKeys = append(kubecltCmdKeys, cmd) + if e.IsAuthChannel { + return unsupportedCmdMsg } - allowedKubectl := strings.Join(kubecltCmdKeys, ", ") - helpMsg := ` -BotKube Help - -Usage: - @BotKube [--cluster-name ] - @BotKube notifier [stop|start|status|showconfig] - @BotKube ping [--cluster-name ] - -Description: - -Kubectl commands: - - Executes kubectl commands on k8s cluster and returns output. - - Example: - @BotKube get pods - @BotKube logs podname -n namespace - @BotKube get deployment --cluster-name cluster_name - - Allowed kubectl commands: - %s - -Cluster Status: - - List all available Kubernetes Clusters and check connection health. - - If flag specified, gives response from the specified cluster. - - Example: - @BotKube ping - @BotKube ping --cluster-name mycluster - -Notifier commands: - - Commands to manage notifier (Runs only on configured channel %s). - - Example: - @BotKube notifier stop Stop sending k8s event notifications to Slack - @BotKube notifier start Start sending k8s event notifications to Slack - @BotKube notifier status Show running status of event notifier - @BotKube notifier showconfig Show BotKube configuration for event notifier - -Options: - --cluster-name Get cluster specific response -` - return fmt.Sprintf(helpMsg, allowedKubectl, channelName) + return "" } func printDefaultMsg() string {