aboutsummaryrefslogtreecommitdiff
path: root/flagfolder/flagfolder.go
diff options
context:
space:
mode:
authorXe Iaso <me@xeiaso.net>2024-05-17 07:20:23 -0500
committerXe Iaso <me@xeiaso.net>2024-05-17 07:28:19 -0500
commit7ce28bfc8ac4f306196bacbc4ff06671e8a58654 (patch)
treeac0b266643e83333257e9bf5c04df6e425b0445d /flagfolder/flagfolder.go
parentf22aa000db6e91f0fb59f4728bdbda0aa40f475d (diff)
downloadx-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.go110
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)
+ }
+ }
+}