From d715fc345f217eb681ddbd7bad36a0661c22e171 Mon Sep 17 00:00:00 2001 From: Karl Fischer Date: Sun, 4 Oct 2020 16:39:32 +0200 Subject: [PATCH] fix ambiguous copy (#55) --- CHANGELOG.md | 6 +++ README.md | 2 +- cli/command.go | 27 +++++++++++++ client/client.go | 2 +- client/traverse.go | 12 +++++- main.go | 76 ++++++++++++------------------------ test/suites/commands/cp.bats | 21 ++++++++-- 7 files changed, 88 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 872357c7..1ddc8112 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## v0.7.2 (October 4, 2020) + +BUG FIXES: + +* Fix copy of ambiguous sub-file ([#55](https://github.com/fishi0x01/vsh/pull/55)) + ## v0.7.1 (October 2, 2020) BUG FIXES: diff --git a/README.md b/README.md index 2de1afda..7421e072 100644 --- a/README.md +++ b/README.md @@ -196,4 +196,4 @@ make integration-tests ## Debugging -The `-v` option enables verbose mode, which also creates a `vsh_trace.log` file to log any error object from the vault API. +`-v DEBUG` sets debug log level, which also creates a `vsh_trace.log` file to log any error object from the vault API. diff --git a/cli/command.go b/cli/command.go index 0ed41560..9f8e1f31 100644 --- a/cli/command.go +++ b/cli/command.go @@ -1,6 +1,7 @@ package cli import ( + "os" "path/filepath" "strings" @@ -16,6 +17,32 @@ type Command interface { Parse(args []string) error } +// Commands contains all available commands +type Commands struct { + Mv *MoveCommand + Cp *CopyCommand + Append *AppendCommand + Rm *RemoveCommand + Ls *ListCommand + Cd *CdCommand + Cat *CatCommand + Grep *GrepCommand +} + +// NewCommands returns a Commands struct with all available commands +func NewCommands(client *client.Client) *Commands { + return &Commands{ + Mv: NewMoveCommand(client), + Cp: NewCopyCommand(client), + Append: NewAppendCommand(client), + Rm: NewRemoveCommand(client), + Ls: NewListCommand(client), + Cd: NewCdCommand(client), + Cat: NewCatCommand(client), + Grep: NewGrepCommand(client, os.Stdout, os.Stderr), + } +} + func cmdPath(pwd string, arg string) (result string) { result = filepath.Clean(pwd + arg) diff --git a/client/client.go b/client/client.go index 83ada1ee..b1983305 100644 --- a/client/client.go +++ b/client/client.go @@ -156,7 +156,7 @@ func (client *Client) GetType(absolutePath string) (kind PathKind) { // Traverse traverses given absolutePath via DFS and returns sub-paths in array func (client *Client) Traverse(absolutePath string) (paths []string) { if client.isTopLevelPath(absolutePath) { - paths = client.topLevelTraverse(normalizedVaultPath(absolutePath)) + paths = client.topLevelTraverse() } else { paths = client.lowLevelTraverse(normalizedVaultPath(absolutePath)) } diff --git a/client/traverse.go b/client/traverse.go index de6c8eac..c38e4654 100644 --- a/client/traverse.go +++ b/client/traverse.go @@ -5,7 +5,7 @@ import ( "strings" ) -func (client *Client) topLevelTraverse(path string) (result []string) { +func (client *Client) topLevelTraverse() (result []string) { for k := range client.KVBackends { result = append(result, k) } @@ -24,7 +24,15 @@ func (client *Client) lowLevelTraverse(path string) (result []string) { if keysInterface, ok := s.Data["keys"]; ok { for _, valInterface := range keysInterface.([]interface{}) { val := valInterface.(string) - result = append(result, client.lowLevelTraverse(path+"/"+val)...) + // prevent ambiguous dir/file to be added twice + if strings.HasSuffix(val, "/") { + // dir + result = append(result, client.lowLevelTraverse(path+"/"+val)...) + } else { + // file + leaf := strings.ReplaceAll("/"+path+"/"+val, "//", "/") + result = append(result, leaf) + } } } } else { diff --git a/main.go b/main.go index 6d89da9a..77d39d35 100644 --- a/main.go +++ b/main.go @@ -16,30 +16,6 @@ import ( var vaultClient *client.Client -type commands struct { - mv *cli.MoveCommand - cp *cli.CopyCommand - append *cli.AppendCommand - rm *cli.RemoveCommand - ls *cli.ListCommand - cd *cli.CdCommand - cat *cli.CatCommand - grep *cli.GrepCommand -} - -func newCommands(client *client.Client) *commands { - return &commands{ - mv: cli.NewMoveCommand(client), - cp: cli.NewCopyCommand(client), - append: cli.NewAppendCommand(client), - rm: cli.NewRemoveCommand(client), - ls: cli.NewListCommand(client), - cd: cli.NewCdCommand(client), - cat: cli.NewCatCommand(client), - grep: cli.NewGrepCommand(client, os.Stdout, os.Stderr), - } -} - var ( vshVersion = "" verbosity = "INFO" @@ -65,7 +41,7 @@ func executor(in string) { // Split the input separate the command and the arguments. in = strings.TrimSpace(in) args := parseInput(in) - commands := newCommands(vaultClient) + commands := cli.NewCommands(vaultClient) var cmd cli.Command var err error @@ -105,32 +81,32 @@ func executor(in string) { } } -func getCommand(args []string, commands *commands) (cmd cli.Command, err error) { +func getCommand(args []string, commands *cli.Commands) (cmd cli.Command, err error) { switch args[0] { - case commands.ls.GetName(): - err = commands.ls.Parse(args) - cmd = commands.ls - case commands.cd.GetName(): - err = commands.cd.Parse(args) - cmd = commands.cd - case commands.mv.GetName(): - err = commands.mv.Parse(args) - cmd = commands.mv - case commands.append.GetName(): - err = commands.append.Parse(args) - cmd = commands.append - case commands.cp.GetName(): - err = commands.cp.Parse(args) - cmd = commands.cp - case commands.rm.GetName(): - err = commands.rm.Parse(args) - cmd = commands.rm - case commands.cat.GetName(): - err = commands.cat.Parse(args) - cmd = commands.cat - case commands.grep.GetName(): - err = commands.grep.Parse(args) - cmd = commands.grep + case commands.Ls.GetName(): + err = commands.Ls.Parse(args) + cmd = commands.Ls + case commands.Cd.GetName(): + err = commands.Cd.Parse(args) + cmd = commands.Cd + case commands.Mv.GetName(): + err = commands.Mv.Parse(args) + cmd = commands.Mv + case commands.Append.GetName(): + err = commands.Append.Parse(args) + cmd = commands.Append + case commands.Cp.GetName(): + err = commands.Cp.Parse(args) + cmd = commands.Cp + case commands.Rm.GetName(): + err = commands.Rm.Parse(args) + cmd = commands.Rm + case commands.Cat.GetName(): + err = commands.Cat.Parse(args) + cmd = commands.Cat + case commands.Grep.GetName(): + err = commands.Grep.Parse(args) + cmd = commands.Grep default: log.UserError("Not a valid command: %s", args[0]) return nil, fmt.Errorf("not a valid command") diff --git a/test/suites/commands/cp.bats b/test/suites/commands/cp.bats index 3033715b..3727245f 100644 --- a/test/suites/commands/cp.bats +++ b/test/suites/commands/cp.bats @@ -141,7 +141,7 @@ load ../../bin/plugins/bats-assert/load assert_output "3" ####################################### - echo "==== case: copy ambigious directory ====" + echo "==== case: copy ambiguous directory ====" run ${APP_BIN} -c "cp ${KV_BACKEND}/src/staging/all/ ${KV_BACKEND}/dest/staging/all/" assert_success @@ -161,13 +161,13 @@ load ../../bin/plugins/bats-assert/load assert_success assert_output "v2" - echo "ensure the ambigious file still exists" + echo "ensure the ambiguous file still exists" run get_vault_value "value" "${KV_BACKEND}/src/staging/all" assert_success assert_output "all" ####################################### - echo "==== case: copy ambigious file ====" + echo "==== case: copy ambiguous file ====" run ${APP_BIN} -c "cp ${KV_BACKEND}/src/tooling ${KV_BACKEND}/dest/tooling" assert_success @@ -181,7 +181,7 @@ load ../../bin/plugins/bats-assert/load assert_success assert_output "tooling" - echo "ensure the ambigious directory still exists" + echo "ensure the ambiguous directory still exists" run get_vault_value "value" "${KV_BACKEND}/src/tooling/v1" assert_success assert_output "v1" @@ -189,4 +189,17 @@ load ../../bin/plugins/bats-assert/load run get_vault_value "value" "${KV_BACKEND}/src/tooling/v2" assert_success assert_output "v2" + + ####################################### + echo "==== case: copy ambiguous directory parent ====" + run ${APP_BIN} -c "cp ${KV_BACKEND}/src/staging/ ${KV_BACKEND}/dest/staging5/" + assert_success + + echo "ensure ambiguous file got copied" + run get_vault_value "value" "${KV_BACKEND}/dest/staging5/all" + assert_success + assert_output "all" + run get_vault_value "value" "${KV_BACKEND}/dest/staging/all/v2" + assert_success + assert_output "v2" }