Skip to content

Commit

Permalink
Improve Botkube CLI login, migrate commands and E2E migration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
pkosiec committed Jul 10, 2023
1 parent 35007e4 commit 7faf9f5
Show file tree
Hide file tree
Showing 34 changed files with 1,070 additions and 776 deletions.
7 changes: 4 additions & 3 deletions .github/workflows/branch-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ on:
push:
branches:
- main
- migration-improvements

env:
HELM_VERSION: v3.9.0
K3D_VERSION: v5.4.6
IMAGE_REGISTRY: "ghcr.io"
IMAGE_REPOSITORY: "kubeshop/botkube"
MIGRATOR_IMAGE_REPOSITORY: "kubeshop/botkube-migration"
CFG_EXPORTER_IMAGE_REPOSITORY: "kubeshop/botkube-config-exporter"
IMAGE_TAG: v9.99.9-dev # TODO: Use commit hash tag to make the predictable builds for each commit on branch

jobs:
Expand Down Expand Up @@ -82,7 +83,7 @@ jobs:
run: make release-snapshot-cli
- name: Add botkube alias
run: |
echo BOTKUBE_BIN="$PWD/dist/botkube-cli_linux_amd64_v1/botkube" >> $GITHUB_ENV
echo BOTKUBE_BINARY_PATH="$PWD/dist/botkube-cli_linux_amd64_v1/botkube" >> $GITHUB_ENV
- name: Install Helm
uses: azure/setup-helm@v1
with:
Expand All @@ -94,7 +95,7 @@ jobs:
- name: Run e2e tests for botkube client
env:
DISCORD_BOT_ID: ${{ secrets.DISCORD_BOT_ID }}
DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_TESTER_APP_TOKEN }}
DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_BOT_TOKEN }}
DISCORD_GUILD_ID: ${{ secrets.DISCORD_GUILD_ID }}
DISCORD_TESTER_APP_TOKEN: ${{ secrets.DISCORD_TESTER_APP_TOKEN }}
BOTKUBE_CLOUD_DEV_GQL_ENDPOINT: ${{ secrets.BOTKUBE_CLOUD_DEV_GQL_ENDPOINT }}
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/pr-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ env:
PR_NUMBER: ${{ github.event.pull_request.number }}
IMAGE_REGISTRY: "ghcr.io"
IMAGE_REPOSITORY: "kubeshop/pr/botkube"
CFG_EXPORTER_IMAGE_REPOSITORY: "kubeshop/pr/botkube-config-exporter"
IMAGE_TAG: ${{ github.event.pull_request.number }}-PR
IMAGE_SAVE_LOAD_DIR: /tmp/botkube-images

Expand Down Expand Up @@ -69,7 +70,7 @@ jobs:
retention-days: 1

push-image:
name: Push Botkube image
name: Push images
runs-on: ubuntu-latest
needs: [ save-image ]

Expand Down
42 changes: 33 additions & 9 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ env:
- IMAGE_REGISTRY={{ if index .Env "IMAGE_REGISTRY" }}{{ .Env.IMAGE_REGISTRY }}{{ else }}ghcr.io{{ end }}
- IMAGE_REPOSITORY={{ if index .Env "IMAGE_REPOSITORY" }}{{ .Env.IMAGE_REPOSITORY }}{{ else }}kubeshop/botkube{{ end }}
- IMAGE_TAG={{ if index .Env "IMAGE_TAG" }}{{ .Env.IMAGE_TAG }}{{ else }}{{ .Tag }}{{ end }}
- MIGRATOR_IMAGE_REPOSITORY={{ if index .Env "MIGRATOR_IMAGE_REPOSITORY" }}{{ .Env.MIGRATOR_IMAGE_REPOSITORY }}{{ else }}kubeshop/botkube-migration{{ end }}
- CFG_EXPORTER_IMAGE_REPOSITORY={{ if index .Env "CFG_EXPORTER_IMAGE_REPOSITORY" }}{{ .Env.CFG_EXPORTER_IMAGE_REPOSITORY }}{{ else }}kubeshop/botkube-config-exporter{{ end }}
- ANALYTICS_API_KEY={{ if index .Env "ANALYTICS_API_KEY" }}{{ .Env.ANALYTICS_API_KEY }}{{ else }}{{ end }}
before:
hooks:
Expand Down Expand Up @@ -33,7 +33,10 @@ builds:
main: cmd/cli/main.go
ldflags:
- -s -w
-X github.com/kubeshop/botkube/internal/cli/migrate.Tag={{ .Env.IMAGE_TAG }}
-X github.com/kubeshop/botkube/cmd/cli/cmd/migrate.DefaultImageTag={{ .Env.IMAGE_TAG }}
-X go.szostok.io/version.version={{.Version}}
-X go.szostok.io/version.buildDate={{.Date}}
-X go.szostok.io/version.name=botkube
env:
- CGO_ENABLED=0
goos:
Expand All @@ -44,8 +47,8 @@ builds:
- arm64
goarm:
- "7"
- id: botkube-cloud-migration
binary: botkube-cloud-migration
- id: botkube-config-exporter
binary: botkube-config-exporter
main: cmd/config-exporter/main.go
env:
- CGO_ENABLED=0
Expand Down Expand Up @@ -80,6 +83,7 @@ changelog:
skip: false

dockers:
# Botkube Agent
- image_templates:
- "{{.Env.IMAGE_REGISTRY}}/{{.Env.IMAGE_REPOSITORY}}:{{ .Env.IMAGE_TAG }}-amd64"
use: buildx
Expand All @@ -104,19 +108,39 @@ dockers:
build_flag_templates:
- "--platform=linux/arm"
- "--build-arg=botkube_version={{ .Env.IMAGE_TAG }}"
# Config Exporter
- image_templates:
- "{{.Env.IMAGE_REGISTRY}}/{{.Env.MIGRATOR_IMAGE_REPOSITORY}}:{{ .Env.IMAGE_TAG }}"
- "{{.Env.IMAGE_REGISTRY}}/{{.Env.CFG_EXPORTER_IMAGE_REPOSITORY}}:{{ .Env.IMAGE_TAG }}-amd64"
use: buildx
dockerfile: "build/Dockerfile.migration"
dockerfile: "build/Dockerfile.config_exporter"
build_flag_templates:
- "--platform=linux/amd64"
- "--build-arg=botkube_cloud_migration_version={{ .Env.IMAGE_TAG }}"
- "--build-arg=botkube_config_exporter_version={{ .Env.IMAGE_TAG }}"
- image_templates:
- "{{.Env.IMAGE_REGISTRY}}/{{.Env.CFG_EXPORTER_IMAGE_REPOSITORY}}:{{ .Env.IMAGE_TAG }}-arm64"
use: buildx
goarch: arm64
dockerfile: "build/Dockerfile.config_exporter"
build_flag_templates:
- "--platform=linux/arm64"
- "--build-arg=botkube_config_exporter_version={{ .Env.IMAGE_TAG }}"
- image_templates:
- "{{.Env.IMAGE_REGISTRY}}/{{.Env.CFG_EXPORTER_IMAGE_REPOSITORY}}:{{ .Env.IMAGE_TAG }}-armv7"
use: buildx
goarch: arm
goarm: 7
dockerfile: "build/Dockerfile.config_exporter"
build_flag_templates:
- "--platform=linux/arm"
- "--build-arg=botkube_config_exporter_version={{ .Env.IMAGE_TAG }}"
docker_manifests:
- name_template: "{{.Env.IMAGE_REGISTRY}}/{{.Env.IMAGE_REPOSITORY}}:{{ .Env.IMAGE_TAG }}"
image_templates:
- "{{.Env.IMAGE_REGISTRY}}/{{.Env.IMAGE_REPOSITORY}}:{{ .Env.IMAGE_TAG }}-amd64"
- "{{.Env.IMAGE_REGISTRY}}/{{.Env.IMAGE_REPOSITORY}}:{{ .Env.IMAGE_TAG }}-arm64"
- "{{.Env.IMAGE_REGISTRY}}/{{.Env.IMAGE_REPOSITORY}}:{{ .Env.IMAGE_TAG }}-armv7"
- name_template: "{{.Env.IMAGE_REGISTRY}}/{{.Env.MIGRATOR_IMAGE_REPOSITORY}}:{{ .Env.IMAGE_TAG }}"
- name_template: "{{.Env.IMAGE_REGISTRY}}/{{.Env.CFG_EXPORTER_IMAGE_REPOSITORY}}:{{ .Env.IMAGE_TAG }}"
image_templates:
- "{{.Env.IMAGE_REGISTRY}}/{{.Env.MIGRATOR_IMAGE_REPOSITORY}}:{{ .Env.IMAGE_TAG }}"
- "{{.Env.IMAGE_REGISTRY}}/{{.Env.CFG_EXPORTER_IMAGE_REPOSITORY}}:{{ .Env.IMAGE_TAG }}-amd64"
- "{{.Env.IMAGE_REGISTRY}}/{{.Env.CFG_EXPORTER_IMAGE_REPOSITORY}}:{{ .Env.IMAGE_TAG }}-arm64"
- "{{.Env.IMAGE_REGISTRY}}/{{.Env.CFG_EXPORTER_IMAGE_REPOSITORY}}:{{ .Env.IMAGE_TAG }}-armv7"
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ test-integration-discord: system-check
@go test -v -tags=integration -race -count=1 ./test/e2e/... -run "TestDiscord"

test-migration-tool: system-check
@go test -v -tags=e2e -race -count=1 ./test/migration/e2e/...
@go test -v -tags=migration -race -count=1 ./test/e2e/...

# Build the binary
build: pre-build
Expand Down Expand Up @@ -93,7 +93,7 @@ gen-plugins-index: build-plugins

gen-docs-cli:
rm -f ./cmd/cli/docs/*
go run cmd/cli/main.go gen-usage-docs
go run -ldflags="-X go.szostok.io/version.name=botkube" cmd/cli/main.go gen-usage-docs
.PHONY: gen-docs-cli

# Pre-build checks
Expand Down
14 changes: 14 additions & 0 deletions build/Dockerfile.config_exporter
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM gcr.io/distroless/static:latest

ARG botkube_config_exporter_version="dev"

LABEL org.opencontainers.image.source="git@github.com:kubeshop/botkube.git" \
org.opencontainers.image.title="Botkube Config Exporter" \
org.opencontainers.image.version="${botkube_config_exporter_version}" \
org.opencontainers.image.description="Botkube Config Exporter fetches the Botkube configuration and stores it in a ConfigMap." \
org.opencontainers.image.documentation="https://docs.botkube.io" \
org.opencontainers.image.licenses="MIT"

COPY botkube-config-exporter /usr/local/bin/botkube-config-exporter

CMD ["botkube-config-exporter"]
14 changes: 0 additions & 14 deletions build/Dockerfile.migration

This file was deleted.

6 changes: 3 additions & 3 deletions cmd/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ The server is stopped after the callback is received.

### Migration

Once logged in, we create a pod in the same namespace as the Botkube instance that mounts the same
secrets and config maps as the Botkube pod and generates and stores the entire configuration in a
config map `botkube-migration`.
Once user is logged in, Botkube CLI creates a Pod in the same namespace where Botkube resides. Then, it mounts the same
Secrets and ConfigMaps as the Botkube Pod, and pulls entire configuration to a
ConfigMap `botkube-config-exporter`.

Once we have the configuration, we can turn it into a API call and create identical
resources in Botkube Cloud.
14 changes: 10 additions & 4 deletions cmd/cli/cmd/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,17 @@ func New() (*Config, error) {
return c, nil
}

const (
dirPerms = 0755
filePerms = 0644
)

// Save saves Config to local FS
func (c *Config) Save() error {
if _, err := os.Stat(filepath.Clean(filepath.Dir(configFilePath))); os.IsNotExist(err) {
// #nosec G301
err := os.MkdirAll(filepath.Clean(filepath.Dir(configFilePath)), 0755)
cfgFileDir := filepath.Clean(filepath.Dir(configFilePath))
cfgFilePath := filepath.Clean(configFilePath)
if _, err := os.Stat(cfgFileDir); os.IsNotExist(err) {
err = os.MkdirAll(cfgFileDir, dirPerms)
if err != nil {
return fmt.Errorf("failed to create config directory: %v", err)
}
Expand All @@ -48,7 +54,7 @@ func (c *Config) Save() error {
}

// #nosec G306
err = os.WriteFile(filepath.Clean(configFilePath), data, 0644)
err = os.WriteFile(cfgFilePath, data, filePerms)
if err != nil {
return fmt.Errorf("failed to write config: %v", err)
}
Expand Down
88 changes: 8 additions & 80 deletions cmd/cli/cmd/login.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,19 @@
package cmd

import (
"context"
"fmt"
"io"
"net/http"
"os"
"time"

"github.com/fatih/color"
"github.com/pkg/browser"
"github.com/spf13/cobra"

"github.com/kubeshop/botkube/cmd/cli/cmd/config"
"github.com/kubeshop/botkube/internal/cli"
"github.com/kubeshop/botkube/internal/cli/heredoc"
)

const (
srvAddress = "localhost:8085"
loginURL = "http://localhost:3000/cli/login?cli_server_login=http://localhost:8085/login_redirect"
redirectURLSuccess = "http://localhost:3000/cli/login?success=true"
"github.com/kubeshop/botkube/internal/cli/login"
)

// NewLogin returns a cobra.Command for logging into a Botkube Cloud.
func NewLogin() *cobra.Command {
var opts login.Options

login := &cobra.Command{
Use: "login [OPTIONS]",
Short: "Login to a Botkube Cloud",
Expand All @@ -33,74 +22,13 @@ func NewLogin() *cobra.Command {
<cli> login
`, cli.Name),
RunE: func(cmd *cobra.Command, args []string) error {
return runLogin(cmd.Context(), os.Stdout)
return login.Run(cmd.Context(), os.Stdout, opts)
},
}

return login
}

func runLogin(_ context.Context, w io.Writer) error {
t, err := fetchToken(srvAddress, loginURL)
if err != nil {
return err
}

c := config.Config{Token: t.Token}
if err := c.Save(); err != nil {
return err
}

okCheck := color.New(color.FgGreen).FprintlnFunc()
okCheck(w, "Login Succeeded")

return nil
}

type tokenResp struct {
Token string `json:"token"`
}

func fetchToken(addr, authUrl string) (*tokenResp, error) {
ch := make(chan *tokenResp)
errCh := make(chan error)

mux := http.NewServeMux()
mux.HandleFunc("/login_redirect", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, redirectURLSuccess, http.StatusFound)

ch <- &tokenResp{
Token: r.URL.Query().Get("token"),
}
})

s := http.Server{
Addr: addr,
Handler: mux,
ReadHeaderTimeout: 2 * time.Second,
}

go func() {
err := s.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
errCh <- err
}
}()

fmt.Println(heredoc.Docf(`
Your browser has been opened to visit:
%s
`, authUrl))
err := browser.OpenURL(authUrl)
if err != nil {
return nil, fmt.Errorf("failed to open page: %v", err)
}
flags := login.Flags()
flags.StringVar(&opts.CloudDashboardURL, "cloud-dashboard-url", "https://app.botkube.io", "Botkube Cloud URL")
flags.StringVar(&opts.LocalServerAddress, "local-server-addr", "localhost:8085", "Address of a local server which is used for the login flow")

select {
case token := <-ch:
_ = s.Shutdown(context.Background())
return token, nil
case err = <-errCh:
return nil, err
}
return login
}
Loading

0 comments on commit 7faf9f5

Please sign in to comment.