Repository: codenotary/immudb (language: go)

Issue:
Server UUID in data root path
**What would you like to be added or enhanced**

Server UUID file is created under `defaultDB` path but it should be created in root data folder instead.

**Why is this needed**

Server UUID is cross-database information and thus it shouldn't be placed inside any database path.

**Additional context**

File to relocated is `immudb.identifier`

Modification should automatically work with existent databases in which such file was already located under defaultdb folder.


Discussion / hints:

Fix patch:
diff --git a/pkg/server/server.go b/pkg/server/server.go
index ac2dd50137..6dd65ac2a3 100644
--- a/pkg/server/server.go
+++ b/pkg/server/server.go
@@ -132,8 +132,7 @@ func (s *ImmuServer) Initialize() error {
 	}
 
 	systemDbRootDir := s.OS.Join(dataDir, s.Options.GetDefaultDbName())
-
-	if s.UUID, err = getOrSetUUID(systemDbRootDir); err != nil {
+	if s.UUID, err = getOrSetUUID(dataDir, systemDbRootDir); err != nil {
 		return logErr(s.Logger, "Unable to get or set uuid: %v", err)
 	}
 
diff --git a/pkg/server/uuid.go b/pkg/server/uuid.go
index 60c54345b2..36caa5c249 100644
--- a/pkg/server/uuid.go
+++ b/pkg/server/uuid.go
@@ -48,20 +48,56 @@ func NewUUIDContext(id xid.ID) uuidContext {
 	return uuidContext{id}
 }
 
-func getOrSetUUID(dir string) (xid.ID, error) {
-	fname := path.Join(dir, IDENTIFIER_FNAME)
-	if fileExists(fname) {
-		b, err := ioutil.ReadFile(fname)
+// getOrSetUUID either reads the identifier file or creates it if it doesn't exist.
+// In earlier versions, the file was located inside default DB directory. Now, it
+// is moved to the data directory. This function migrates the file to data directory
+// in case it exists in the default db directory.
+func getOrSetUUID(dataDir, defaultDbDir string) (xid.ID, error) {
+	fileInDataDir := path.Join(dataDir, IDENTIFIER_FNAME)
+	if fileExists(fileInDataDir) {
+		return getUUID(fileInDataDir)
+	}
+
+	fileInDefaultDbDir := path.Join(defaultDbDir, IDENTIFIER_FNAME)
+	if fileExists(fileInDefaultDbDir) {
+		guid, err := getUUID(fileInDefaultDbDir)
 		if err != nil {
-			return xid.ID{}, err
+			return guid, err
 		}
-		return xid.FromBytes(b)
+
+		err = moveUUIDFile(guid, fileInDataDir, fileInDefaultDbDir)
+		return guid, err
 	}
+
 	guid := xid.New()
-	err := ioutil.WriteFile(fname, guid.Bytes(), os.ModePerm)
+	err := setUUID(guid, fileInDataDir)
 	return guid, err
 }
 
+func getUUID(fname string) (xid.ID, error) {
+	b, err := ioutil.ReadFile(fname)
+	if err != nil {
+		return xid.ID{}, err
+	}
+	return xid.FromBytes(b)
+}
+
+func setUUID(guid xid.ID, fname string) error {
+	return ioutil.WriteFile(fname, guid.Bytes(), os.ModePerm)
+}
+
+func moveUUIDFile(guid xid.ID, fileInDataDir, fileInDefaultDbDir string) error {
+	// write the new file first in case we crash in between. Do not use
+	// os.Rename here because in case of a crash, bad things can happen.
+	if err := setUUID(guid, fileInDataDir); err != nil {
+		return err
+	}
+
+	// now, delete the old file once we have new file setup
+	err := os.Remove(fileInDefaultDbDir)
+	return err
+}
+
 // fileExists checks if a file exists and is not a directory before we
 // try using it to prevent further errors.
 func fileExists(filename string) bool {


Test patch:
diff --git a/pkg/server/uuid_test.go b/pkg/server/uuid_test.go
index cc3890f8a8..972dbb119d 100644
--- a/pkg/server/uuid_test.go
+++ b/pkg/server/uuid_test.go
@@ -20,6 +20,7 @@ import (
 	"context"
 	"io/ioutil"
 	"os"
+	"path"
 	"testing"
 
 	"github.com/rs/xid"
@@ -28,7 +29,7 @@ import (
 )
 
 func TestNewUUID(t *testing.T) {
-	id, err := getOrSetUUID("./")
+	id, err := getOrSetUUID("./", "./defaultDb")
 	if err != nil {
 		t.Fatalf("error creating UUID, %v", err)
 	}
@@ -47,7 +48,7 @@ func TestNewUUID(t *testing.T) {
 func TestExistingUUID(t *testing.T) {
 	x, _ := xid.FromString("bs6c1kn1lu5qfesu061g")
 	ioutil.WriteFile(IDENTIFIER_FNAME, x.Bytes(), os.ModePerm)
-	id, err := getOrSetUUID("./")
+	id, err := getOrSetUUID("./", "./defaultDb")
 	if err != nil {
 		t.Fatalf("error creating UUID, %v", err)
 	}
@@ -63,8 +64,38 @@ func TestExistingUUID(t *testing.T) {
 	}
 }
 
+func TestMigrateUUID(t *testing.T) {
+	defaultDbDir := "defaultDb"
+	if err := os.Mkdir(defaultDbDir, os.ModePerm); err != nil {
+		t.Fatalf("error in creating default db dir")
+	}
+	defer os.Remove(defaultDbDir)
+
+	fileInDefaultDbDir := path.Join(defaultDbDir, IDENTIFIER_FNAME)
+	x, _ := xid.FromString("bs6c1kn1lu5qfesu061g")
+	ioutil.WriteFile(fileInDefaultDbDir, x.Bytes(), os.ModePerm)
+	id, err := getOrSetUUID("./", defaultDbDir)
+	if err != nil {
+		t.Fatalf("error creating UUID, %v", err)
+	}
+	defer os.RemoveAll(fileInDefaultDbDir)
+	defer os.RemoveAll(IDENTIFIER_FNAME)
+
+	if !fileExists(IDENTIFIER_FNAME) {
+		t.Errorf("uuid file not created, %s", err)
+	}
+	if fileExists(fileInDefaultDbDir) {
+		t.Errorf("uuid file not moved, %s", err)
+	}
+
+	uuid := NewUUIDContext(id)
+	if id.Compare(uuid.UUID) != 0 {
+		t.Fatalf("NewUUIDContext error expected %v, got %v", id, uuid.UUID)
+	}
+}
+
 func TestUUIDContextSetter(t *testing.T) {
-	id, err := getOrSetUUID("./")
+	id, err := getOrSetUUID("./", "./defaultDb")
 	if err != nil {
 		t.Fatalf("error creating UUID, %v", err)
 	}
@@ -101,7 +132,7 @@ func TestUUIDContextSetter(t *testing.T) {
 }
 
 func TestUUIDStreamContextSetter(t *testing.T) {
-	id, err := getOrSetUUID("./")
+	id, err := getOrSetUUID("./", "./defaultDb")
 	if err != nil {
 		t.Fatalf("error creating UUID, %v", err)
 	}