From 3d66e17110b20abc81ed3af9a4e735f453312a8c Mon Sep 17 00:00:00 2001
From: Benjamin Bollen <ben@erisindustries.com>
Date: Wed, 25 May 2016 17:31:49 +0200
Subject: [PATCH] configure Do.DataDir as default WorkDir/data; introduce
 directory relates filesystem functions in /util; inspired by common but
 debugged

---
 cmd/serve.go      | 67 +++++++++++++++++++++++++++++++----------------
 definitions/do.go | 25 +++++++++++++++---
 util/fs.go        | 46 ++++++++++++++++++++++++++++++++
 3 files changed, 113 insertions(+), 25 deletions(-)
 create mode 100644 util/fs.go

diff --git a/cmd/serve.go b/cmd/serve.go
index 91f3c41a..a8ad8187 100644
--- a/cmd/serve.go
+++ b/cmd/serve.go
@@ -17,11 +17,14 @@
 package commands
 
 import (
+  "fmt"
   "os"
 
   cobra "github.com/spf13/cobra"
 
-  log "github.com/eris-ltd/eris-logger"
+  log  "github.com/eris-ltd/eris-logger"
+
+  util "github.com/eris-ltd/eris-db/util"
 )
 
 var ServeCmd = &cobra.Command {
@@ -30,10 +33,28 @@ var ServeCmd = &cobra.Command {
   Long:  `Eris-DB serve starts an eris-db node with client API enabled by default.
 The Eris-DB node is modularly configured for the consensus engine and application
 manager.  The client API can be disabled.`,
-  Example: `$ eris-db serve -- will start the Eris-DB node based on the configuration file in the current working directory`,
-  Run: func(cmd *cobra.Command, args []string) {
-    serve()
+  Example: `$ eris-db serve -- will start the Eris-DB node based on the configuration file "server_config.toml" in the current working directory
+$ eris-db serve --work-dir <path-to-working-directory> -- will start the Eris-DB node based on the configuration file "server_config.toml" in the provided working directory`,
+  PreRun: func(cmd *cobra.Command, args []string) {
+    // if WorkDir was not set by a flag or by $ERIS_DB_WORKDIR
+    // NOTE [ben]: we can consider an `Explicit` flag that eliminates
+    // the use of any assumptions while starting Eris-DB
+    if do.WorkDir == "" {
+      if currentDirectory, err := os.Getwd(); err != nil {
+        log.Fatalf("No directory provided and failed to get current working directory: %v", err)
+        os.Exit(1)
+      } else {
+        log.Warn("No working directory provided in ERIS_DB_WORKDIR or --work-dir\n" +
+          "Will use current working directory ", currentDirectory)
+        do.WorkDir = currentDirectory
+      }
+    }
+    if !util.IsDir(do.WorkDir) {
+      log.Fatalf("Provided working directory %s is not a directory", do.WorkDir)
+    }
+    log.Debug("Working directory is set as %s", do.WorkDir)
   },
+  Run: Serve,
 }
 
 // build the serve subcommand
@@ -44,12 +65,17 @@ func buildServeCommand() {
 func addServeFlags() {
   ServeCmd.PersistentFlags().StringVarP(&do.WorkDir, "work-dir", "w",
     defaultWorkDir(), "specify the working directory for the chain to run.  If omitted, and no path set in $ERIS_DB_WORKDIR, the current working directory is taken.")
+  ServeCmd.PersistentFlags().StringVarP(&do.DataDir, "data-dir", "a",
+    defaultDataDir(), "specify the data directory.  If omitted and not set in $ERIS_DB_DATADIR, <working_directory>/data is taken.")
 }
 
 //------------------------------------------------------------------------------
 // functions
 
-func serve() {
+// serve() prepares the environment and sets up the core for Eris_DB to run.
+// After the setup succeeds, serve() starts the core and halts for core to
+// terminate.
+func Serve(cmd *cobra.Command, args []string) {
   // load configuration from a single location to avoid a wrong configuration
   // file is loaded.
   if err := do.ReadConfig(do.WorkDir, "server_config", "toml"); err != nil {
@@ -62,31 +88,28 @@ func serve() {
     log.Fatalf("Failed to read non-empty string for ChainId from config.")
     os.Exit(1)
   }
-
   log.Info("Eris-DB serve initializing ", do.ChainId, " from ", do.WorkDir)
-}
 
+  // Ensure data directory is set and accesible
+  if err := do.InitialiseDataDirectory(); err != nil {
+    log.Fatalf("Failed to initialise data directory (%s): %v", do.DataDir, err)
+    os.Exit(1)
+  }
+  log.Debug(fmt.Sprintf("Data directory is set at %s", do.DataDir))
+
+
+}
 
 //------------------------------------------------------------------------------
 // Defaults
 
 func defaultWorkDir() string {
   // if ERIS_DB_WORKDIR environment variable is not set, keep do.WorkDir empty
-  providedDirectory := setDefaultString("ERIS_DB_WORKDIR", "")
-  if providedDirectory == "" {
-    if currentDirectory, err := os.Getwd(); err != nil {
-      log.Fatalf("No directory provided and failed to get current working directory: %v", err)
-      os.Exit(1)
-    } else {
-      log.Warn("No working directory provided in ERIS_DB_WORKDIR or --work-dir\n" +
-        "Will use current working directory ", currentDirectory)
-      return currentDirectory
-    }
-  }
-  return providedDirectory
+  return setDefaultString("ERIS_DB_WORKDIR", "")
 }
 
-func defaultToCurrentWorkingDirectory() {
-  if do.WorkDir == "" {
-  }
+func defaultDataDir() string {
+  // As the default data directory depends on the default working directory,
+  // wait setting a default value, and initialise the data directory from serve()
+  return setDefaultString("ERIS_DB_DATADIR", "")
 }
diff --git a/definitions/do.go b/definitions/do.go
index 5ecc2f1c..0410a456 100644
--- a/definitions/do.go
+++ b/definitions/do.go
@@ -17,7 +17,12 @@
 package definitions
 
 import (
+  "path"
+  "os"
+
   viper "github.com/spf13/viper"
+
+  util "github.com/eris-ltd/eris-db/util"
 )
 
 type Do struct {
@@ -25,7 +30,14 @@ type Do struct {
   // only set through command line flags or environment variables
 	Debug        bool     // ERIS_DB_DEBUG
 	Verbose      bool     // ERIS_DB_VERBOSE
-  WorkDir      string
+
+  // Work directory is the root directory for Eris-DB to act in
+  WorkDir      string   // ERIS_DB_WORKDIR
+  // Data directory is defaulted to WorkDir + `/data`.
+  // If Eris-CLI maps a data container, DataDir is intended to point
+  // to that mapped data directory.
+  DataDir      string   // ERIS_DB_DATADIR
+
   // Capital configuration options explicitly extracted from the Viper config
 	ChainId      string   // has to be set to non-empty string,
                         // uniquely identifying the chain.
@@ -43,9 +55,8 @@ func NowDo() *Do {
 	do := new(Do)
 	do.Debug = false
 	do.Verbose = false
-	// the default value for output is set to true in cmd/eris-db.go;
-	// avoid double setting it here though
   do.WorkDir = ""
+  do.DataDir = ""
   do.ChainId = ""
 	do.Config = viper.New()
 	return do
@@ -64,3 +75,11 @@ func (d *Do) ReadConfig(directory string, name string, configType string) error
   d.Config.AddConfigPath(directory)
   return d.Config.ReadInConfig()
 }
+
+// InitialiseDataDirectory will default to WorkDir/data if DataDir is empty
+func (d *Do) InitialiseDataDirectory() error {
+  if d.DataDir == "" {
+    d.DataDir = path.Join(d.WorkDir, "data")
+  }
+  return util.EnsureDir(d.DataDir, os.ModePerm)
+}
diff --git a/util/fs.go b/util/fs.go
new file mode 100644
index 00000000..b33450b3
--- /dev/null
+++ b/util/fs.go
@@ -0,0 +1,46 @@
+// Copyright 2015, 2016 Eris Industries (UK) Ltd.
+// This file is part of Eris-RT
+
+// Eris-RT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Eris-RT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Eris-RT.  If not, see <http://www.gnu.org/licenses/>.
+
+package util
+
+import (
+  "fmt"
+  "os"
+)
+
+// Ensure the directory exists or create it if needed.
+func EnsureDir(dir string, mode os.FileMode) error {
+  if fileOptions, err := os.Stat(dir); os.IsNotExist(err) {
+    err := os.MkdirAll(dir, mode)
+    if err != nil {
+      return fmt.Errorf("Could not create directory %v. %v\n", dir, err)
+    }
+  } else if err != nil {
+    return fmt.Errorf("Error asserting directory %s: %v", dir, err)
+  } else if !fileOptions.IsDir() {
+    return fmt.Errorf("Path already exists as a file: %s\n", dir)
+  }
+  return nil
+}
+
+// Check whether the provided directory exists
+func IsDir(directory string) bool {
+  fileInfo, err := os.Stat(directory)
+  if err != nil {
+    return false
+  }
+  return fileInfo.IsDir()
+}
-- 
GitLab