-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
kv backend store for inventory and profile subsystems (#61)
- Loading branch information
1 parent
99efae5
commit 5fb703e
Showing
11 changed files
with
246 additions
and
291 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,87 +1,27 @@ | ||
// Package diskv implements a diskv-backed inventory subsystem storage backend. | ||
// Package diskv implements an inventory subsystem backend using diskv. | ||
package diskv | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"path/filepath" | ||
|
||
"github.com/micromdm/nanocmd/subsystem/inventory/storage" | ||
"github.com/micromdm/nanocmd/subsystem/inventory/storage/kv" | ||
|
||
"github.com/micromdm/nanolib/storage/kv/kvdiskv" | ||
"github.com/peterbourgon/diskv/v3" | ||
) | ||
|
||
// Diskv is an on-disk enrollment inventory data store. | ||
// Diskv is an inventory subsystem backend which uses diskv as the key-value store. | ||
type Diskv struct { | ||
diskv *diskv.Diskv | ||
*kv.KV | ||
} | ||
|
||
// New creates a new initialized inventory data store. | ||
// New creates a new profile store at on disk at path. | ||
func New(path string) *Diskv { | ||
flatTransform := func(s string) []string { return []string{} } | ||
return &Diskv{ | ||
diskv: diskv.New(diskv.Options{ | ||
KV: kv.New(kvdiskv.New(diskv.New(diskv.Options{ | ||
BasePath: filepath.Join(path, "inventory"), | ||
Transform: flatTransform, | ||
Transform: kvdiskv.FlatTransform, | ||
CacheSizeMax: 1024 * 1024, | ||
}), | ||
} | ||
} | ||
|
||
// RetrieveInventory retrieves the inventory data for enrollment IDs. | ||
func (s *Diskv) RetrieveInventory(ctx context.Context, opt *storage.SearchOptions) (map[string]storage.Values, error) { | ||
ret := make(map[string]storage.Values) | ||
for _, id := range opt.IDs { | ||
if !s.diskv.Has(id) { | ||
continue | ||
} | ||
raw, err := s.diskv.Read(id) | ||
if err != nil { | ||
return ret, fmt.Errorf("reading values for %s: %w", id, err) | ||
} | ||
var vals storage.Values | ||
if err = json.Unmarshal(raw, &vals); err != nil { | ||
return ret, fmt.Errorf("unmarshal values for %s: %w", id, err) | ||
} | ||
ret[id] = vals | ||
} | ||
return ret, nil | ||
} | ||
|
||
// StoreInventoryValues stores inventory data about the specified ID. | ||
func (s *Diskv) StoreInventoryValues(ctx context.Context, id string, values storage.Values) error { | ||
var err error | ||
var raw []byte | ||
var vals storage.Values | ||
if s.diskv.Has(id) { | ||
// this is likely race-prone as we perform a read-process-write on the same key. | ||
if raw, err = s.diskv.Read(id); err != nil { | ||
return fmt.Errorf("reading values: %w", err) | ||
} | ||
if len(raw) > 0 { | ||
if err = json.Unmarshal(raw, &vals); err != nil { | ||
return fmt.Errorf("unmarshal values: %w", err) | ||
} | ||
if vals != nil { | ||
for k := range values { | ||
vals[k] = values[k] | ||
} | ||
} | ||
} | ||
} | ||
if vals == nil { | ||
vals = values | ||
} | ||
if raw, err = json.Marshal(vals); err != nil { | ||
return fmt.Errorf("marshal values: %w", err) | ||
}))), | ||
} | ||
if err = s.diskv.Write(id, raw); err != nil { | ||
return fmt.Errorf("write values: %w", err) | ||
} | ||
return nil | ||
} | ||
|
||
// DeleteInventory deletes all inventory data for an enrollment ID. | ||
func (s *Diskv) DeleteInventory(ctx context.Context, id string) error { | ||
return s.diskv.Erase(id) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,12 @@ | ||
package diskv | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
"github.com/micromdm/nanocmd/subsystem/inventory/storage" | ||
"github.com/micromdm/nanocmd/subsystem/inventory/storage/test" | ||
) | ||
|
||
func TestDiskv(t *testing.T) { | ||
test.TestStorage(t, func() storage.Storage { return New("teststor") }) | ||
os.RemoveAll("teststor") | ||
test.TestStorage(t, func() storage.Storage { return New(t.TempDir()) }) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// Package kv implements an inventory subsystem storage backend using a key-value store. | ||
package kv | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"sync" | ||
|
||
"github.com/micromdm/nanocmd/subsystem/inventory/storage" | ||
|
||
"github.com/micromdm/nanolib/storage/kv" | ||
) | ||
|
||
// KV is an inventory subsystem storage backend using a key-value store. | ||
type KV struct { | ||
mu sync.RWMutex | ||
b kv.KeysPrefixTraversingBucket | ||
} | ||
|
||
// New creates a new inventory subsystem backend. | ||
func New(b kv.KeysPrefixTraversingBucket) *KV { | ||
return &KV{b: b} | ||
} | ||
|
||
// RetrieveInventory queries and returns the inventory values by mapped | ||
// by enrollment ID from the key-value store. Must provide opt and IDs. | ||
func (s *KV) RetrieveInventory(ctx context.Context, opt *storage.SearchOptions) (map[string]storage.Values, error) { | ||
if opt == nil || len(opt.IDs) < 1 { | ||
return nil, storage.ErrNoIDs | ||
} | ||
|
||
s.mu.RLock() | ||
defer s.mu.RUnlock() | ||
|
||
r := make(map[string]storage.Values) | ||
for _, id := range opt.IDs { | ||
jsonValues, err := s.b.Get(ctx, id) | ||
if errors.Is(err, kv.ErrKeyNotFound) { | ||
continue | ||
} else if err != nil { | ||
return r, fmt.Errorf("getting values for %s: %w", id, err) | ||
} | ||
|
||
var values storage.Values | ||
if err = json.Unmarshal(jsonValues, &values); err != nil { | ||
return r, fmt.Errorf("unmarshal values for %s: %w", id, err) | ||
} | ||
r[id] = values | ||
} | ||
return r, nil | ||
} | ||
|
||
// StoreInventoryValues stores inventory data about the specified ID. | ||
func (s *KV) StoreInventoryValues(ctx context.Context, id string, newValues storage.Values) error { | ||
if id == "" { | ||
return storage.ErrNoIDs | ||
} | ||
if len(newValues) == 0 { | ||
return nil | ||
} | ||
|
||
s.mu.Lock() | ||
defer s.mu.Unlock() | ||
|
||
jsonValues, err := s.b.Get(ctx, id) | ||
if err != nil && !errors.Is(err, kv.ErrKeyNotFound) { | ||
return fmt.Errorf("get values: %w", err) | ||
} | ||
|
||
var values storage.Values | ||
if len(jsonValues) < 1 { | ||
values = newValues | ||
} else { | ||
// load existing values | ||
if err = json.Unmarshal(jsonValues, &values); err != nil { | ||
return fmt.Errorf("unmarshal values: %w", err) | ||
} | ||
|
||
// merge the new values in | ||
for k := range newValues { | ||
values[k] = newValues[k] | ||
} | ||
} | ||
|
||
if jsonValues, err = json.Marshal(&values); err != nil { | ||
return fmt.Errorf("marshal values: %w", err) | ||
} | ||
|
||
if err = s.b.Set(ctx, id, jsonValues); err != nil { | ||
return fmt.Errorf("set values: %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// DeleteInventory deletes all inventory data for an enrollment ID. | ||
func (s *KV) DeleteInventory(ctx context.Context, id string) error { | ||
return s.b.Delete(ctx, id) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.