Cleanup blueprint indexing (#447)

Saving a blueprint was creating multiple records in the
blueprint index bucket. One was the index that gets used
for lookup which is a key of the blueprint Name and the value
being the blueprint UUID and the other record had a key and
value of blueprint UUID. The	second with a key of UUID is not
needed since the key in the main blueprint bucket is already UUID
This commit is contained in:
Scott Knight
2018-07-05 16:28:26 -04:00
committed by Victor Vrantchan
parent 1593d130e7
commit 6dcd89ac49
2 changed files with 145 additions and 25 deletions

View File

@@ -47,17 +47,6 @@ func NewDB(
return datastore, nil
}
func (db *DB) Create(bp *blueprint.Blueprint) error {
_, err := db.BlueprintByName(bp.Name)
if err != nil && isNotFound(err) {
return errors.New("blueprint must have a unique name")
} else if err != nil {
return errors.Wrap(err, "creating blueprint")
}
return db.Save(bp)
}
func (db *DB) List() ([]blueprint.Blueprint, error) {
// TODO add filter/limit with ForEach
var blueprints []blueprint.Blueprint
@@ -109,22 +98,16 @@ func (db *DB) Save(bp *blueprint.Blueprint) error {
if err != nil {
return errors.Wrap(err, "marshalling blueprint")
}
indexes := []string{bp.UUID, bp.Name}
idxBucket := tx.Bucket([]byte(blueprintIndexBucket))
if idxBucket == nil {
return fmt.Errorf("bucket %v not found!", idxBucket)
}
for _, idx := range indexes {
if idx == "" {
continue
}
key := []byte(idx)
if err := idxBucket.Put(key, []byte(bp.UUID)); err != nil {
return errors.Wrap(err, "put blueprint idx to boltdb")
}
key := []byte(bp.Name)
if err := idxBucket.Put(key, []byte(bp.UUID)); err != nil {
return errors.Wrap(err, "put blueprint idx to boltdb")
}
key := []byte(bp.UUID)
key = []byte(bp.UUID)
if err := bkt.Put(key, bpproto); err != nil {
return errors.Wrap(err, "put blueprint to boltdb")
}
@@ -192,10 +175,6 @@ func (db *DB) Delete(name string) error {
if err != nil {
return err
}
err = i.Delete([]byte(bp.UUID))
if err != nil {
return err
}
err = b.Delete([]byte(bp.UUID))
if err != nil {
return err

View File

@@ -0,0 +1,141 @@
package builtin
import (
"io/ioutil"
"os"
"testing"
"github.com/boltdb/bolt"
"github.com/micromdm/micromdm/platform/blueprint"
profile "github.com/micromdm/micromdm/platform/profile/builtin"
user "github.com/micromdm/micromdm/platform/user/builtin"
)
func TestSave(t *testing.T) {
db := setupDB(t)
bp := &blueprint.Blueprint{}
bp.ApplyAt = []string{"Enroll"}
if err := db.Save(bp); err == nil {
t.Fatal("blueprints are required to have an UUID and Name")
}
bp.UUID = ""
bp.Name = "blueprint"
if err := db.Save(bp); err == nil {
t.Fatal("blueprints are required to have an UUID")
}
bp.UUID = "a-b-c-d"
bp.Name = ""
if err := db.Save(bp); err == nil {
t.Fatal("blueprints are required to have a Name")
}
bp.UUID = "a-b-c-d"
bp.Name = "blueprint"
if err := db.Save(bp); err != nil {
t.Fatalf("saving blueprint in datastore: %s", err)
}
bp.UUID = "e-f-g-h"
bp.Name = "blueprint"
if err := db.Save(bp); err == nil {
t.Fatal("blueprint names must be unique")
}
bp.UUID = "e-f-g-h"
bp.Name = "blueprint2"
if err := db.Save(bp); err != nil {
t.Fatalf("saving blueprint2 in datastore: %s", err)
}
byName, err := db.BlueprintByName("blueprint")
if err != nil {
t.Fatalf("getting blueprint by Name: %s", err)
}
if byName == nil || byName.UUID != "a-b-c-d" {
t.Fatalf("have %s, want %s", byName.UUID, "a-b-c-d")
}
byApplyAt, err := db.BlueprintsByApplyAt("Enroll")
if err != nil {
t.Fatalf("getting blueprint by ApplyAt: %s", err)
}
if len(byApplyAt) != 2 {
t.Fatalf("multiple blueprints not saved correctly")
}
}
func TestList(t *testing.T) {
db := setupDB(t)
bp1 := &blueprint.Blueprint{
UUID: "a-b-c-d",
Name: "blueprint-1",
}
bp2 := &blueprint.Blueprint{
UUID: "e-f-g-h",
Name: "blueprint-2",
}
if err := db.Save(bp1); err != nil {
t.Fatalf("saving blueprint-1 to datastore: %s", err)
}
if err := db.Save(bp2); err != nil {
t.Fatalf("saving blueprint-2 to datastore: %s", err)
}
bps, err := db.List()
if err != nil {
t.Fatalf("listing blueprints: %s", err)
}
if len(bps) != 2 {
t.Fatalf("expected %d, found %d", 2, len(bps))
}
}
func TestDelete(t *testing.T) {
db := setupDB(t)
bp1 := &blueprint.Blueprint{
UUID: "a-b-c-d",
Name: "blueprint",
}
if err := db.Save(bp1); err != nil {
t.Fatalf("saving blueprint to datastore: %s", err)
}
if err := db.Delete("blueprint"); err != nil {
t.Fatalf("deleting blueprint in datastore: %s", err)
}
_, err := db.BlueprintByName("blueprint")
if err == nil {
t.Fatalf("expected blueprint to be deleted: %s", err)
}
}
func setupDB(t *testing.T) *DB {
f, _ := ioutil.TempFile("", "bolt-")
f.Close()
os.Remove(f.Name())
db, err := bolt.Open(f.Name(), 0777, nil)
if err != nil {
t.Fatalf("couldn't open bolt, err %s\n", err)
}
profileDB, err := profile.NewDB(db)
if err != nil {
t.Fatalf("couldn't create profile DB, err %s\n", err)
}
userDB, err := user.NewDB(db)
if err != nil {
t.Fatalf("couldn't create user DB, err %s\n", err)
}
blueprintDB, err := NewDB(db, profileDB, userDB)
if err != nil {
t.Fatalf("couldn't create blueprint DB, err %s\n", err)
}
return blueprintDB
}