|
|
@@ -19,23 +19,23 @@ type FileSystem struct {
|
|
|
|
|
|
func New() *FileSystem {
|
|
|
return &FileSystem{
|
|
|
- Root: newDir("/", 0, 0, nil, ""),
|
|
|
+ Root: newDir("/", 0, 0, nil, "", Permanent),
|
|
|
WatcherHub: newWatchHub(1000),
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
-func (fs *FileSystem) Get(keyPath string, recusive bool, index uint64, term uint64) (*Event, error) {
|
|
|
- // TODO: add recursive get
|
|
|
- n, err := fs.InternalGet(keyPath, index, term)
|
|
|
+func (fs *FileSystem) Get(nodePath string, recusive bool, index uint64, term uint64) (*Event, error) {
|
|
|
+ n, err := fs.InternalGet(nodePath, index, term)
|
|
|
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
- e := newEvent(Get, keyPath, index, term)
|
|
|
+ e := newEvent(Get, nodePath, index, term)
|
|
|
|
|
|
if n.IsDir() { // node is dir
|
|
|
+ e.Dir = true
|
|
|
|
|
|
children, _ := n.List()
|
|
|
e.KVPairs = make([]KeyValuePair, len(children))
|
|
|
@@ -57,7 +57,6 @@ func (fs *FileSystem) Get(keyPath string, recusive bool, index uint64, term uint
|
|
|
|
|
|
// eliminate hidden nodes
|
|
|
e.KVPairs = e.KVPairs[:i]
|
|
|
-
|
|
|
} else { // node is file
|
|
|
e.Value = n.Value
|
|
|
}
|
|
|
@@ -65,93 +64,133 @@ func (fs *FileSystem) Get(keyPath string, recusive bool, index uint64, term uint
|
|
|
return e, nil
|
|
|
}
|
|
|
|
|
|
-func (fs *FileSystem) Set(keyPath string, value string, expireTime time.Time, index uint64, term uint64) (*Event, error) {
|
|
|
- keyPath = path.Clean("/" + keyPath)
|
|
|
+// Create function creates the Node at nodePath. Create will help to create intermediate directories with no ttl.
|
|
|
+// If the node has already existed, create will fail.
|
|
|
+// If any node on the path is a file, create will fail.
|
|
|
+func (fs *FileSystem) Create(nodePath string, value string, expireTime time.Time, index uint64, term uint64) (*Event, error) {
|
|
|
+ nodePath = path.Clean("/" + nodePath)
|
|
|
|
|
|
- // update file system known index and term
|
|
|
- fs.Index, fs.Term = index, term
|
|
|
+ // make sure we can create the node
|
|
|
+ _, err := fs.InternalGet(nodePath, index, term)
|
|
|
|
|
|
- dir, name := path.Split(keyPath)
|
|
|
+ if err == nil { // key already exists
|
|
|
+ return nil, etcdErr.NewError(105, nodePath)
|
|
|
+ }
|
|
|
|
|
|
- // walk through the keyPath and get the last directory node
|
|
|
- d, err := fs.walk(dir, fs.checkDir)
|
|
|
+ etcdError, _ := err.(etcdErr.Error)
|
|
|
|
|
|
- if err != nil {
|
|
|
+ if etcdError.ErrorCode == 104 { // we cannot create the key due to meet a file while walking
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
- e := newEvent(Set, keyPath, fs.Index, fs.Term)
|
|
|
- e.Value = value
|
|
|
+ dir, _ := path.Split(nodePath)
|
|
|
|
|
|
- f, err := d.GetFile(name)
|
|
|
+ // walk through the nodePath, create dirs and get the last directory node
|
|
|
+ d, err := fs.walk(dir, fs.checkDir)
|
|
|
|
|
|
- if err == nil {
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
|
|
|
- if f != nil { // update previous file if exist
|
|
|
- e.PrevValue = f.Value
|
|
|
- f.Write(e.Value, index, term)
|
|
|
+ e := newEvent(Create, nodePath, fs.Index, fs.Term)
|
|
|
|
|
|
- // if the previous ExpireTime is not Permanent and expireTime is given
|
|
|
- // we stop the previous expire routine
|
|
|
- if f.ExpireTime != Permanent && expireTime != Permanent {
|
|
|
- f.stopExpire <- true
|
|
|
- }
|
|
|
- } else { // create new file
|
|
|
+ var n *Node
|
|
|
|
|
|
- f = newFile(keyPath, value, fs.Index, fs.Term, d, "", expireTime)
|
|
|
+ if len(value) != 0 { // create file
|
|
|
+ e.Value = value
|
|
|
|
|
|
- err = d.Add(f)
|
|
|
+ n = newFile(nodePath, value, fs.Index, fs.Term, d, "", expireTime)
|
|
|
|
|
|
- }
|
|
|
+ } else { // create directory
|
|
|
+ e.Dir = true
|
|
|
+
|
|
|
+ n = newDir(nodePath, fs.Index, fs.Term, d, "", expireTime)
|
|
|
|
|
|
}
|
|
|
|
|
|
+ err = d.Add(n)
|
|
|
+
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
// Node with TTL
|
|
|
if expireTime != Permanent {
|
|
|
- go f.Expire()
|
|
|
- e.Expiration = &f.ExpireTime
|
|
|
+ go n.Expire()
|
|
|
+ e.Expiration = &n.ExpireTime
|
|
|
e.TTL = int64(expireTime.Sub(time.Now()) / time.Second)
|
|
|
}
|
|
|
|
|
|
+ fs.WatcherHub.notify(e)
|
|
|
return e, nil
|
|
|
}
|
|
|
|
|
|
-func (fs *FileSystem) TestAndSet(keyPath string, prevValue string, prevIndex uint64, value string, expireTime time.Time, index uint64, term uint64) (*Event, error) {
|
|
|
- f, err := fs.InternalGet(keyPath, index, term)
|
|
|
+// Update function updates the value/ttl of the node.
|
|
|
+// If the node is a file, the value and the ttl can be updated.
|
|
|
+// If the node is a directory, only the ttl can be updated.
|
|
|
+func (fs *FileSystem) Update(nodePath string, value string, expireTime time.Time, index uint64, term uint64) (*Event, error) {
|
|
|
+ n, err := fs.InternalGet(nodePath, index, term)
|
|
|
|
|
|
- if err != nil {
|
|
|
+ if err != nil { // if the node does not exist, return error
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
|
|
|
- etcdError, _ := err.(etcdErr.Error)
|
|
|
- if etcdError.ErrorCode == 100 { // file does not exist
|
|
|
+ e := newEvent(Update, nodePath, fs.Index, fs.Term)
|
|
|
|
|
|
- if prevValue == "" && prevIndex == 0 { // test against if prevValue is empty
|
|
|
- fs.Set(keyPath, value, expireTime, index, term)
|
|
|
- e := newEvent(TestAndSet, keyPath, index, term)
|
|
|
- e.Value = value
|
|
|
- return e, nil
|
|
|
- }
|
|
|
+ if n.IsDir() { // if the node is a directory, we can only update ttl
|
|
|
|
|
|
- return nil, err
|
|
|
+ if len(value) != 0 {
|
|
|
+ return nil, etcdErr.NewError(102, nodePath)
|
|
|
+ }
|
|
|
+
|
|
|
+ } else { // if the node is a file, we can update value and ttl
|
|
|
+ e.PrevValue = n.Value
|
|
|
|
|
|
+ if len(value) != 0 {
|
|
|
+ e.Value = value
|
|
|
}
|
|
|
|
|
|
+ n.Write(value, index, term)
|
|
|
+ }
|
|
|
+
|
|
|
+ // update ttl
|
|
|
+ if n.ExpireTime != Permanent && expireTime != Permanent {
|
|
|
+ n.stopExpire <- true
|
|
|
+ }
|
|
|
+
|
|
|
+ if expireTime != Permanent {
|
|
|
+ go n.Expire()
|
|
|
+ e.Expiration = &n.ExpireTime
|
|
|
+ e.TTL = int64(expireTime.Sub(time.Now()) / time.Second)
|
|
|
+ }
|
|
|
+
|
|
|
+ fs.WatcherHub.notify(e)
|
|
|
+ return e, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (fs *FileSystem) TestAndSet(nodePath string, prevValue string, prevIndex uint64,
|
|
|
+ value string, expireTime time.Time, index uint64, term uint64) (*Event, error) {
|
|
|
+
|
|
|
+ f, err := fs.InternalGet(nodePath, index, term)
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
if f.IsDir() { // can only test and set file
|
|
|
- return nil, etcdErr.NewError(102, keyPath)
|
|
|
+ return nil, etcdErr.NewError(102, nodePath)
|
|
|
}
|
|
|
|
|
|
if f.Value == prevValue || f.ModifiedIndex == prevIndex {
|
|
|
// if test succeed, write the value
|
|
|
- e := newEvent(TestAndSet, keyPath, index, term)
|
|
|
+ e := newEvent(TestAndSet, nodePath, index, term)
|
|
|
e.PrevValue = f.Value
|
|
|
e.Value = value
|
|
|
f.Write(value, index, term)
|
|
|
+
|
|
|
+ fs.WatcherHub.notify(e)
|
|
|
+
|
|
|
return e, nil
|
|
|
}
|
|
|
|
|
|
@@ -159,20 +198,16 @@ func (fs *FileSystem) TestAndSet(keyPath string, prevValue string, prevIndex uin
|
|
|
return nil, etcdErr.NewError(101, cause)
|
|
|
}
|
|
|
|
|
|
-func (fs *FileSystem) Delete(keyPath string, recurisive bool, index uint64, term uint64) (*Event, error) {
|
|
|
- n, err := fs.InternalGet(keyPath, index, term)
|
|
|
+// Delete function deletes the node at the given path.
|
|
|
+// If the node is a directory, recursive must be true to delete it.
|
|
|
+func (fs *FileSystem) Delete(nodePath string, recursive bool, index uint64, term uint64) (*Event, error) {
|
|
|
+ n, err := fs.InternalGet(nodePath, index, term)
|
|
|
|
|
|
- if err != nil {
|
|
|
+ if err != nil { // if the node does not exist, return error
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
- err = n.Remove(recurisive)
|
|
|
-
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- e := newEvent(Delete, keyPath, index, term)
|
|
|
+ e := newEvent(Delete, nodePath, index, term)
|
|
|
|
|
|
if n.IsDir() {
|
|
|
e.Dir = true
|
|
|
@@ -180,12 +215,24 @@ func (fs *FileSystem) Delete(keyPath string, recurisive bool, index uint64, term
|
|
|
e.PrevValue = n.Value
|
|
|
}
|
|
|
|
|
|
+ callback := func(path string) { // notify function
|
|
|
+ fs.WatcherHub.notifyWithPath(e, path, true)
|
|
|
+ }
|
|
|
+
|
|
|
+ err = n.Remove(recursive, callback)
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ fs.WatcherHub.notify(e)
|
|
|
+
|
|
|
return e, nil
|
|
|
}
|
|
|
|
|
|
-// walk function walks all the keyPath and apply the walkFunc on each directory
|
|
|
-func (fs *FileSystem) walk(keyPath string, walkFunc func(prev *Node, component string) (*Node, error)) (*Node, error) {
|
|
|
- components := strings.Split(keyPath, "/")
|
|
|
+// walk function walks all the nodePath and apply the walkFunc on each directory
|
|
|
+func (fs *FileSystem) walk(nodePath string, walkFunc func(prev *Node, component string) (*Node, error)) (*Node, error) {
|
|
|
+ components := strings.Split(nodePath, "/")
|
|
|
|
|
|
curr := fs.Root
|
|
|
|
|
|
@@ -205,9 +252,9 @@ func (fs *FileSystem) walk(keyPath string, walkFunc func(prev *Node, component s
|
|
|
return curr, nil
|
|
|
}
|
|
|
|
|
|
-// InternalGet function get the node of the given keyPath.
|
|
|
-func (fs *FileSystem) InternalGet(keyPath string, index uint64, term uint64) (*Node, error) {
|
|
|
- keyPath = path.Clean("/" + keyPath)
|
|
|
+// InternalGet function get the node of the given nodePath.
|
|
|
+func (fs *FileSystem) InternalGet(nodePath string, index uint64, term uint64) (*Node, error) {
|
|
|
+ nodePath = path.Clean("/" + nodePath)
|
|
|
|
|
|
// update file system known index and term
|
|
|
fs.Index, fs.Term = index, term
|
|
|
@@ -226,7 +273,7 @@ func (fs *FileSystem) InternalGet(keyPath string, index uint64, term uint64) (*N
|
|
|
return nil, etcdErr.NewError(100, path.Join(parent.Path, name))
|
|
|
}
|
|
|
|
|
|
- f, err := fs.walk(keyPath, walkFunc)
|
|
|
+ f, err := fs.walk(nodePath, walkFunc)
|
|
|
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
@@ -247,7 +294,7 @@ func (fs *FileSystem) checkDir(parent *Node, dirName string) (*Node, error) {
|
|
|
return subDir, nil
|
|
|
}
|
|
|
|
|
|
- n := newDir(path.Join(parent.Path, dirName), fs.Index, fs.Term, parent, parent.ACL)
|
|
|
+ n := newDir(path.Join(parent.Path, dirName), fs.Index, fs.Term, parent, parent.ACL, Permanent)
|
|
|
|
|
|
parent.Children[dirName] = n
|
|
|
|