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

Added '@Botkube commands list' to show all the supported kubectl cmds #328

Merged
merged 9 commits into from
Sep 2, 2020
34 changes: 34 additions & 0 deletions pkg/execute/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ var (
validFilterCommand = map[string]bool{
"filters": true,
}
validInfoCommand = map[string]bool{
"commands": true,
}
validDebugCommands = map[string]bool{
"exec": true,
"logs": true,
Expand Down Expand Up @@ -146,6 +149,14 @@ const (
FilterDisable FiltersAction = "disable"
)

// InfoAction for options in Info commands
type InfoAction string
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not export InfoAction type if not needed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

corrected InfoAction to infoAction

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The output of the command is

allowed verbs:
- cluster-info
- diff
- get
- api-resources
- api-versions
- describe
- explain
- logs
- top
- auth
allowed resources:
- storageclasses
- nodes
- deployments
- pods
- namespaces
- daemonsets
- statefulsets


// Info command options
const (
InfoList InfoAction = "list"
)

func (action FiltersAction) String() string {
return string(action)
}
Expand Down Expand Up @@ -210,6 +221,12 @@ func (e *DefaultExecutor) Execute() string {
if validFilterCommand[args[0]] {
return e.runFilterCommand(args, e.ClusterName, e.IsAuthChannel)
}

//Check if info command
if validInfoCommand[args[0]] {
return e.runInfoCommand(args, e.IsAuthChannel)
}

if e.IsAuthChannel {
return printDefaultMsg(e.Platform)
}
Expand Down Expand Up @@ -360,6 +377,23 @@ func (e *DefaultExecutor) runFilterCommand(args []string, clusterName string, is
return printDefaultMsg(e.Platform)
}

//runInfoCommand to list allowed commands
func (e *DefaultExecutor) runInfoCommand(args []string, isAuthChannel bool) string {
if isAuthChannel == false {
return ""
}
if len(args) < 2 && args[1] != string(InfoList) {
return IncompleteCmdMsg
}
return makeCommandInfoList()
}

func makeCommandInfoList() string {
allowedVerbs := utils.GetFormatedCommandsList("allowed verbs:", utils.AllowedKubectlVerbMap)
allowedResources := utils.GetFormatedCommandsList("allowed resources:", utils.AllowedKubectlResourceMap)
return allowedVerbs + allowedResources
}

// Use tabwriter to display string in tabular form
// https://golang.org/pkg/text/tabwriter
func makeFiltersList() string {
Expand Down
14 changes: 14 additions & 0 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
package utils

import (
"bytes"
"fmt"
"os"
"regexp"
"strconv"
Expand Down Expand Up @@ -477,3 +479,15 @@ func GetClusterNameFromKubectlCmd(cmd string) string {
}
return s
}

//GetFormatedCommandsList get the formated commands list
func GetFormatedCommandsList(header string, commands map[string]bool) string {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use generic name for the function. It feels that this func is specifically for command list only

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

corrected name to GetStingInYamlFormat, pls suggest

var b bytes.Buffer
fmt.Fprintln(&b, header)
for k, v := range commands {
if v {
fmt.Fprintf(&b, " - %s\n", k)
}
}
return b.String()
}
13 changes: 13 additions & 0 deletions pkg/utils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package utils

import (
"fmt"
"testing"
)

Expand Down Expand Up @@ -49,3 +50,15 @@ func TestGetClusterNameFromKubectlCmd(t *testing.T) {
}
}
}

func TestGetFormatedCommandsList(t *testing.T) {
var header = "allowed verbs"
var commands = map[string]bool{
"api-versions": true,
}
expected := fmt.Sprintf(header + "\n - api-versions\n")
got := GetFormatedCommandsList(header, commands)
if got != expected {
t.Errorf("expected: %v, got: %v", expected, got)
}
}
29 changes: 28 additions & 1 deletion test/e2e/command/botkube.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (

"github.com/infracloudio/botkube/pkg/config"
"github.com/infracloudio/botkube/pkg/execute"
"github.com/infracloudio/botkube/pkg/log"
"github.com/infracloudio/botkube/test/e2e/utils"
"github.com/nlopes/slack"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -59,8 +60,29 @@ func (c *context) testBotkubeCommand(t *testing.T) {
"ImageTagChecker true Checks and adds recommendation if 'latest' image tag is used for container image.\n" +
"IngressValidator true Checks if services and tls secrets used in ingress specs are available.\n",
},
"BotKube commands list": {
command: "commands list",
expected: "allowed verbs:\n" +
" - api-resources\n" +
" - describe\n" +
" - diff\n" +
" - explain\n" +
" - get\n" +
" - logs\n" +
" - api-versions\n" +
" - cluster-info\n" +
" - top\n" +
" - auth\n" +
"allowed resources:\n" +
" - nodes\n" +
" - deployments\n" +
" - pods\n" +
" - namespaces\n" +
" - daemonsets\n" +
" - statefulsets\n" +
" - storageclasses\n",
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
if c.TestEnv.Config.Communications.Slack.Enabled {
Expand All @@ -80,6 +102,9 @@ func (c *context) testBotkubeCommand(t *testing.T) {
case "filters list":
fl := compareFilters(strings.Split(test.expected, "\n"), strings.Split(strings.Trim(m.Text, "```"), "\n"))
assert.Equal(t, fl, true)
case "commands list":
cl := compareFilters(strings.Split(test.expected, "\n"), strings.Split(strings.Trim(m.Text, "```"), "\n"))
assert.Equal(t, cl, true)
default:
assert.Equal(t, test.expected, m.Text)
}
Expand All @@ -90,6 +115,7 @@ func (c *context) testBotkubeCommand(t *testing.T) {

func compareFilters(expected, actual []string) bool {
if len(expected) != len(actual) {
log.Infof("************************Expected %d Actual %d", len(expected), len(actual))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the debug logs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

return false
}

Expand All @@ -103,6 +129,7 @@ func compareFilters(expected, actual []string) bool {
}
}
if !found {
log.Infof("************************Not Found %s", a)
return false
}
}
Expand Down
3 changes: 2 additions & 1 deletion test/resource_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ settings:
# method which are allowed
verbs: ["api-resources", "api-versions", "cluster-info", "describe", "diff", "explain", "get", "logs", "top", "auth"]
# resource configuration which is allowed
resources: ["deployments", "pods" , "namespaces", "daemonsets", "statefulsets", "storageclasses", "serviceaccounts"]
resources: ["deployments", "pods" , "namespaces", "daemonsets", "statefulsets", "storageclasses", "nodes"]

# set Namespace to execute botkube kubectl commands by default
defaultNamespace: default
# Set true to enable commands execution from configured channel only
Expand Down