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(export): add --export-upper and --export-include-path options #271

Closed
wants to merge 1 commit into from
Closed
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
13 changes: 11 additions & 2 deletions cmd/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ type exportOptions struct {

SkipErrors bool `env:"SKIP_ERRORS" envDefault:"false"`

ExportIncludePath bool `env:"EXPORT_INCLUDE_PATH"`
ExportUpper bool `env:"EXPORT_UPPER"`

TemplateFile string `env:"TEMPLATE_FILE"`
TemplateString string `env:"TEMPLATE_STRING"`

Expand Down Expand Up @@ -55,18 +58,20 @@ func NewExportCmd() *cobra.Command {
enginePath, _ := utils.HandleEnginePath(o.EnginePath, o.Path)

printer = prt.NewSecretPrinter(
prt.WithEnginePath(enginePath),
prt.OnlyKeys(o.OnlyKeys),
prt.OnlyPaths(o.OnlyPaths),
prt.CustomValueLength(o.MaxValueLength),
prt.ShowValues(o.ShowValues),
prt.WithTemplate(o.TemplateString, o.TemplateFile),
prt.ToFormat(o.outputFormat),
prt.WithVaultClient(vaultClient),
prt.WithWriter(writer),
prt.ShowVersion(o.ShowVersion),
prt.ShowMetadata(o.ShowMetadata),
prt.WithHyperLinks(o.WithHyperLink),
prt.WithEnginePath(enginePath),
prt.WithTemplate(o.TemplateString, o.TemplateFile),
prt.WithExportIncludePath(o.ExportIncludePath),
prt.WithExportUpper(o.ExportUpper),
)

// prepare map
Expand Down Expand Up @@ -101,6 +106,10 @@ func NewExportCmd() *cobra.Command {
cmd.Flags().IntVar(&o.MaxValueLength, "max-value-length", o.MaxValueLength, "maximum char length of values. Set to \"-1\" for disabling "+
"(env: VKV_EXPORT_MAX_VALUE_LENGTH)")

// Export
cmd.Flags().BoolVar(&o.ExportIncludePath, "export-include-path", o.ExportIncludePath, "include the secret path as the env var prefix in format export (env: VKV_EXPORT_EXPORT_INCLUDE_PATH)")
cmd.Flags().BoolVar(&o.ExportUpper, "export-upper", o.ExportUpper, "upper case the env var names (env: VKV_EXPORT_UPPER)")

// Template
cmd.Flags().StringVar(&o.TemplateFile, "template-file", o.TemplateFile, "path to a file containing Go-template syntax to render the KV entries (env: VKV_EXPORT_TEMPLATE_FILE)")
cmd.Flags().StringVar(&o.TemplateString, "template-string", o.TemplateString, "template string containing Go-template syntax to render KV entries (env: VKV_EXPORT_TEMPLATE_STRING)")
Expand Down
29 changes: 24 additions & 5 deletions docs/example_direnv.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,37 @@ You can use `vkv` and [`direnv`](https://direnv.net/) to autimatically source KV
## Demo

Create in a project a `.envrc` file:
```bash
export VAULT_ADDR="https://vault:8200"
export VAULT_TOKEN="$(cat ~/.vault-token)"

eval $(vkv export -p kv/secrets -f export)
```bash
> export VAULT_ADDR="https://vault:8200"
> export VAULT_TOKEN="$(cat ~/.vault-token)"
> eval $(vkv export -p kv/secrets -f export)
```

Now if you go into that directory and run `direnv allow`,
you have the secrets under `kv/secrets` exported as env various:

```bash
env | grep OS_
> env | grep OS_
OS_USER=admin
OS_PASSWORD=pasword
```

## Upper Case the env var keys
`--export-upper` will upper-case all env var keys:

```bash
> vkv export -p secret/sub/demo -f export --export-upper
export DEMO='hello world'
export PASSWORD='s3cre5<'
```

## Include paths as env var key prefix
`--export-include-path` will prefix the secrets path and use `_` as the delimiter:

```bash
> vkv export -p secret/sub/demo -f export --export-upper --export-include-path
export SECRET_SUB_DEMO_DEMO='hello world'
export SECRET_SUB_DEMO_PASSWORD='s3cre5<'
export SECRET_SUB_DEMO_USER='admin'
```
33 changes: 19 additions & 14 deletions pkg/printer/secret/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package secret

import (
"fmt"
"strings"

"github.com/FalcoSuessgott/vkv/pkg/utils"
)
Expand All @@ -15,24 +16,28 @@ var exportMap map[string]interface{}
func (p *Printer) printExport(secrets map[string]interface{}) error {
exportMap = make(map[string]interface{})

buildExport(secrets)
utils.TransformMap("", secrets, &exportMap)

for _, k := range utils.SortMapKeys(exportMap) {
fmt.Fprintf(p.writer, exportFmtString, k, exportMap[k])
}
for _, path := range utils.SortMapKeys(exportMap) {
secrets, ok := exportMap[path].(map[string]interface{})
if !ok {
return fmt.Errorf("cannot convert %v to map[string]interface{}", exportMap[path])
}

return nil
}
for _, secretName := range utils.SortMapKeys(secrets) {
envKey := secretName

func buildExport(secrets map[string]interface{}) {
for _, v := range secrets {
m, ok := v.(map[string]interface{})
if ok {
buildExport(m)
} else {
for k, v := range secrets {
exportMap[k] = v
if p.includePath {
envKey = strings.ReplaceAll(fmt.Sprintf("%s/%s", path, envKey), "/", "_")
}

if p.upper {
envKey = strings.ToUpper(envKey)
}

fmt.Fprintf(p.writer, exportFmtString, envKey, secrets[secretName])
}
}

return nil
}
34 changes: 34 additions & 0 deletions pkg/printer/secret/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,40 @@ export user='password'
},
output: "",
},
{
name: "upper",
s: map[string]interface{}{
"secret": map[string]interface{}{
"key": "value",
"user": "password",
},
}, opts: []Option{
ToFormat(Export),
ShowValues(true),
WithExportUpper(true),
},
output: `export KEY='value'
export USER='password'
`,
},
{
name: "include path",
rootPath: "root",
s: map[string]interface{}{
"secret": map[string]interface{}{
"key": "value",
"user": "password",
},
}, opts: []Option{
ToFormat(Export),
ShowValues(true),
WithExportUpper(true),
WithExportIncludePath(true),
},
output: `export ROOT_SECRET_KEY='value'
export ROOT_SECRET_USER='password'
`,
},
}

for _, tc := range testCases {
Expand Down
23 changes: 21 additions & 2 deletions pkg/printer/secret/secret_printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,13 @@ type Printer struct {
showMetadata bool
withHyperLinks bool
valueLength int
template string
vaultClient *vault.Vault

template string

includePath bool
upper bool

vaultClient *vault.Vault
}

// CustomValueLength option for trimming down the output of secrets.
Expand Down Expand Up @@ -166,6 +171,20 @@ func WithTemplate(str, path string) Option {
}
}

// WithExportUpper sets the export keys to uppercase.
func WithExportUpper(b bool) Option {
return func(p *Printer) {
p.upper = b
}
}

// WithExportIncludePath sets the export keys to include the path.
func WithExportIncludePath(b bool) Option {
return func(p *Printer) {
p.includePath = b
}
}

// WithVaultClient inject a vault client.
func WithVaultClient(v *vault.Vault) Option {
return func(p *Printer) {
Expand Down
Loading