diff --git a/dvid/utils.go b/dvid/utils.go index 2278d186..d70c88ce 100644 --- a/dvid/utils.go +++ b/dvid/utils.go @@ -147,6 +147,22 @@ func (fname Filename) HasExtensionPrefix(exts ...string) bool { return false } +// Converts the given (possibly) relative path into an absolute path, +// relative to the given anchor directory, not the current working directory. +// If the given relativePath is already an absolute path, +// it is returned unchanged. +func ConvertToAbsolute(relativePath string, anchorDir string) (string, error) { + if filepath.IsAbs(relativePath) { + return relativePath, nil + } + absDir, err := filepath.Abs(anchorDir) + if err != nil { + return relativePath, fmt.Errorf("Could not decode TOML config: %v\n", err) + } + absPath := filepath.Join(absDir, relativePath) + return absPath, nil +} + // DataFromPost returns data submitted in the given key of a POST request. func DataFromPost(r *http.Request, key string) ([]byte, error) { f, _, err := r.FormFile(key) diff --git a/server/server_local.go b/server/server_local.go index f452cfff..398828d2 100644 --- a/server/server_local.go +++ b/server/server_local.go @@ -11,6 +11,7 @@ package server import ( "bytes" "fmt" + "path/filepath" "net/smtp" "os" "os/exec" @@ -68,6 +69,45 @@ type tomlConfig struct { Groupcache storage.GroupcacheConfig } +// Some settings in the TOML can be given as relative paths. +// This function converts them in-place to absolute paths, +// assuming the given paths were relative to the TOML file's own directory. +func (c *tomlConfig) ConvertPathsToAbsolute(configPath string) error { + var err error + + configDir := filepath.Dir(configPath) + + // [server].webClient + c.Server.WebClient, err = dvid.ConvertToAbsolute(c.Server.WebClient, configDir) + if err != nil { + return fmt.Errorf("Error converting webClient setting to absolute path") + } + + // [logging].logfile + c.Logging.Logfile, err = dvid.ConvertToAbsolute(c.Logging.Logfile, configDir) + if err != nil { + return fmt.Errorf("Error converting logfile setting to absolute path") + } + + // [store.foobar].path + for alias, sc := range c.Store { + p, ok := sc["path"] + if !ok { + continue + } + path, ok := p.(string) + if !ok { + return fmt.Errorf("Don't understand path setting for store %q", alias) + } + absPath, err := dvid.ConvertToAbsolute(path, configDir) + if err != nil { + return fmt.Errorf("Error converting store.%s.path to absolute path: %q", alias, path) + } + sc["path"] = absPath + } + return nil +} + func (c tomlConfig) Stores() (map[storage.Alias]dvid.StoreConfig, error) { stores := make(map[storage.Alias]dvid.StoreConfig, len(c.Store)) for alias, sc := range c.Store { @@ -154,11 +194,15 @@ func LoadConfig(filename string) (*datastore.InstanceConfig, *dvid.LogConfig, *s if _, err := toml.DecodeFile(filename, &tc); err != nil { return nil, nil, nil, fmt.Errorf("Could not decode TOML config: %v\n", err) } + var err error + err = tc.ConvertPathsToAbsolute(filename) + if err != nil { + return nil, nil, nil, fmt.Errorf("Could not convert relative paths to absolute paths in TOML config: %v\n", err) + } // Get all defined stores. backend := new(storage.Backend) backend.Groupcache = tc.Groupcache - var err error backend.Stores, err = tc.Stores() if err != nil { return nil, nil, nil, err diff --git a/server/server_local_test.go b/server/server_local_test.go index cb51f8d0..ad6abcd3 100644 --- a/server/server_local_test.go +++ b/server/server_local_test.go @@ -4,6 +4,7 @@ import ( "io/ioutil" "os" "testing" + "github.com/janelia-flyem/dvid/storage" ) func loadConfigFile(t *testing.T, filename string) string { @@ -35,3 +36,49 @@ func TestParseConfig(t *testing.T) { t.Errorf("Bad backend configuration retrieval: %v\n", backendCfg) } } + +func TestTOMLConfigAbsolutePath(t *testing.T) { + // Initialize the filepath settings + var c tomlConfig + c.Server.WebClient = "dvid-distro/dvid-console" + c.Logging.Logfile = "./foobar.log" + + c.Store = make(map[storage.Alias]storeConfig) + + c.Store["foo"] = make(storeConfig) + c.Store["foo"]["engine"] = "basholeveldb" + c.Store["foo"]["path"] = "foo-storage-db" + + c.Store["bar"] = make(storeConfig) + c.Store["bar"]["engine"] = "basholeveldb" + c.Store["bar"]["path"] = "/tmp/bar-storage-db" // Already absolute, should stay unchanged. + + // Convert relative paths to absolute + c.ConvertPathsToAbsolute("/tmp/dvid-configs/myconfig.toml") + + // Checks + if c.Server.WebClient != "/tmp/dvid-configs/dvid-distro/dvid-console" { + t.Errorf("WebClient not correctly converted to absolute path: %s", c.Server.WebClient) + } + + if c.Logging.Logfile != "/tmp/dvid-configs/foobar.log" { + t.Errorf("Logfile not correctly converted to absolute path: %s", c.Logging.Logfile) + } + + foo, _ := c.Store["foo"] + path, _ := foo["path"] + if path.(string) != "/tmp/dvid-configs/foo-storage-db" { + t.Errorf("[store.foo].path not correctly converted to absolute path: %s", path) + } + + engine, _ := foo["engine"] + if engine.(string) != "basholeveldb" { + t.Errorf("[store.foo].engine should not have been touched: %s", path) + } + + bar, _ := c.Store["bar"] + path, _ = bar["path"] + if path.(string) != "/tmp/bar-storage-db" { + t.Errorf("[store.bar].path was already absolute and should have been left unchanged: %s", path) + } +}