Skip to content

Commit f1e31af

Browse files
committed
wip: vaults v2
1 parent 926c2ad commit f1e31af

File tree

10 files changed

+268
-1
lines changed

10 files changed

+268
-1
lines changed

.github/dependabot.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ updates:
2121
charmbracelet-deps:
2222
patterns:
2323
- "github.com/charmbracelet/*"
24+
koanf-deps:
25+
patterns:
26+
- "github.com/knadh/koanf/*"
2427
- package-ecosystem: "github-actions"
2528
directory: "/"
2629
schedule:

go.mod

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ require (
1313
github.com/jahvon/glamour v0.8.1-patch3
1414
github.com/jahvon/open-golang v0.0.0-20240522004812-68511c3bc9ef
1515
github.com/jahvon/tuikit v0.0.27
16+
github.com/knadh/koanf/parsers/json v0.1.0
17+
github.com/knadh/koanf/parsers/toml v0.1.0
18+
github.com/knadh/koanf/parsers/yaml v0.1.0
19+
github.com/knadh/koanf/providers/env v1.0.0
20+
github.com/knadh/koanf/providers/file v1.1.2
21+
github.com/knadh/koanf/v2 v2.1.2
1622
github.com/mattn/go-runewidth v0.0.16
1723
github.com/onsi/ginkgo/v2 v2.22.2
1824
github.com/onsi/gomega v1.36.2
@@ -46,17 +52,20 @@ require (
4652
github.com/dlclark/regexp2 v1.11.5 // indirect
4753
github.com/dustin/go-humanize v1.0.1 // indirect
4854
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
55+
github.com/fsnotify/fsnotify v1.8.0 // indirect
4956
github.com/go-logfmt/logfmt v0.6.0 // indirect
5057
github.com/go-logr/logr v1.4.2 // indirect
5158
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
5259
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 // indirect
60+
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
5361
github.com/godbus/dbus/v5 v5.1.0 // indirect
5462
github.com/google/go-cmp v0.6.0 // indirect
5563
github.com/google/pprof v0.0.0-20250208200701-d0013a598941 // indirect
5664
github.com/google/uuid v1.6.0 // indirect
5765
github.com/gorilla/css v1.0.1 // indirect
5866
github.com/huandu/xstrings v1.5.0 // indirect
5967
github.com/inconshreveable/mousetrap v1.1.0 // indirect
68+
github.com/knadh/koanf/maps v0.1.1 // indirect
6069
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
6170
github.com/mattn/go-isatty v0.0.20 // indirect
6271
github.com/mattn/go-localereader v0.0.1 // indirect
@@ -70,6 +79,7 @@ require (
7079
github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a // indirect
7180
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
7281
github.com/otiai10/mint v1.6.3 // indirect
82+
github.com/pelletier/go-toml v1.9.5 // indirect
7383
github.com/rivo/uniseg v0.4.7 // indirect
7484
github.com/russross/blackfriday/v2 v2.1.0 // indirect
7585
github.com/sahilm/fuzzy v0.1.1 // indirect

go.sum

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI
6060
github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
6161
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
6262
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
63+
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
64+
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
6365
github.com/gen2brain/beeep v0.0.0-20240516210008-9c006672e7f4 h1:ygs9POGDQpQGLJPlq4+0LBUmMBNox1N4JSpw+OETcvI=
6466
github.com/gen2brain/beeep v0.0.0-20240516210008-9c006672e7f4/go.mod h1:0W7dI87PvXJ1Sjs0QPvWXKcQmNERY77e8l7GFhZB/s4=
6567
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
@@ -72,6 +74,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
7274
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
7375
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 h1:qZNfIGkIANxGv/OqtnntR4DfOY2+BgwR60cAcu/i3SE=
7476
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4/go.mod h1:kW3HQ4UdaAyrUCSSDR4xUzBKW6O2iA4uHhk7AtyYp10=
77+
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
78+
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
7579
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
7680
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
7781
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
@@ -94,6 +98,20 @@ github.com/jahvon/open-golang v0.0.0-20240522004812-68511c3bc9ef h1:4PS/MNVT6Rsv
9498
github.com/jahvon/open-golang v0.0.0-20240522004812-68511c3bc9ef/go.mod h1:dUmuT5CN6osIeLSRtTPJOf0Yz+qAbcyU6omnCzI+ZfQ=
9599
github.com/jahvon/tuikit v0.0.27 h1:NV+Zzput4twGch+KTwpAOFlR+R/MKtS9DYs3p1sONwg=
96100
github.com/jahvon/tuikit v0.0.27/go.mod h1:zUysbTPUE0tAEB5DfdWvL6AT3g8AZQ+fcwrfUhVXuwA=
101+
github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs=
102+
github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
103+
github.com/knadh/koanf/parsers/json v0.1.0 h1:dzSZl5pf5bBcW0Acnu20Djleto19T0CfHcvZ14NJ6fU=
104+
github.com/knadh/koanf/parsers/json v0.1.0/go.mod h1:ll2/MlXcZ2BfXD6YJcjVFzhG9P0TdJ207aIBKQhV2hY=
105+
github.com/knadh/koanf/parsers/toml v0.1.0 h1:S2hLqS4TgWZYj4/7mI5m1CQQcWurxUz6ODgOub/6LCI=
106+
github.com/knadh/koanf/parsers/toml v0.1.0/go.mod h1:yUprhq6eo3GbyVXFFMdbfZSo928ksS+uo0FFqNMnO18=
107+
github.com/knadh/koanf/parsers/yaml v0.1.0 h1:ZZ8/iGfRLvKSaMEECEBPM1HQslrZADk8fP1XFUxVI5w=
108+
github.com/knadh/koanf/parsers/yaml v0.1.0/go.mod h1:cvbUDC7AL23pImuQP0oRw/hPuccrNBS2bps8asS0CwY=
109+
github.com/knadh/koanf/providers/env v1.0.0 h1:ufePaI9BnWH+ajuxGGiJ8pdTG0uLEUWC7/HDDPGLah0=
110+
github.com/knadh/koanf/providers/env v1.0.0/go.mod h1:mzFyRZueYhb37oPmC1HAv/oGEEuyvJDA98r3XAa8Gak=
111+
github.com/knadh/koanf/providers/file v1.1.2 h1:aCC36YGOgV5lTtAFz2qkgtWdeQsgfxUkxDOe+2nQY3w=
112+
github.com/knadh/koanf/providers/file v1.1.2/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI=
113+
github.com/knadh/koanf/v2 v2.1.2 h1:I2rtLRqXRy1p01m/utEtpZSSA6dcJbgGVuE27kW2PzQ=
114+
github.com/knadh/koanf/v2 v2.1.2/go.mod h1:Gphfaen0q1Fc1HTgJgSTC4oRX9R2R5ErYMZJy8fLJBo=
97115
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
98116
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
99117
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -135,6 +153,8 @@ github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
135153
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
136154
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
137155
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
156+
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
157+
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
138158
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
139159
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
140160
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -190,8 +210,9 @@ golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
190210
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
191211
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
192212
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
193-
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
194213
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
214+
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
215+
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
195216
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
196217
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
197218
mvdan.cc/sh/v3 v3.10.0 h1:v9z7N1DLZ7owyLM/SXZQkBSXcwr2IGMm2LY2pmhVXj4=

internal/services/vault/adapter.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package vault
2+
3+
type Adapter interface {
4+
Get(key string) (string, error)
5+
Set(key string, value string) error
6+
Delete(key string) error
7+
List() ([]string, error)
8+
}

internal/services/vault/config.go

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package vault
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
10+
"github.com/knadh/koanf/parsers/json"
11+
"github.com/knadh/koanf/parsers/toml"
12+
"github.com/knadh/koanf/parsers/yaml"
13+
"github.com/knadh/koanf/providers/env"
14+
"github.com/knadh/koanf/providers/file"
15+
"github.com/knadh/koanf/v2"
16+
)
17+
18+
const (
19+
envPrefix = "FLOW_VAULT_"
20+
vaultConfigFileName = "vaults"
21+
vaultConfigFileExt = ".yaml"
22+
)
23+
24+
type VaultConfig struct {
25+
// Provider specifies the provider's type
26+
Provider string `koanf:"provider"`
27+
// Config specifies the provider-specific configuration
28+
Config map[string]interface{} `koanf:"config"`
29+
}
30+
31+
type Config struct {
32+
// Current specifies the name of thr current vault to use
33+
Current string `koanf:"current"`
34+
// Vaults specifies a map of vault names to their configurations
35+
Vaults map[string]VaultConfig `koanf:"vaults"`
36+
}
37+
38+
type LoadOptions struct {
39+
ConfigPath string
40+
AutoDiscoveryPath string
41+
AllowEnv bool
42+
RequireConfig bool
43+
}
44+
45+
// LoadCOnfig loads the vault configuration from the specified sources
46+
func LoadConfig(opts LoadOptions) (*Config, error) {
47+
k := koanf.New(".")
48+
49+
if opts.AutoDiscoveryPath != "" {
50+
if err := loadFromDir(k, opts.AutoDiscoveryPath); err != nil {
51+
if opts.RequireConfig || !errors.Is(err, os.ErrNotExist) {
52+
return nil, fmt.Errorf("failed to load config dir: %w", err)
53+
}
54+
}
55+
}
56+
57+
if opts.ConfigPath != "" {
58+
if err := loadFromFile(k, opts.ConfigPath); err != nil {
59+
if opts.RequireConfig || !errors.Is(err, os.ErrNotExist) {
60+
return nil, fmt.Errorf("failed to load config file: %w", err)
61+
}
62+
}
63+
}
64+
65+
if opts.AllowEnv {
66+
if err := loadFromEnv(k); err != nil {
67+
return nil, fmt.Errorf("failed to load environment variables: %w", err)
68+
}
69+
}
70+
71+
var cfg Config
72+
if err := k.Unmarshal("", &cfg); err != nil {
73+
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
74+
}
75+
76+
return &cfg, nil
77+
}
78+
79+
// loadFromFile loads configuration from a file, supporting multiple formats
80+
func loadFromFile(k *koanf.Koanf, path string) error {
81+
var parser koanf.Parser
82+
83+
switch strings.ToLower(filepath.Ext(path)) {
84+
case ".yaml", ".yml":
85+
parser = yaml.Parser()
86+
case ".json":
87+
parser = json.Parser()
88+
case ".toml":
89+
parser = toml.Parser()
90+
default:
91+
return fmt.Errorf("unsupported config file format: %s", path)
92+
}
93+
94+
if err := k.Load(file.Provider(path), parser); err != nil {
95+
return err
96+
}
97+
98+
return nil
99+
}
100+
101+
// loadFromDir loads configuration from a directory, supporting multiple formats
102+
func loadFromDir(k *koanf.Koanf, path string) error {
103+
if _, err := os.Stat(filepath.Join(path, vaultConfigFileName+vaultConfigFileExt)); err != nil {
104+
if !os.IsNotExist(err) {
105+
return err
106+
}
107+
} else {
108+
return loadFromFile(k, filepath.Join(path, vaultConfigFileName+vaultConfigFileExt))
109+
}
110+
if _, err := os.Stat(filepath.Join(path, vaultConfigFileName+".yml")); err != nil {
111+
if !os.IsNotExist(err) {
112+
return err
113+
}
114+
} else {
115+
return loadFromFile(k, filepath.Join(path, vaultConfigFileName+".yml"))
116+
}
117+
if _, err := os.Stat(filepath.Join(path, vaultConfigFileName+".json")); err != nil {
118+
if !os.IsNotExist(err) {
119+
return err
120+
}
121+
} else {
122+
return loadFromFile(k, filepath.Join(path, vaultConfigFileName+".json"))
123+
}
124+
if _, err := os.Stat(filepath.Join(path, vaultConfigFileName+".toml")); err != nil {
125+
if !os.IsNotExist(err) {
126+
return err
127+
}
128+
} else {
129+
return loadFromFile(k, filepath.Join(path, vaultConfigFileName+".toml"))
130+
}
131+
return fmt.Errorf("no config file found with name %s: %w", vaultConfigFileName, os.ErrNotExist)
132+
}
133+
134+
// loadFromEnv loads configuration from environment variables
135+
func loadFromEnv(k *koanf.Koanf) error {
136+
return k.Load(env.Provider(envPrefix, ".", func(s string) string {
137+
return strings.Replace(strings.ToLower(
138+
strings.TrimPrefix(s, envPrefix)), "_", ".", -1)
139+
}), nil)
140+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package vault
2+
3+
import "github.com/jahvon/flow/internal/services/vault/providers"
4+
5+
func init() {
6+
Providers.Register(providers.DefaultProviderName, &providers.DefaultProvider{})
7+
Providers.Register(providers.CLIProviderName, &providers.CLIProvider{})
8+
}
9+
10+
type Provider interface {
11+
New(config map[string]interface{}) (Adapter, error)
12+
}
13+
14+
var Providers = &Registry{
15+
providers: make(map[string]Provider),
16+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package providers
2+
3+
import "github.com/jahvon/flow/internal/services/run"
4+
5+
const CLIProviderName = "cli"
6+
7+
type CLIProvider struct{}
8+
9+
func (p *CLIProvider) New(config map[string]interface{}) (Adapter, error) {
10+
return &CLIAdapter{}, nil
11+
}
12+
13+
type CLIAdapter struct {
14+
getCommand string
15+
setCommand string
16+
deleteCommand string
17+
listCommand string
18+
}
19+
20+
func (a *CLIAdapter) Get(key string) (string, error) {
21+
run.RunCmd()
22+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package providers
2+
3+
const DefaultProviderName = "default"
4+
5+
type DefaultProvider struct{}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package vault
2+
3+
import "sync"
4+
5+
type Registry struct {
6+
providers map[string]Provider
7+
mu sync.RWMutex
8+
}
9+
10+
func (r *Registry) Register(name string, provider Provider) {
11+
r.mu.Lock()
12+
defer r.mu.Unlock()
13+
r.providers[name] = provider
14+
}
15+
16+
func (r *Registry) Get(name string) (Provider, bool) {
17+
r.mu.RLock()
18+
defer r.mu.RUnlock()
19+
provider, exists := r.providers[name]
20+
return provider, exists
21+
}

internal/services/vault/service.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package vault
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/jahvon/flow/internal/services/vault/providers"
7+
)
8+
9+
func NewVault(config *Config) (Adapter, error) {
10+
if config.Vault == nil {
11+
provider, _ := Providers.Get(providers.DefaultProviderName)
12+
return provider.New(nil)
13+
}
14+
15+
provider, exists := Providers.Get(config.Vault.Provider)
16+
if !exists {
17+
return nil, fmt.Errorf("unknown vault provider: %s", config.Vault.Provider)
18+
}
19+
20+
return provider.New(config.Vault.Config)
21+
}

0 commit comments

Comments
 (0)