Skip to content

Commit 8f442f0

Browse files
committed
feat: Allow of disabling of recursive traversal of directories
The layout of our Kubernetes manifests are such that traversing down each subdirectory may not be desired. This allows us to opt-out of this behaviour. Signed-off-by: Adrian Moisey <adrian@changeover.za.net>
1 parent b046a7d commit 8f442f0

File tree

7 files changed

+216
-4
lines changed

7 files changed

+216
-4
lines changed

cmd/generate.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
func NewGenerateCommand() *cobra.Command {
2121
const StdIn = "-"
2222
var configPath, secretName string
23+
var disableRecursive bool
2324
var verboseOutput bool
2425
var disableCache bool
2526

@@ -43,7 +44,7 @@ func NewGenerateCommand() *cobra.Command {
4344
return err
4445
}
4546
} else {
46-
files, err := listFiles(path)
47+
files, err := listFiles(path, disableRecursive)
4748
if len(files) < 1 {
4849
return fmt.Errorf("no YAML or JSON files were found in %s", path)
4950
}
@@ -119,5 +120,6 @@ func NewGenerateCommand() *cobra.Command {
119120
command.Flags().StringVarP(&secretName, "secret-name", "s", "", "name of a Kubernetes Secret in the argocd namespace containing Vault configuration data in the argocd namespace of your ArgoCD host (Only available when used in ArgoCD). The namespace can be overridden by using the format <namespace>:<name>")
120121
command.Flags().BoolVar(&verboseOutput, "verbose-sensitive-output", false, "enable verbose mode for detailed info to help with debugging. Includes sensitive data (credentials), logged to stderr")
121122
command.Flags().BoolVar(&disableCache, "disable-token-cache", false, "disable the automatic token cache feature that store tokens locally")
123+
command.Flags().BoolVar(&disableRecursive, "disable-recursive", false, "disable resursive path traversal")
122124
return command
123125
}

cmd/generate_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,71 @@ func TestMain(t *testing.T) {
300300
}
301301
})
302302

303+
t.Run("will not recurse down directories if disabled", func(t *testing.T) {
304+
args := []string{
305+
"--disable-recursive",
306+
"../fixtures/input/nonempty-recursive",
307+
}
308+
cmd := NewGenerateCommand()
309+
310+
b := bytes.NewBufferString("")
311+
e := bytes.NewBufferString("")
312+
cmd.SetArgs(args)
313+
cmd.SetOut(b)
314+
cmd.SetErr(e)
315+
cmd.Execute()
316+
out, err := io.ReadAll(b) // Read buffer to bytes
317+
if err != nil {
318+
t.Fatal(err)
319+
}
320+
stderr, err := io.ReadAll(e) // Read buffer to bytes
321+
if err != nil {
322+
t.Fatal(err)
323+
}
324+
325+
buf, err := os.ReadFile("../fixtures/output/secret-recursive-disabled.yaml")
326+
if err != nil {
327+
t.Fatal(err)
328+
}
329+
330+
expected := string(buf)
331+
if string(out) != expected {
332+
t.Fatalf("expected:\n\n%s\nbut got:\n\n%s\nerr: %s", expected, string(out), string(stderr))
333+
}
334+
})
335+
336+
t.Run("will recurse down directories if unset", func(t *testing.T) {
337+
args := []string{
338+
"../fixtures/input/nonempty-recursive",
339+
}
340+
cmd := NewGenerateCommand()
341+
342+
b := bytes.NewBufferString("")
343+
e := bytes.NewBufferString("")
344+
cmd.SetArgs(args)
345+
cmd.SetOut(b)
346+
cmd.SetErr(e)
347+
cmd.Execute()
348+
out, err := io.ReadAll(b) // Read buffer to bytes
349+
if err != nil {
350+
t.Fatal(err)
351+
}
352+
stderr, err := io.ReadAll(e) // Read buffer to bytes
353+
if err != nil {
354+
t.Fatal(err)
355+
}
356+
357+
buf, err := os.ReadFile("../fixtures/output/secret-recursive-enabled.yaml")
358+
if err != nil {
359+
t.Fatal(err)
360+
}
361+
362+
expected := string(buf)
363+
if string(out) != expected {
364+
t.Fatalf("expected:\n\n%s\nbut got:\n\n%s\nerr: %s", expected, string(out), string(stderr))
365+
}
366+
})
367+
303368
os.Unsetenv("AVP_TYPE")
304369
os.Unsetenv("VAULT_ADDR")
305370
os.Unsetenv("AVP_AUTH_TYPE")

cmd/util.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,26 @@ import (
1111
k8yaml "k8s.io/apimachinery/pkg/util/yaml"
1212
)
1313

14-
func listFiles(root string) ([]string, error) {
14+
func listFiles(root string, disableRecursive bool) ([]string, error) {
1515
var files []string
1616

17-
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
17+
walkFn := func(path string, info os.FileInfo, err error) error {
18+
if err != nil {
19+
return fmt.Errorf("error accessing path %s: %w", path, err)
20+
}
21+
if info.IsDir() {
22+
if disableRecursive && path != root {
23+
return filepath.SkipDir
24+
}
25+
return nil
26+
}
1827
if filepath.Ext(path) == ".yaml" || filepath.Ext(path) == ".yml" || filepath.Ext(path) == ".json" {
1928
files = append(files, path)
2029
}
2130
return nil
22-
})
31+
}
32+
33+
err := filepath.Walk(root, walkFn)
2334
if err != nil {
2435
return files, err
2536
}

cmd/util_test.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package cmd
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"reflect"
7+
"sort"
8+
"testing"
9+
)
10+
11+
// Helper to create files and directories for testing
12+
func createTestFiles(t *testing.T, root string, files map[string]string) {
13+
for path, content := range files {
14+
fullPath := filepath.Join(root, path)
15+
dir := filepath.Dir(fullPath)
16+
if err := os.MkdirAll(dir, 0o755); err != nil {
17+
t.Fatalf("failed to create dir %s: %v", dir, err)
18+
}
19+
if err := os.WriteFile(fullPath, []byte(content), 0o644); err != nil {
20+
t.Fatalf("failed to write file %s: %v", fullPath, err)
21+
}
22+
}
23+
}
24+
25+
func TestListFiles_NonRecursive(t *testing.T) {
26+
tmpDir := t.TempDir()
27+
files := map[string]string{
28+
"file1.yaml": "a: b",
29+
"file2.yml": "c: d",
30+
"file3.json": "{}",
31+
"file4.txt": "not yaml",
32+
"subdir/file5.yaml": "e: f",
33+
}
34+
createTestFiles(t, tmpDir, files)
35+
36+
got, err := listFiles(tmpDir, true)
37+
if err != nil {
38+
t.Fatalf("unexpected error: %v", err)
39+
}
40+
41+
want := []string{
42+
filepath.Join(tmpDir, "file1.yaml"),
43+
filepath.Join(tmpDir, "file2.yml"),
44+
filepath.Join(tmpDir, "file3.json"),
45+
}
46+
sort.Strings(got)
47+
sort.Strings(want)
48+
if !reflect.DeepEqual(got, want) {
49+
t.Errorf("expected %v, got %v", want, got)
50+
}
51+
}
52+
53+
func TestListFiles_Recursive(t *testing.T) {
54+
tmpDir := t.TempDir()
55+
files := map[string]string{
56+
"file1.yaml": "a: b",
57+
"file2.yml": "c: d",
58+
"file3.json": "{}",
59+
"file4.txt": "not yaml",
60+
"subdir/file5.yaml": "e: f",
61+
"subdir/file6.json": "{}",
62+
"subdir2/file7.yml": "g: h",
63+
"subdir2/file8.txt": "not yaml",
64+
}
65+
createTestFiles(t, tmpDir, files)
66+
67+
got, err := listFiles(tmpDir, false)
68+
if err != nil {
69+
t.Fatalf("unexpected error: %v", err)
70+
}
71+
72+
want := []string{
73+
filepath.Join(tmpDir, "file1.yaml"),
74+
filepath.Join(tmpDir, "file2.yml"),
75+
filepath.Join(tmpDir, "file3.json"),
76+
filepath.Join(tmpDir, "subdir", "file5.yaml"),
77+
filepath.Join(tmpDir, "subdir", "file6.json"),
78+
filepath.Join(tmpDir, "subdir2", "file7.yml"),
79+
}
80+
sort.Strings(got)
81+
sort.Strings(want)
82+
if !reflect.DeepEqual(got, want) {
83+
t.Errorf("expected %v, got %v", want, got)
84+
}
85+
}
86+
87+
func TestListFiles_Error(t *testing.T) {
88+
// Non-existent directory should return an error
89+
_, err := listFiles("/non/existent/dir", false)
90+
if err == nil {
91+
t.Error("expected error for non-existent directory, got nil")
92+
}
93+
}

docs/cmd/generate.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ argocd-vault-plugin generate PATH [flags]
77
### Options
88
```
99
-c, --config-path string path to a file containing Vault configuration (YAML, JSON, envfile) to use
10+
--disable-recursive disable resursive path traversal
11+
--disable-token-cache disable the automatic token cache feature that store tokens locally
1012
-h, --help help for generate
1113
-s, --secret-name string name of a Kubernetes Secret in the argocd namespace containing Vault configuration data in the argocd namespace of your ArgoCD host (Only available when used in ArgoCD). The namespace can be overridden by using the format <namespace>:<name>
1214
--verbose-sensitive-output enable verbose mode for detailed info to help with debugging. Includes sensitive data (credentials), logged to stderr
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
apiVersion: v1
2+
data:
3+
SECRET_NUM: MQ==
4+
SECRET_VAR: dGVzdC1wYXNzd29yZA==
5+
kind: Secret
6+
metadata:
7+
annotations:
8+
avp.kubernetes.io/kv-version: "1"
9+
avp.kubernetes.io/path: secret/testing
10+
name: test-name
11+
namespace: test-namespace
12+
type: Opaque
13+
---
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
apiVersion: v1
2+
data:
3+
SECRET_NUM: MQ==
4+
SECRET_VAR: dGVzdC1wYXNzd29yZA==
5+
kind: Secret
6+
metadata:
7+
annotations:
8+
avp.kubernetes.io/kv-version: "1"
9+
avp.kubernetes.io/path: secret/testing
10+
name: test-name
11+
namespace: test-namespace
12+
type: Opaque
13+
---
14+
apiVersion: v1
15+
data:
16+
SECRET_NUM: MQ==
17+
SECRET_VAR: dGVzdC1wYXNzd29yZA==
18+
kind: Secret
19+
metadata:
20+
annotations:
21+
avp.kubernetes.io/kv-version: "1"
22+
avp.kubernetes.io/path: secret/testing
23+
name: test-name
24+
namespace: test-namespace
25+
type: Opaque
26+
---

0 commit comments

Comments
 (0)