feat(vault-tui): implement KV client and service for managing secrets
- Added internal/vault/client.go for creating a Vault client with configuration settings. - Introduced internal/vault/errors.go to classify Vault API errors for better UI handling. - Created internal/vault/kv.go to manage KV secrets, including listing, reading, writing, and deleting operations. - Implemented internal/vault/mounts.go to list and describe secret engine mounts. - Developed internal/vault/service.go to provide a unified entry point for Vault operations. - Added internal/vault/kv_test.go for comprehensive testing of KV operations. - Introduced internal/ui/toast.go for transient notifications in the UI. - Added renovate.json for dependency management and updates.
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Load reads and decodes the file at path. A missing file is not an error:
|
||||
// it returns a zero-valued *File so the tool remains usable purely from
|
||||
// flags/env (see the package doc). Unknown YAML keys are a hard error so
|
||||
// typos in a hand-edited config surface immediately instead of being
|
||||
// silently ignored.
|
||||
func Load(path string) (*File, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return &File{Version: SchemaVersion}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("reading config %q: %w", path, err)
|
||||
}
|
||||
|
||||
var f File
|
||||
dec := yaml.NewDecoder(bytes.NewReader(data))
|
||||
dec.KnownFields(true)
|
||||
if err := dec.Decode(&f); err != nil {
|
||||
return nil, fmt.Errorf("parsing config %q: %w", path, err)
|
||||
}
|
||||
|
||||
if f.Version == 0 {
|
||||
f.Version = SchemaVersion
|
||||
}
|
||||
if f.Version != SchemaVersion {
|
||||
return nil, fmt.Errorf("config %q has version %d, vault-tui supports version %d", path, f.Version, SchemaVersion)
|
||||
}
|
||||
for name, p := range f.Profiles {
|
||||
if p != nil && p.Token.Value != "" {
|
||||
fmt.Fprintf(os.Stderr, "warning: profile %q sets token.value directly in the config file; prefer VAULT_TOKEN or token.file\n", name)
|
||||
}
|
||||
}
|
||||
return &f, nil
|
||||
}
|
||||
|
||||
// Save writes f to path as YAML, creating parent directories as needed.
|
||||
// The file is written with 0600 permissions since profiles may carry
|
||||
// TLS key paths and (discouraged but supported) inline token values.
|
||||
func Save(path string, f *File) error {
|
||||
if err := os.MkdirAll(dirOf(path), 0o700); err != nil {
|
||||
return fmt.Errorf("creating config directory: %w", err)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
enc := yaml.NewEncoder(&buf)
|
||||
enc.SetIndent(2)
|
||||
if err := enc.Encode(f); err != nil {
|
||||
return fmt.Errorf("encoding config: %w", err)
|
||||
}
|
||||
if err := enc.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, buf.Bytes(), 0o600)
|
||||
}
|
||||
|
||||
func dirOf(path string) string {
|
||||
for i := len(path) - 1; i >= 0; i-- {
|
||||
if path[i] == '/' {
|
||||
return path[:i]
|
||||
}
|
||||
}
|
||||
return "."
|
||||
}
|
||||
|
||||
// Exists reports whether a config file is present at path.
|
||||
func Exists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil
|
||||
}
|
||||
Reference in New Issue
Block a user