diff --git a/etcdserver/server.go b/etcdserver/server.go index 856f5138c..c3f72704d 100644 --- a/etcdserver/server.go +++ b/etcdserver/server.go @@ -141,7 +141,7 @@ type EtcdServer struct { // NewServer creates a new EtcdServer from the supplied configuration. The // configuration is considered static for the lifetime of the EtcdServer. func NewServer(cfg *ServerConfig) (*EtcdServer, error) { - st := store.New() + st := store.New(StoreAdminPrefix, StoreKeysPrefix) var w *wal.WAL var n raft.Node var s *raft.MemoryStorage diff --git a/store/store.go b/store/store.go index 7553b5a73..7024703c0 100644 --- a/store/store.go +++ b/store/store.go @@ -25,6 +25,7 @@ import ( "github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork" etcdErr "github.com/coreos/etcd/error" + "github.com/coreos/etcd/pkg/types" ) // The default version to set when the store is first initialized. @@ -68,21 +69,27 @@ type store struct { ttlKeyHeap *ttlKeyHeap // need to recovery manually worldLock sync.RWMutex // stop the world lock clock clockwork.Clock + readonlySet types.Set } -func New() Store { - s := newStore() +// The given namespaces will be created as initial directories in the returned store. +func New(namespaces ...string) Store { + s := newStore(namespaces...) s.clock = clockwork.NewRealClock() return s } -func newStore() *store { +func newStore(namespaces ...string) *store { s := new(store) s.CurrentVersion = defaultVersion s.Root = newDir(s, "/", s.CurrentIndex, nil, "", Permanent) + for _, namespace := range namespaces { + s.Root.Add(newDir(s, namespace, s.CurrentIndex, s.Root, "", Permanent)) + } s.Stats = newStats() s.WatcherHub = newWatchHub(1000) s.ttlKeyHeap = newTtlKeyHeap() + s.readonlySet = types.NewUnsafeSet(append(namespaces, "/")...) return s } @@ -203,7 +210,7 @@ func (s *store) CompareAndSwap(nodePath string, prevValue string, prevIndex uint nodePath = path.Clean(path.Join("/", nodePath)) // we do not allow the user to change "/" - if nodePath == "/" { + if s.readonlySet.Contains(nodePath) { return nil, etcdErr.NewError(etcdErr.EcodeRootROnly, "/", s.CurrentIndex) } @@ -258,7 +265,7 @@ func (s *store) Delete(nodePath string, dir, recursive bool) (*Event, error) { nodePath = path.Clean(path.Join("/", nodePath)) // we do not allow the user to change "/" - if nodePath == "/" { + if s.readonlySet.Contains(nodePath) { return nil, etcdErr.NewError(etcdErr.EcodeRootROnly, "/", s.CurrentIndex) } @@ -401,7 +408,7 @@ func (s *store) Update(nodePath string, newValue string, expireTime time.Time) ( nodePath = path.Clean(path.Join("/", nodePath)) // we do not allow the user to change "/" - if nodePath == "/" { + if s.readonlySet.Contains(nodePath) { return nil, etcdErr.NewError(etcdErr.EcodeRootROnly, "/", s.CurrentIndex) } @@ -461,7 +468,7 @@ func (s *store) internalCreate(nodePath string, dir bool, value string, unique, nodePath = path.Clean(path.Join("/", nodePath)) // we do not allow the user to change "/" - if nodePath == "/" { + if s.readonlySet.Contains(nodePath) { return nil, etcdErr.NewError(etcdErr.EcodeRootROnly, "/", currIndex) } diff --git a/store/store_test.go b/store/store_test.go index 5cfe49901..c0ab2e249 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -23,6 +23,15 @@ import ( etcdErr "github.com/coreos/etcd/error" ) +func TestNewStoreWithNamespaces(t *testing.T) { + s := newStore("/0", "/1") + + _, err := s.Get("/0", false, false) + assert.Nil(t, err, "") + _, err = s.Get("/1", false, false) + assert.Nil(t, err, "") +} + // Ensure that the store can retrieve an existing value. func TestStoreGetValue(t *testing.T) { s := newStore() @@ -433,22 +442,24 @@ func TestStoreDeleteDiretoryFailsIfNonRecursiveAndDir(t *testing.T) { } func TestRootRdOnly(t *testing.T) { - s := newStore() + s := newStore("/0") - _, err := s.Set("/", true, "", Permanent) - assert.NotNil(t, err, "") + for _, tt := range []string{"/", "/0"} { + _, err := s.Set(tt, true, "", Permanent) + assert.NotNil(t, err, "") - _, err = s.Delete("/", true, true) - assert.NotNil(t, err, "") + _, err = s.Delete(tt, true, true) + assert.NotNil(t, err, "") - _, err = s.Create("/", true, "", false, Permanent) - assert.NotNil(t, err, "") + _, err = s.Create(tt, true, "", false, Permanent) + assert.NotNil(t, err, "") - _, err = s.Update("/", "", Permanent) - assert.NotNil(t, err, "") + _, err = s.Update(tt, "", Permanent) + assert.NotNil(t, err, "") - _, err = s.CompareAndSwap("/", "", 0, "", Permanent) - assert.NotNil(t, err, "") + _, err = s.CompareAndSwap(tt, "", 0, "", Permanent) + assert.NotNil(t, err, "") + } } func TestStoreCompareAndDeletePrevValue(t *testing.T) {