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

Filter and quiet flag for list CLI command. #221

Merged
merged 4 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
62 changes: 60 additions & 2 deletions containerm/cli/cli_command_ctrs_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
"fmt"
"os"
"strconv"

"strings"
daniel-milchev marked this conversation as resolved.
Show resolved Hide resolved
"text/tabwriter"

"github.com/eclipse-kanto/container-management/containerm/client"
Expand All @@ -30,7 +32,9 @@ type listCmd struct {
}

type listConfig struct {
name string
name string
quiet bool
filter []string
}

func (cc *listCmd) init(cli *cli) {
Expand All @@ -43,7 +47,7 @@ func (cc *listCmd) init(cli *cli) {
RunE: func(cmd *cobra.Command, args []string) error {
return cc.run(args)
},
Example: "list\n list --name <container-name>\n list -n <container-name>",
Example: " list\n list --name <container-name>\n list -n <container-name>\n list -q\n list --filter <status/image/exitcode>",
daniel-milchev marked this conversation as resolved.
Show resolved Hide resolved
}
cc.setupFlags()
}
Expand All @@ -57,6 +61,20 @@ func (cc *listCmd) run(args []string) error {
if err != nil {
return err
}
if len(cc.config.filter) > 0 && len(ctrs) > 0 {
filtered, err := filterBy(cc.config.filter, ctrs)
if err != nil {
return err
}
ctrs = filtered
}
if cc.config.quiet != false && len(ctrs) > 0 {
for _, ctr := range ctrs {
fmt.Printf("%s ", ctr.ID)
}
fmt.Println()
return nil
}
daniel-milchev marked this conversation as resolved.
Show resolved Hide resolved
if len(ctrs) == 0 {
fmt.Println("No found containers.")
} else {
Expand All @@ -69,6 +87,46 @@ func (cc *listCmd) setupFlags() {
flagSet := cc.cmd.Flags()
// init name flags
flagSet.StringVarP(&cc.config.name, "name", "n", "", "List all containers with a specific name.")
flagSet.BoolVarP(&cc.config.quiet, "quiet", "q", false, "List only container IDs.")
flagSet.StringSliceVar(&cc.config.filter, "filter", nil, "Lists only containers with the filter.")
daniel-milchev marked this conversation as resolved.
Show resolved Hide resolved
}

func filterBy(input []string, ctrs []*types.Container) ([]*types.Container, error) {
var (
holderStatus string
holderImage string
convertedExitCode int = -1
err error
)
filteredCtrs := []*types.Container{}
for _, inp := range input {
if strings.HasPrefix(inp, "status=") {
holderStatus = strings.TrimPrefix(inp, "status=")
} else if strings.HasPrefix(inp, "image=") {
holderImage = strings.TrimPrefix(inp, "image=")
} else if strings.HasPrefix(inp, "exitcode=") {
exitcodeHolder := strings.TrimPrefix(inp, "exitcode=")
convertedExitCode, err = strconv.Atoi(exitcodeHolder)
daniel-milchev marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("no such filter")
daniel-milchev marked this conversation as resolved.
Show resolved Hide resolved
}
}
for _, ctr := range ctrs {
if holderStatus != "" && !strings.EqualFold(ctr.State.Status.String(), holderStatus) {
continue
}
if holderImage != "" && !strings.EqualFold(ctr.Image.Name, holderImage) {
continue
}
if int64(convertedExitCode) != -1 && ctr.State.ExitCode != int64(convertedExitCode) {
continue
}
filteredCtrs = append(filteredCtrs, ctr)
}
return filteredCtrs, nil
}

/*
Expand Down
75 changes: 74 additions & 1 deletion containerm/cli/cli_command_ctrs_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ import (

const (
// command flags
listCmdFlagName = "name"
listCmdFlagName = "name"
listCmdFlagQuiet = "quiet"
listCmdFlagFilter = "filter"

// test input constants
listContainerID = "test-ctr"
Expand Down Expand Up @@ -117,6 +119,42 @@ func (listTc *listCommandTest) generateRunExecutionConfigs() map[string]testRunE
},
mockExecution: listTc.mockExecListByNameNoCtrs,
},
"test_list_quiet": {
flags: map[string]string{
listCmdFlagQuiet: "true",
},
mockExecution: listTc.mockExecListQuiet,
},
"test_list_with_filter_status": {
flags: map[string]string{
listCmdFlagFilter: "status=creating",
},
mockExecution: listTc.mockExecListWithFilter,
},
"test_list_with_filter_image": {
flags: map[string]string{
listCmdFlagFilter: "image=test",
},
mockExecution: listTc.mockExecListWithFilter,
},
"test_list_with_filter_exit_code": {
flags: map[string]string{
listCmdFlagFilter: "exitcode=0",
},
mockExecution: listTc.mockExecListWithFilter,
},
"test_list_with_multiple_filters": {
flags: map[string]string{
listCmdFlagFilter: "image=test,exitcode=0",
},
mockExecution: listTc.mockExecListWithFilter,
},
"test_list_with_filter_error": {
flags: map[string]string{
listCmdFlagFilter: "test=test",
},
mockExecution: listTc.mockExecListWithFilterError,
},
"test_list_by_name_err": {
flags: map[string]string{
listCmdFlagName: listFlagName,
Expand Down Expand Up @@ -172,6 +210,41 @@ func (listTc *listCommandTest) mockExecListByNameNoCtrs(args []string) error {
return nil
}

func (listTc *listCommandTest) mockExecListQuiet(args []string) error {
// setup expected calls
ctrs := []*types.Container{{
ID: listContainerID,
Name: listFlagName,
State: &types.State{},
}}
listTc.mockClient.EXPECT().List(context.Background()).Times(1).Return(ctrs, nil)
// no error expected
return nil
}

func (listTc *listCommandTest) mockExecListWithFilter(args []string) error {
// setup expected calls
ctrs := []*types.Container{{
ID: listContainerID,
Name: listFlagName,
State: &types.State{},
}}
listTc.mockClient.EXPECT().List(context.Background()).Times(1).Return(ctrs, nil)
// no error expected
return nil
}

func (listTc *listCommandTest) mockExecListWithFilterError(args []string) error {
err := log.NewError("no such filter")
ctrs := []*types.Container{{
ID: listContainerID,
Name: listFlagName,
State: &types.State{},
}}
listTc.mockClient.EXPECT().List(context.Background()).Times(1).Return(ctrs, nil)
return err
}

func (listTc *listCommandTest) mockExecListByNameErrors(args []string) error {
// setup expected calls
err := log.NewError("failed to get containers list")
Expand Down
48 changes: 32 additions & 16 deletions integration/framework/cli/cmd_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,10 @@ func RunCmdTestCases(t *testing.T, cmdList []TestCaseCMD) {
if cmd.setupCmd != nil {
runMultipleCommands(t, *cmd.setupCmd)
}
result := icmd.RunCmd(cmd.icmd)
checkArguments(t, &cmd.icmd)
fmt.Printf("Running command: %s %s\n", cmd.icmd.Command[0], cmd.icmd.Command[1:])
daniel-milchev marked this conversation as resolved.
Show resolved Hide resolved
result := icmd.RunCommand(cmd.icmd.Command[0], cmd.icmd.Command[1:]...)
// result := icmd.RunCmd(cmd.icmd)
daniel-milchev marked this conversation as resolved.
Show resolved Hide resolved
if cmd.goldenFile != "" {
assert.Assert(t, golden.String(result.Stdout(), cmd.goldenFile))
}
Expand All @@ -122,25 +125,36 @@ func RunCmdTestCases(t *testing.T, cmdList []TestCaseCMD) {
}
}

func runMultipleCommands(t *testing.T, cmdArr []icmd.Cmd) {
for _, cmd := range cmdArr {
result := icmd.RunCmd(cmd)
result.Assert(t, icmd.Expected{ExitCode: 0})
}
}

func buildCmd(binary string, args ...string) *icmd.Cmd {
envArgs := []string{}
for _, arg := range args {
func checkArguments(t *testing.T, cmd *icmd.Cmd) {
execCmd := []string{}
for _, arg := range cmd.Command {
if strings.HasPrefix(arg, "$(") && strings.HasSuffix(arg, ")") {
arg = strings.TrimPrefix(arg, "$(")
arg = strings.TrimSuffix(arg, ")")
arguments := strings.Split(arg, " ")
cmd := icmd.Command(arguments[0], arguments[1:]...)
checkArguments(t, &cmd)
result := icmd.RunCmd(cmd)
assert.Equal(t, result.ExitCode, 0)
execCmd = append(execCmd, strings.Split(strings.TrimSuffix(strings.TrimSuffix(result.Stdout(), "\n"), " "), " ")...)
continue
}
if strings.HasPrefix(arg, "$") {
if val, ok := os.LookupEnv(strings.TrimPrefix(arg, "$")); ok {
arg = val
}
}
envArgs = append(envArgs, arg)
execCmd = append(execCmd, arg)
}
*cmd = icmd.Cmd{Command: execCmd}
}

func runMultipleCommands(t *testing.T, cmdArr []icmd.Cmd) {
for _, cmd := range cmdArr {
checkArguments(t, &cmd)
result := icmd.RunCommand(cmd.Command[0], cmd.Command[1:]...)
result.Assert(t, icmd.Expected{ExitCode: 0})
}
cmd := icmd.Command(binary, envArgs...)
return &cmd
}

func assertCustomResult(t *testing.T, result icmd.Result, name string, args ...string) {
Expand All @@ -152,7 +166,8 @@ func assertCustomResult(t *testing.T, result icmd.Result, name string, args ...s
func fromAPITestCommand(cmd TestCommand) TestCaseCMD {
return TestCaseCMD{
name: cmd.Name,
icmd: *buildCmd(cmd.Command.Binary, cmd.Command.Args...),
icmd: icmd.Command(cmd.Command.Binary, cmd.Command.Args...),
// icmd: *checkArguments(cmd.Command.Binary, cmd.Command.Args...),
daniel-milchev marked this conversation as resolved.
Show resolved Hide resolved
expected: icmd.Expected{
ExitCode: cmd.Expected.ExitCode,
Timeout: cmd.Expected.Timeout,
Expand All @@ -174,7 +189,8 @@ func buildCmdArrFromCommand(cmd *[]Command) *[]icmd.Cmd {
}
cmds := make([]icmd.Cmd, 0)
for _, cmd := range *cmd {
cmds = append(cmds, *buildCmd(cmd.Binary, cmd.Args...))
cmds = append(cmds, icmd.Command(cmd.Binary, cmd.Args...))
// cmds = append(cmds, *checkArguments(cmd.Binary, cmd.Args...))
daniel-milchev marked this conversation as resolved.
Show resolved Hide resolved
}
return &cmds
}
10 changes: 7 additions & 3 deletions integration/testdata/list-help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ Usage:
kanto-cm list

Examples:
list
list
list --name <container-name>
list -n <container-name>
list -q
list --filter <status/image/exitcode>

Flags:
-h, --help help for list
-n, --name string List all containers with a specific name.
--filter strings Lists only containers with the filter.
-h, --help help for list
-n, --name string List all containers with a specific name.
-q, --quiet List only container IDs.

Global Flags:
--debug Switch commands log level to DEBUG mode
Expand Down
20 changes: 20 additions & 0 deletions integration/testdata/list-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,26 @@ onExit:
- binary: "kanto-cm"
args: ["remove", "--host", "$KANTO_HOST", "-n", "list_containers_with_state_created", "-f"]
---
name: list_containers_with_quiet
daniel-milchev marked this conversation as resolved.
Show resolved Hide resolved
setupCmd:
- binary: kanto-cm
args: ["create", "--host", "$KANTO_HOST", "-n", "list_containers_with_quiet_one", "docker.io/library/influxdb:1.8.4"]
- binary: kanto-cm
args: ["create", "--host", "$KANTO_HOST", "-n", "list_containers_with_quiet_two", "docker.io/library/influxdb:1.8.4"]
command:
binary: kanto-cm
args: ["list", "--host", "$KANTO_HOST", "-q"]
expected:
exitCode: 0
customResult:
type: REGEX
args: ["[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"]
onExit:
- binary: "kanto-cm"
args: ["remove", "--host", "$KANTO_HOST", "-n", "list_containers_with_quiet_one", "-f"]
- binary: "kanto-cm"
args: ["remove", "--host", "$KANTO_HOST", "-n", "list_containers_with_quiet_two", "-f"]
---
name: list_existing_container
setupCmd:
- binary: kanto-cm
Expand Down
14 changes: 13 additions & 1 deletion integration/testdata/remove-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,16 @@ onExit:
- binary: kanto-cm
args: ["stop", "--host", "$KANTO_HOST", "-s", "SIGKILL", "-n", "remove_container_with_state_running"]
- binary: "kanto-cm"
args: ["remove", "--host", "$KANTO_HOST", "-n", "remove_container_with_state_running", "-f"]
args: ["remove", "--host", "$KANTO_HOST", "-n", "remove_container_with_state_running", "-f"]
---
name: remove_multiple_containers
setupCmd:
- binary: kanto-cm
args: ["create", "--host", "$KANTO_HOST", "-n", "remove_container_one", "docker.io/library/influxdb:1.8.4"]
- binary: kanto-cm
args: ["create", "--host", "$KANTO_HOST", "-n", "remove_container_two", "docker.io/library/influxdb:1.8.4"]
command:
binary: kanto-cm
args: ["remove", "--host", "$KANTO_HOST", "$(kanto-cm list --quiet)"]
expected:
exitCode: 0
Loading