From 910bf9c76d33ee6d408f23ce3470418c54aa6029 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 17 Apr 2015 09:18:21 -0700 Subject: [PATCH] logical/framework: PathStruct --- logical/framework/path_struct.go | 93 +++++++++++++++++++++++++++ logical/framework/path_struct_test.go | 56 ++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 logical/framework/path_struct.go create mode 100644 logical/framework/path_struct_test.go diff --git a/logical/framework/path_struct.go b/logical/framework/path_struct.go new file mode 100644 index 000000000..fb9a5bee7 --- /dev/null +++ b/logical/framework/path_struct.go @@ -0,0 +1,93 @@ +package framework + +import ( + "encoding/json" + "fmt" + + "github.com/hashicorp/vault/logical" +) + +// PathStruct can be used to generate a path that stores a struct +// in the storage. This structure is a map[string]interface{} but the +// types are set according to the schema in this structure. +type PathStruct struct { + Name string + Path string + Schema map[string]*FieldSchema + HelpSynopsis string + HelpDescription string + + Read bool +} + +// Get reads the structure. +func (p *PathStruct) Get(s logical.Storage) (map[string]interface{}, error) { + entry, err := s.Get(fmt.Sprintf("struct/%s", p.Name)) + if err != nil { + return nil, err + } + if entry == nil { + return nil, nil + } + + var result map[string]interface{} + if err := json.Unmarshal(entry.Value, &result); err != nil { + return nil, err + } + + return result, nil +} + +// Put writes the structure. +func (p *PathStruct) Put(s logical.Storage, v map[string]interface{}) error { + bytes, err := json.Marshal(v) + if err != nil { + return err + } + + return s.Put(&logical.StorageEntry{ + Key: fmt.Sprintf("struct/%s", p.Name), + Value: bytes, + }) +} + +// Paths are the paths to append to the Backend paths. +func (p *PathStruct) Paths() []*Path { + // The single path we support to read/write this config + path := &Path{ + Pattern: p.Path, + Fields: p.Schema, + + Callbacks: map[logical.Operation]OperationFunc{ + logical.WriteOperation: p.pathWrite, + }, + + HelpSynopsis: p.HelpSynopsis, + HelpDescription: p.HelpDescription, + } + + // If we support reads, add that + if p.Read { + path.Callbacks[logical.ReadOperation] = p.pathRead + } + + return []*Path{path} +} + +func (p *PathStruct) pathRead( + req *logical.Request, d *FieldData) (*logical.Response, error) { + v, err := p.Get(req.Storage) + if err != nil { + return nil, err + } + + return &logical.Response{ + Data: v, + }, nil +} + +func (p *PathStruct) pathWrite( + req *logical.Request, d *FieldData) (*logical.Response, error) { + err := p.Put(req.Storage, d.Raw) + return nil, err +} diff --git a/logical/framework/path_struct_test.go b/logical/framework/path_struct_test.go new file mode 100644 index 000000000..88003b5fd --- /dev/null +++ b/logical/framework/path_struct_test.go @@ -0,0 +1,56 @@ +package framework + +import ( + "testing" + + "github.com/hashicorp/vault/logical" +) + +func TestPathStruct(t *testing.T) { + p := &PathStruct{ + Name: "foo", + Path: "bar", + Schema: map[string]*FieldSchema{ + "value": &FieldSchema{Type: TypeString}, + }, + Read: true, + } + + storage := new(logical.InmemStorage) + var b logical.Backend = &Backend{Paths: p.Paths()} + + // Write via HTTP + _, err := b.HandleRequest(&logical.Request{ + Operation: logical.WriteOperation, + Path: "bar", + Data: map[string]interface{}{ + "value": "baz", + }, + Storage: storage, + }) + if err != nil { + t.Fatalf("bad: %#v", err) + } + + // Read via HTTP + resp, err := b.HandleRequest(&logical.Request{ + Operation: logical.ReadOperation, + Path: "bar", + Storage: storage, + }) + if err != nil { + t.Fatalf("bad: %#v", err) + } + if resp.Data["value"] != "baz" { + t.Fatalf("bad: %#v", resp) + } + + // Read via API + v, err := p.Get(storage) + if err != nil { + t.Fatalf("bad: %#v", err) + } + if v["value"] != "baz" { + t.Fatalf("bad: %#v", v) + } +}