Skip to content

Commit

Permalink
Merge pull request #49 from infracloudio/develop
Browse files Browse the repository at this point in the history
Includes following changes:
- Multicluster support
- Add Makefile
- Disable help command (since moved to "Slash Command")
  • Loading branch information
PrasadG193 committed Mar 7, 2019
2 parents 346efdc + 775417c commit 79bac64
Show file tree
Hide file tree
Showing 13 changed files with 272 additions and 121 deletions.
1 change: 1 addition & 0 deletions .release
@@ -0,0 +1 @@
release=1.0.0
4 changes: 2 additions & 2 deletions .travis.yml
Expand Up @@ -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
Expand All @@ -21,4 +21,4 @@ before_script:
- helm lint helm/botkube

script:
- build/docker.sh infracloudio/botkube latest
- make
51 changes: 51 additions & 0 deletions 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
25 changes: 16 additions & 9 deletions build/Dockerfile
@@ -1,9 +1,8 @@
FROM golang:1.11-alpine
# Development image
FROM golang:alpine AS BUILD-ENV

RUN mkdir -p /go/src/app
WORKDIR /go/src/app

CMD ["/botkube"]
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 && \
Expand All @@ -14,11 +13,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=${GOOS_VAL} GOARCH=${GOARCH_VAL} 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
9 changes: 0 additions & 9 deletions build/docker.sh

This file was deleted.

2 changes: 0 additions & 2 deletions config.yaml
Expand Up @@ -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
51 changes: 51 additions & 0 deletions 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.

Binary file added design/workflow.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 1 addition & 3 deletions helm/botkube/values.yaml
Expand Up @@ -6,7 +6,7 @@ replicaCount: 1

image:
repository: infracloud/botkube
tag: "0.4"
tag: "latest"
pullPolicy: Always

nameOverride: ""
Expand Down Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion pkg/config/config.go
Expand Up @@ -49,7 +49,6 @@ type Slack struct {
type Settings struct {
ClusterName string
AllowKubectl bool
CheckChannel bool
}

// New returns new Config
Expand Down
13 changes: 7 additions & 6 deletions pkg/controller/controller.go
@@ -1,6 +1,7 @@
package controller

import (
"fmt"
"os"
"os/signal"
"strconv"
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 79bac64

Please sign in to comment.