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

feat(import): support engine path #280

Merged
merged 1 commit into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
54 changes: 10 additions & 44 deletions cmd/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
SilenceErrors: true,
PreRunE: o.validateFlags,
RunE: func(cmd *cobra.Command, args []string) error {
enginePath, _ := utils.HandleEnginePath(o.EnginePath, o.Path)
enginePath, subPath := utils.HandleEnginePath(o.EnginePath, o.Path)

printer = prt.NewSecretPrinter(
prt.OnlyKeys(o.OnlyKeys),
Expand All @@ -66,16 +66,17 @@
prt.ShowVersion(o.ShowVersion),
prt.ShowMetadata(o.ShowMetadata),
prt.WithHyperLinks(o.WithHyperLink),
prt.WithEnginePath(enginePath),
prt.WithEnginePath(utils.NormalizePath(enginePath)),
)

// prepare map
m, err := o.buildMap()
secrets, err := vaultClient.ListRecursive(enginePath, subPath, o.SkipErrors)
if err != nil {
return err
}

if err := printer.Out(m); err != nil {
result := utils.UnflattenMap(utils.NormalizePath(path.Join(enginePath, subPath)), utils.ToMapStringInterface(secrets), o.EnginePath)

if err := printer.Out(result); err != nil {
return err
}

Expand All @@ -86,9 +87,9 @@
cmd.Flags().SortFlags = false

// Input
cmd.Flags().StringVarP(&o.Path, "path", "p", o.Path, fmt.Sprintf("KV Engine path (env: %s)", envVarExportPrefix+"_PATH"))
cmd.Flags().StringVarP(&o.EnginePath, "engine-path", "e", o.EnginePath, "engine path in case your KV-engine contains special characters such as \"/\", the path value will then be appended if specified (\"<engine-path>/<path>\") (env: VKV_EXPORT_ENGINE_PATH)")
cmd.Flags().BoolVar(&o.SkipErrors, "skip-errors", o.SkipErrors, "dont exit on errors (permission denied, deleted secrets) (env: VKV_EXPORT_SKIP_ERRORS)")
cmd.Flags().StringVarP(&o.Path, "path", "p", o.Path, "KV Engine path (env: VKV_EXPORT_PATH")
cmd.Flags().StringVarP(&o.EnginePath, "engine-path", "e", o.EnginePath, "engine path in case your KV-engine contains special characters such as \"/\", the path (-p) flag will then be appended if specified (\"<engine-path>/<path>\") (env: VKV_EXPORT_ENGINE_PATH)")
cmd.Flags().BoolVar(&o.SkipErrors, "skip-errors", o.SkipErrors, "don't exit on errors (permission denied, deleted secrets) (env: VKV_EXPORT_SKIP_ERRORS)")

// Modify
cmd.Flags().BoolVar(&o.OnlyKeys, "only-keys", o.OnlyKeys, "show only keys (env: VKV_EXPORT_ONLY_KEYS)")
Expand Down Expand Up @@ -119,9 +120,7 @@
case (o.OnlyKeys && o.ShowValues), (o.OnlyPaths && o.ShowValues), (o.OnlyKeys && o.OnlyPaths):
return errInvalidFlagCombination
case o.EnginePath == "" && o.Path == "":
return errors.New("no KV-paths given. Either --engine-path / -e or --path / -p needs to be specified")
case o.EnginePath != "" && o.Path != "":
return errors.New("cannot specify both engine-path and path")
return errors.New("no KV-paths given. Either --engine-path/-e or --path/-p needs to be specified")

Check warning on line 123 in cmd/export.go

View check run for this annotation

Codecov / codecov/patch

cmd/export.go#L123

Added line #L123 was not covered by tests
case true:
switch strings.ToLower(o.FormatString) {
case "yaml", "yml":
Expand Down Expand Up @@ -172,36 +171,3 @@

return nil
}

func (o *exportOptions) buildMap() (map[string]interface{}, error) {
var isSecretPath bool

rootPath, subPath := utils.HandleEnginePath(o.EnginePath, o.Path)

// read recursive all secrets
s, err := vaultClient.ListRecursive(rootPath, subPath, o.SkipErrors)
if err != nil {
return nil, err
}

// check if path is a directory or secret path
if _, isSecret := vaultClient.ReadSecrets(rootPath, subPath); isSecret == nil {
isSecretPath = true
}

path := path.Join(rootPath, subPath)
if o.EnginePath != "" {
path = subPath
}

// prepare the output map
pathMap := utils.PathMap(path, utils.ToMapStringInterface(s), isSecretPath)

if o.EnginePath != "" {
return map[string]interface{}{
o.EnginePath: pathMap,
}, nil
}

return pathMap, nil
}
95 changes: 61 additions & 34 deletions cmd/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bytes"
"io"

"github.com/FalcoSuessgott/vkv/pkg/fs"
prt "github.com/FalcoSuessgott/vkv/pkg/printer/secret"
)

Expand All @@ -15,16 +14,6 @@ func (s *VaultSuite) TestValidateExportFlags() {
opts *importOptions
err bool
}{
{
name: "path and engine path mutually exclusive",
args: []string{"--path=p", "--engine-path=e"},
err: true,
},
{
name: "path or engine path required",
args: []string{"--show-values"},
err: true,
},
{
name: "only keys and only paths mutually exclusive",
args: []string{"-p=1", "--only-keys", "--only-paths"},
Expand Down Expand Up @@ -55,33 +44,72 @@ func (s *VaultSuite) TestValidateExportFlags() {
func (s *VaultSuite) TestExportImportCommand() {
testCases := []struct {
name string
path string
expected string
err bool
importCmdArgs []string
exportCmdArgs []string
}{
{
name: "yaml",
path: "yaml",
importCmdArgs: []string{"-f=testdata/1.yaml", "-p=yaml"},
name: "import secrets, export from path",
importCmdArgs: []string{"-f=testdata/1.yaml"},
exportCmdArgs: []string{"-p=yaml", "-f=yaml", "--show-values"},
expected: "testdata/1.yaml",
},
{
name: "json",
path: "json",
importCmdArgs: []string{"-f=testdata/2.json", "-p=json"},
exportCmdArgs: []string{"-p=json", "-f=json", "--show-values"},
expected: "testdata/2.json",
expected: `yaml/:
secret:
user: password
`,
},
{
name: "import secrets, overwrite path, read from path",
importCmdArgs: []string{"-f=testdata/1.yaml", "-p=yaml2"},
exportCmdArgs: []string{"-p=yaml2", "-f=yaml", "--show-values"},
expected: `yaml2/:
secret:
user: password
`,
},
{
name: "import secrets, overwrite path and subpath, read from path",
importCmdArgs: []string{"-f=testdata/1.yaml", "-p=yaml2/sub"},
exportCmdArgs: []string{"-p=yaml2", "-f=yaml", "--show-values"},
expected: `yaml2/:
sub/:
secret:
user: password
`,
},
{
name: "import secrets, overwrite path with engine path, export from engine path",
importCmdArgs: []string{"-f=testdata/2.json", "-e=engine/path"},
exportCmdArgs: []string{"-e=engine/path", "-f=json", "--show-values"},
expected: `{
"engine/path/": {
"admin": {
"sub": "password"
}
}
}
`,
},
{
name: "import secrets, overwrite path with engine path and subpath, export from engine path",
importCmdArgs: []string{"-f=testdata/2.json", "-e=engine/path", "-p=sub"},
exportCmdArgs: []string{"-e=engine/path", "-f=json", "--show-values"},
expected: `{
"engine/path/": {
"sub/": {
"admin": {
"sub": "password"
}
}
}
}
`,
},
{
name: "dryrun",
path: "json",
name: "no output, error, dryrun",
err: true,
importCmdArgs: []string{"-f=testdata/2.json", "-d", "-p=json"},
exportCmdArgs: []string{"-p=json", "-f=json", "--show-values"},
expected: "testdata/2.json",
},
}

Expand All @@ -94,9 +122,9 @@ func (s *VaultSuite) TestExportImportCommand() {
importCmd := NewImportCmd()
importCmd.SetArgs(tc.importCmdArgs)

s.Require().NoError(importCmd.Execute(), ("import " + tc.name))
s.Require().NoError(importCmd.Execute(), "import "+tc.name)

// 2. read secrets and assert
// 2. read secrets, capture output and assert
b := bytes.NewBufferString("")
writer = b

Expand All @@ -105,14 +133,13 @@ func (s *VaultSuite) TestExportImportCommand() {

err := exportCmd.Execute()

s.Require().Equal(tc.err, err != nil, "export "+tc.name)

// if no error - compare exported secretd with expected value
if !tc.err {
if tc.err {
s.Require().Error(err, "export "+tc.name)
} else {
// if no error - compare exported secrets with expected value
out, _ := io.ReadAll(b)
exp, _ := fs.ReadFile(tc.expected)

s.Require().Equal(string(exp), string(out), "secrets "+tc.name)
s.Require().Equal(tc.expected, string(out), "secrets "+tc.name)
}
})
}
Expand Down
Loading
Loading