diff options
| author | Xe Iaso <me@xeiaso.net> | 2024-05-17 07:20:23 -0500 |
|---|---|---|
| committer | Xe Iaso <me@xeiaso.net> | 2024-05-17 07:28:19 -0500 |
| commit | 7ce28bfc8ac4f306196bacbc4ff06671e8a58654 (patch) | |
| tree | ac0b266643e83333257e9bf5c04df6e425b0445d /flagfolder/flagfolder.go | |
| parent | f22aa000db6e91f0fb59f4728bdbda0aa40f475d (diff) | |
| download | x-7ce28bfc8ac4f306196bacbc4ff06671e8a58654.tar.xz x-7ce28bfc8ac4f306196bacbc4ff06671e8a58654.zip | |
internal: add package flagfolder to populate FlagSets with a secret mount
Signed-off-by: Xe Iaso <me@xeiaso.net>
Diffstat (limited to 'flagfolder/flagfolder.go')
| -rw-r--r-- | flagfolder/flagfolder.go | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/flagfolder/flagfolder.go b/flagfolder/flagfolder.go new file mode 100644 index 0000000..cb0d249 --- /dev/null +++ b/flagfolder/flagfolder.go @@ -0,0 +1,110 @@ +// Package flagfolder parses a folder on the disk as if each file in it had the contents of a command line flag. +// +// This is mainly intended to be used with environments like Kubernetes where you have your secrets mounted as a filesystem. +package flagfolder + +import ( + "flag" + "fmt" + "log/slog" + "os" + "path/filepath" + + "github.com/stoewer/go-strcase" +) + +// ParseSet parses secrets in a single folder into the given *flag.FlagSet. +// +// By default this will attempt to correct for several styles of naming for files: +// +// * kebab-case (the default) +// * SHOUTING-KEBAB-CASE +// * snake_case +// * SHOUTING_SNAKE_CASE +// * camelCase +// * HammerCase +func ParseSet(secretLocation string, set *flag.FlagSet) error { + var ( + data []byte + err error + ) + + set.VisitAll(func(f *flag.Flag) { + if err != nil { + return + } + + for _, fname := range []string{ + filepath.Join(secretLocation, f.Name), + filepath.Join(secretLocation, strcase.UpperKebabCase(f.Name)), + filepath.Join(secretLocation, strcase.LowerCamelCase(f.Name)), + filepath.Join(secretLocation, strcase.UpperCamelCase(f.Name)), + filepath.Join(secretLocation, strcase.SnakeCase(f.Name)), + filepath.Join(secretLocation, strcase.UpperSnakeCase(f.Name)), + } { + data, err = os.ReadFile(fname) + if err != nil { + slog.Debug("can't read", "fname", fname, "err", err) + if os.IsNotExist(err) { + err = nil + continue + } + continue + } + } + + if ferr := f.Value.Set(string(data)); ferr != nil { + err = fmt.Errorf("flagfolder: failed to set flag %q with value %q", f.Name, string(data)) + } + }) + + return err +} + +// Parse parses all files in every folder under /run/secrets as if they were command-line flags. +// +// This is most useful when you are using environments like Kubernetes where the path of least resistance +// is to mount your secrets as a filesystem. Mount all your secrets into the pod and then let it figure +// itself out! +// +// To use this effectively, ensure that your Pods and Deployments mount secrets as volumes like this: +// +// volumes: +// - name: secret-volume +// secret: +// secretName: shell +// containers: +// - name: shell +// image: ubuntu:latest +// volumeMounts: +// - name: secret-volume +// readOnly: true +// mountPath: "/run/secrets/shell" +// +// By default this will attempt to correct for several styles of naming for files: +// +// * kebab-case (the default) +// * SHOUTING-KEBAB-CASE +// * snake_case +// * SHOUTING_SNAKE_CASE +// * camelCase +// * HammerCase +func Parse() { + stats, err := os.ReadDir("/run/secrets") + if err != nil { + slog.Debug("can't read from /run/secrets", "err", err) + return + } + + for _, stat := range stats { + if !stat.IsDir() { + continue + } + + loc := filepath.Join("/run/secrets", stat.Name()) + + if err := ParseSet(loc, flag.CommandLine); err != nil { + slog.Error("can't parse folder", "folder", loc, "err", err) + } + } +} |
