feat: add Vault
Co-authored-by: Dmitry Fedotov <dmitry@uint32.ru> Co-committed-by: Dmitry Fedotov <dmitry@uint32.ru>
This commit is contained in:
99
internal/vault/vault.go
Normal file
99
internal/vault/vault.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package vault
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
|
||||
"github.com/hashicorp/vault/api"
|
||||
)
|
||||
|
||||
type Storage struct {
|
||||
client *api.Client
|
||||
path string
|
||||
}
|
||||
|
||||
func Open(token string, path string, addr string) (*Storage, error) {
|
||||
conf := &api.Config{
|
||||
Address: addr,
|
||||
}
|
||||
|
||||
c, err := api.NewClient(conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.SetToken(token)
|
||||
|
||||
return &Storage{client: c, path: path}, nil
|
||||
}
|
||||
|
||||
func (s *Storage) Save(key string, data []byte) error {
|
||||
kv := s.client.KVv1(s.path)
|
||||
|
||||
str := base64.StdEncoding.EncodeToString(data)
|
||||
m := map[string]any{
|
||||
"data": map[string]string{
|
||||
"payload": str,
|
||||
},
|
||||
}
|
||||
|
||||
if err := kv.Put(context.Background(), "testkey", m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Storage) Load(key string) ([]byte, error) {
|
||||
kv := s.client.KVv1(s.path)
|
||||
|
||||
m, err := kv.Get(context.Background(), key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, ok := m.Data["data"] // map[string]any
|
||||
if !ok {
|
||||
return nil, errors.New("no data found")
|
||||
}
|
||||
|
||||
payloadmap, ok := data.(map[string]any)
|
||||
if !ok {
|
||||
return nil, errors.New("no payload map")
|
||||
}
|
||||
|
||||
rawb, ok := payloadmap["payload"]
|
||||
if !ok {
|
||||
return nil, errors.New("no payload bytes")
|
||||
}
|
||||
|
||||
str, ok := rawb.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("could not convert payload to bytes")
|
||||
}
|
||||
|
||||
b := []byte{}
|
||||
|
||||
b, err = base64.StdEncoding.AppendDecode(b, []byte(str))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (s *Storage) Delete(key string) error {
|
||||
kv := s.client.KVv1(s.path)
|
||||
|
||||
if err := kv.Delete(context.Background(), key); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Storage) Close() error {
|
||||
s.client.ClearToken()
|
||||
return nil
|
||||
}
|
57
internal/vault/vault_test.go
Normal file
57
internal/vault/vault_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package vault
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVaultStorage(t *testing.T) {
|
||||
token, ok := os.LookupEnv("V_TOKEN")
|
||||
if !ok {
|
||||
t.Skip("no V_TOKEN")
|
||||
}
|
||||
|
||||
addr, ok := os.LookupEnv("V_ADDR")
|
||||
if !ok {
|
||||
t.Skip("no V_ADDR")
|
||||
}
|
||||
|
||||
path, ok := os.LookupEnv("V_PATH")
|
||||
if !ok {
|
||||
t.Skip("no V_PATH")
|
||||
}
|
||||
|
||||
t.Log(addr)
|
||||
t.Log(path)
|
||||
|
||||
st, err := Open(token, path, addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testkey := "testkey"
|
||||
data := []byte("this is a test")
|
||||
|
||||
if err := st.Save(testkey, data); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
b, err := st.Load(testkey)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(data, b) {
|
||||
t.Errorf("values are not equal, want: %s, have: %s", string(data), string(b))
|
||||
}
|
||||
|
||||
if err := st.Delete(testkey); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
b, err = st.Load(testkey)
|
||||
if err == nil {
|
||||
t.Log("nil error when loading deleted key")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user