Fix deleteFile (missing deletes for etcd metadata)

Also move resource and serializer interfaces to types.go
This commit is contained in:
Soam Vasani
2016-09-20 00:25:50 -07:00
parent 66854ff286
commit a0cfadbd73
3 changed files with 118 additions and 42 deletions
+52 -15
View File
@@ -18,22 +18,15 @@ package controller
import (
"errors"
"reflect"
log "github.com/Sirupsen/logrus"
"github.com/coreos/etcd/client"
"github.com/satori/go.uuid"
"golang.org/x/net/context"
"reflect"
)
type (
resource interface {
key() string
}
serializer interface {
serialize(r resource) ([]byte, error)
deserialize(buf []byte, r resource) error
}
resourceStore struct {
*fileStore
client.KeysAPI
@@ -62,7 +55,7 @@ func getKey(r resource) (string, error) {
if err != nil {
return "", err
}
rkey := r.key()
rkey := r.Key()
return (typName + "/" + rkey), nil
}
@@ -139,6 +132,7 @@ func (rs *resourceStore) writeFile(parentKey string, contents []byte) (string, s
return "", "", err
}
parentKey = "file/" + parentKey
resp, err := rs.KeysAPI.CreateInOrder(context.Background(), parentKey, uid, nil)
if err != nil {
_ = rs.fileStore.delete(uid)
@@ -149,6 +143,7 @@ func (rs *resourceStore) writeFile(parentKey string, contents []byte) (string, s
}
func (rs *resourceStore) readFile(key string, uid *string) ([]byte, error) {
key = "file/" + key
resp, err := rs.KeysAPI.Get(context.Background(), key, &client.GetOptions{Sort: true})
if err != nil {
return nil, err
@@ -168,7 +163,7 @@ func (rs *resourceStore) readFile(key string, uid *string) ([]byte, error) {
}
}
if !found {
return nil, errors.New("Invalid UUID")
return nil, errors.New("Invalid UID " + *uid)
}
}
@@ -177,15 +172,57 @@ func (rs *resourceStore) readFile(key string, uid *string) ([]byte, error) {
}
func (rs *resourceStore) deleteFile(key string, uid string) error {
key = "file/" + key
resp, err := rs.KeysAPI.Get(context.Background(), key, &client.GetOptions{Sort: true})
if err != nil {
return err
}
var node *client.Node
for _, u := range resp.Node.Nodes {
if u.Value == uid {
node = u
}
}
if node == nil {
log.WithFields(log.Fields{"key": key, "uid": uid}).Error("unreferenced file")
return errors.New("won't delete unreferenced file")
}
err = rs.fileStore.delete(node.Value)
if err != nil {
return err
}
_, err = rs.KeysAPI.Delete(context.Background(), node.Key, nil)
if err != nil {
return err
}
if len(resp.Node.Nodes) == 1 {
_, err = rs.KeysAPI.Delete(context.Background(), key, &client.DeleteOptions{Dir: true})
return err
}
return nil
}
func (rs *resourceStore) deleteAllFiles(key string) error {
key = "file/" + key
resp, err := rs.KeysAPI.Get(context.Background(), key, &client.GetOptions{Sort: true})
if err != nil {
return err
}
for _, u := range resp.Node.Nodes {
if u.Value == uid {
err = rs.fileStore.delete(u.Value)
err = rs.fileStore.delete(u.Value)
if err != nil {
return err
}
_, err = rs.KeysAPI.Delete(context.Background(), u.Key, nil)
if err != nil {
return err
}
}
return errors.New("won't delete unreferenced file")
_, err = rs.KeysAPI.Delete(context.Background(), key, &client.DeleteOptions{Dir: true})
return err
}
+38 -27
View File
@@ -17,12 +17,13 @@ limitations under the License.
package controller
import (
// "github.com/coreos/etcd/client"
"golang.org/x/net/context"
"io/ioutil"
"log"
"os"
"testing"
"github.com/coreos/etcd/client"
"golang.org/x/net/context"
)
type TestResource struct {
@@ -30,11 +31,11 @@ type TestResource struct {
B int
}
func (tr TestResource) key() string {
func (tr TestResource) Key() string {
return tr.A
}
func check(err error) {
func panicIf(err error) {
if err != nil {
log.Panicf("err: %v", err)
}
@@ -46,91 +47,101 @@ func assert(b bool, msg string) {
}
}
func TestResourceStore(t *testing.T) {
func getTestResourceStore() (*fileStore, client.KeysAPI, *resourceStore) {
// make a tmp dir
dir, err := ioutil.TempDir("", "testFileStore")
check(err)
defer os.RemoveAll(dir)
panicIf(err)
fs := makeFileStore(dir)
// assume etcd is running, connect to it
ks := getEtcdKeyAPI([]string{"http://localhost:2379"})
s := JsonSerializer{}
rs := makeResourceStore(fs, ks, s)
return fs, ks, rs
}
func TestResourceStore(t *testing.T) {
fs, ks, rs := getTestResourceStore()
defer os.RemoveAll(fs.root)
s := JsonSerializer{}
tr := TestResource{A: "hello", B: 1}
// Delete the key first, in case of a panic'd previous test run; ignore errors
_ = rs.delete("TestResource", tr.key())
_ = rs.delete("TestResource", tr.Key())
// Create
err = rs.create(tr)
check(err)
defer rs.delete("TestResource", tr.key())
err := rs.create(tr)
panicIf(err)
defer ks.Delete(context.Background(), "/TestResource", &client.DeleteOptions{Dir: true})
defer rs.delete("TestResource", tr.Key())
// Etcd key /TestResource/hello should exist
_, err = ks.Get(context.Background(), "TestResource/hello", nil)
check(err)
panicIf(err)
// Read
tr1 := TestResource{}
err = rs.read(tr.key(), &tr1)
check(err)
err = rs.read(tr.Key(), &tr1)
panicIf(err)
assert(tr1 == tr, "retrieved value must equal created value")
// Update and Read
tr.B += 1
err = rs.update(tr)
check(err)
err = rs.read(tr.key(), &tr1)
check(err)
panicIf(err)
err = rs.read(tr.Key(), &tr1)
panicIf(err)
assert(tr1 == tr, "retrieved value must equal updated value")
// Get list
results, err := rs.getAll("TestResource")
check(err)
panicIf(err)
res := make([]TestResource, 0, 0)
for _, r := range results {
tmp := TestResource{}
err = s.deserialize([]byte(r), &tmp)
check(err)
panicIf(err)
res = append(res, tmp)
}
assert(res[0] == tr, "value from retrieved list must equal updated value")
// file tests
fileKey := "foo"
fileKey := "resourceStoreTest"
fileContents1 := []byte("hello")
fileContents2 := []byte("world")
key, uid1, err := rs.writeFile(fileKey, fileContents1)
check(err)
panicIf(err)
defer rs.deleteFile(fileKey, uid1)
log.Printf("key = %v, uid = %v", key, uid1)
// read latest
contents, err := rs.readFile(fileKey, nil)
check(err)
panicIf(err)
assert(string(contents) == string(fileContents1), "retrieved file contents must match written value")
// update-- same key new contents
_, uid2, err := rs.writeFile(fileKey, fileContents2)
check(err)
panicIf(err)
defer rs.deleteFile(fileKey, uid2)
// read latest
contents, err = rs.readFile(fileKey, nil)
check(err)
panicIf(err)
assert(string(contents) == string(fileContents2), "retrieved file contents must match updated value")
// read by uid
// 1
contents, err = rs.readFile(fileKey, &uid1)
check(err)
panicIf(err)
assert(string(contents) == string(fileContents1), "retrieved file contents must match updated value")
// 2
contents, err = rs.readFile(fileKey, &uid2)
check(err)
panicIf(err)
assert(string(contents) == string(fileContents2), "retrieved file contents must match updated value")
}
+28
View File
@@ -0,0 +1,28 @@
/*
Copyright 2016 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controller
type (
resource interface {
Key() string
}
serializer interface {
serialize(r resource) ([]byte, error)
deserialize(buf []byte, r resource) error
}
)