summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/platform/main.go22
-rw-r--r--utils/config.go100
-rw-r--r--utils/config_test.go174
3 files changed, 252 insertions, 44 deletions
diff --git a/cmd/platform/main.go b/cmd/platform/main.go
index b5ea51920..6d9952c70 100644
--- a/cmd/platform/main.go
+++ b/cmd/platform/main.go
@@ -6,19 +6,10 @@ package main
import (
"fmt"
"os"
- "path/filepath"
"syscall"
-)
-func findMattermostBinary() string {
- for _, file := range []string{"./mattermost", "../mattermost", "./bin/mattermost"} {
- path, _ := filepath.Abs(file)
- if stat, err := os.Stat(path); err == nil && !stat.IsDir() {
- return path
- }
- }
- return "./mattermost"
-}
+ "github.com/mattermost/mattermost-server/utils"
+)
func main() {
// Print angry message to use mattermost command directly
@@ -33,7 +24,14 @@ The platform binary will be removed in a future version.
args := os.Args
args[0] = "mattermost"
args = append(args, "--platform")
- if err := syscall.Exec(findMattermostBinary(), args, nil); err != nil {
+
+ realMattermost := utils.FindFile("mattermost")
+ if realMattermost == "" {
+ // This will still fail, of course.
+ realMattermost = "./mattermost"
+ }
+
+ if err := syscall.Exec(utils.FindFile("mattermost"), args, nil); err != nil {
fmt.Println("Could not start Mattermost, use the mattermost command directly.")
}
}
diff --git a/utils/config.go b/utils/config.go
index 00fd2642a..da1918490 100644
--- a/utils/config.go
+++ b/utils/config.go
@@ -32,36 +32,94 @@ const (
LOG_FILENAME = "mattermost.log"
)
-// FindConfigFile attempts to find an existing configuration file. fileName can be an absolute or
-// relative path or name such as "/opt/mattermost/config.json" or simply "config.json". An empty
-// string is returned if no configuration is found.
-func FindConfigFile(fileName string) (path string) {
- if filepath.IsAbs(fileName) {
- if _, err := os.Stat(fileName); err == nil {
- return fileName
+var (
+ commonBaseSearchPaths = []string{
+ ".",
+ "..",
+ "../..",
+ "../../..",
+ }
+)
+
+func FindPath(path string, baseSearchPaths []string, filter func(os.FileInfo) bool) string {
+ if filepath.IsAbs(path) {
+ if _, err := os.Stat(path); err == nil {
+ return path
}
- } else {
- for _, dir := range []string{"./config", "../config", "../../config", "../../../config", "."} {
- path, _ := filepath.Abs(filepath.Join(dir, fileName))
- if _, err := os.Stat(path); err == nil {
- return path
+
+ return ""
+ }
+
+ searchPaths := []string{}
+ for _, baseSearchPath := range baseSearchPaths {
+ searchPaths = append(searchPaths, baseSearchPath)
+ }
+
+ // Additionally attempt to search relative to the location of the running binary.
+ var binaryDir string
+ if exe, err := os.Executable(); err == nil {
+ if exe, err = filepath.EvalSymlinks(exe); err == nil {
+ if exe, err = filepath.Abs(exe); err == nil {
+ binaryDir = filepath.Dir(exe)
}
}
}
- return ""
-}
+ if binaryDir != "" {
+ for _, baseSearchPath := range baseSearchPaths {
+ searchPaths = append(
+ searchPaths,
+ filepath.Join(binaryDir, baseSearchPath),
+ )
+ }
+ }
-// FindDir looks for the given directory in nearby ancestors, falling back to `./` if not found.
-func FindDir(dir string) (string, bool) {
- for _, parent := range []string{".", "..", "../..", "../../.."} {
- foundDir, err := filepath.Abs(filepath.Join(parent, dir))
+ for _, parent := range searchPaths {
+ found, err := filepath.Abs(filepath.Join(parent, path))
if err != nil {
continue
- } else if _, err := os.Stat(foundDir); err == nil {
- return foundDir, true
+ } else if fileInfo, err := os.Stat(found); err == nil {
+ if filter != nil && filter(fileInfo) {
+ return found
+ } else {
+ return found
+ }
}
}
- return "./", false
+
+ return ""
+}
+
+// FindConfigFile attempts to find an existing configuration file. fileName can be an absolute or
+// relative path or name such as "/opt/mattermost/config.json" or simply "config.json". An empty
+// string is returned if no configuration is found.
+func FindConfigFile(fileName string) (path string) {
+ found := FindFile(filepath.Join("config", fileName))
+ if found == "" {
+ found = FindPath(fileName, []string{"."}, nil)
+ }
+
+ return found
+}
+
+// FindFile looks for the given file in nearby ancestors relative to the current working
+// directory as well as the directory of the executable.
+func FindFile(path string) string {
+ return FindPath(path, commonBaseSearchPaths, func(fileInfo os.FileInfo) bool {
+ return !fileInfo.IsDir()
+ })
+}
+
+// FindDir looks for the given directory in nearby ancestors relative to the current working
+// directory as well as the directory of the executable, falling back to `./` if not found.
+func FindDir(dir string) (string, bool) {
+ found := FindPath(dir, commonBaseSearchPaths, func(fileInfo os.FileInfo) bool {
+ return fileInfo.IsDir()
+ })
+ if found == "" {
+ return "./", false
+ }
+
+ return found, true
}
func MloggerConfigFromLoggerConfig(s *model.LogSettings) *mlog.LoggerConfiguration {
diff --git a/utils/config_test.go b/utils/config_test.go
index 75bbc420f..f57add303 100644
--- a/utils/config_test.go
+++ b/utils/config_test.go
@@ -46,20 +46,172 @@ func TestTimezoneConfig(t *testing.T) {
}
func TestFindConfigFile(t *testing.T) {
- dir, err := ioutil.TempDir("", "")
- require.NoError(t, err)
- defer os.RemoveAll(dir)
+ t.Run("config.json in current working directory, not inside config/", func(t *testing.T) {
+ // Force a unique working directory
+ cwd, err := ioutil.TempDir("", "")
+ require.NoError(t, err)
+ defer os.RemoveAll(cwd)
+
+ prevDir, err := os.Getwd()
+ require.NoError(t, err)
+ defer os.Chdir(prevDir)
+ os.Chdir(cwd)
+
+ configJson, err := filepath.Abs("config.json")
+ require.NoError(t, err)
+ require.NoError(t, ioutil.WriteFile(configJson, []byte("{}"), 0600))
+
+ // Relative paths end up getting symlinks fully resolved.
+ configJsonResolved, err := filepath.EvalSymlinks(configJson)
+ require.NoError(t, err)
+
+ assert.Equal(t, configJsonResolved, FindConfigFile("config.json"))
+ })
- path := filepath.Join(dir, "config.json")
- require.NoError(t, ioutil.WriteFile(path, []byte("{}"), 0600))
+ t.Run("config/config.json from various paths", func(t *testing.T) {
+ // Create the following directory structure:
+ // tmpDir1/
+ // config/
+ // config.json
+ // tmpDir2/
+ // tmpDir3/
+ // tmpDir4/
+ // tmpDir5/
+ tmpDir1, err := ioutil.TempDir("", "")
+ require.NoError(t, err)
+ defer os.RemoveAll(tmpDir1)
+
+ err = os.Mkdir(filepath.Join(tmpDir1, "config"), 0700)
+ require.NoError(t, err)
+
+ tmpDir2, err := ioutil.TempDir(tmpDir1, "")
+ require.NoError(t, err)
+
+ tmpDir3, err := ioutil.TempDir(tmpDir2, "")
+ require.NoError(t, err)
+
+ tmpDir4, err := ioutil.TempDir(tmpDir3, "")
+ require.NoError(t, err)
+
+ tmpDir5, err := ioutil.TempDir(tmpDir4, "")
+ require.NoError(t, err)
+
+ configJson := filepath.Join(tmpDir1, "config", "config.json")
+ require.NoError(t, ioutil.WriteFile(configJson, []byte("{}"), 0600))
+
+ // Relative paths end up getting symlinks fully resolved, so use this below as necessary.
+ configJsonResolved, err := filepath.EvalSymlinks(configJson)
+ require.NoError(t, err)
+
+ testCases := []struct {
+ Description string
+ Cwd *string
+ FileName string
+ Expected string
+ }{
+ {
+ "absolute path to config.json",
+ nil,
+ configJson,
+ configJson,
+ },
+ {
+ "absolute path to config.json from directory containing config.json",
+ &tmpDir1,
+ configJson,
+ configJson,
+ },
+ {
+ "relative path to config.json from directory containing config.json",
+ &tmpDir1,
+ "config.json",
+ configJsonResolved,
+ },
+ {
+ "subdirectory of directory containing config.json",
+ &tmpDir2,
+ "config.json",
+ configJsonResolved,
+ },
+ {
+ "twice-nested subdirectory of directory containing config.json",
+ &tmpDir3,
+ "config.json",
+ configJsonResolved,
+ },
+ {
+ "thrice-nested subdirectory of directory containing config.json",
+ &tmpDir4,
+ "config.json",
+ configJsonResolved,
+ },
+ {
+ "can't find from four nesting levels deep",
+ &tmpDir5,
+ "config.json",
+ "",
+ },
+ }
- assert.Equal(t, path, FindConfigFile(path))
+ for _, testCase := range testCases {
+ t.Run(testCase.Description, func(t *testing.T) {
+ if testCase.Cwd != nil {
+ prevDir, err := os.Getwd()
+ require.NoError(t, err)
+ defer os.Chdir(prevDir)
+ os.Chdir(*testCase.Cwd)
+ }
+
+ assert.Equal(t, testCase.Expected, FindConfigFile(testCase.FileName))
+ })
+ }
+ })
+
+ t.Run("config/config.json relative to executable", func(t *testing.T) {
+ osExecutable, err := os.Executable()
+ require.NoError(t, err)
+ osExecutableDir := filepath.Dir(osExecutable)
+
+ // Force a working directory different than the executable.
+ cwd, err := ioutil.TempDir("", "")
+ require.NoError(t, err)
+ defer os.RemoveAll(cwd)
+
+ prevDir, err := os.Getwd()
+ require.NoError(t, err)
+ defer os.Chdir(prevDir)
+ os.Chdir(cwd)
+
+ testCases := []struct {
+ Description string
+ RelativePath string
+ }{
+ {
+ "config/config.json",
+ ".",
+ },
+ {
+ "../config/config.json",
+ "../",
+ },
+ }
- prevDir, err := os.Getwd()
- require.NoError(t, err)
- defer os.Chdir(prevDir)
- os.Chdir(dir)
- assert.Equal(t, path, FindConfigFile(path))
+ for _, testCase := range testCases {
+ t.Run(testCase.Description, func(t *testing.T) {
+ // Install the config in config/config.json relative to the executable
+ configJson := filepath.Join(osExecutableDir, testCase.RelativePath, "config", "config.json")
+ require.NoError(t, os.Mkdir(filepath.Dir(configJson), 0700))
+ require.NoError(t, ioutil.WriteFile(configJson, []byte("{}"), 0600))
+ defer os.RemoveAll(filepath.Dir(configJson))
+
+ // Relative paths end up getting symlinks fully resolved.
+ configJsonResolved, err := filepath.EvalSymlinks(configJson)
+ require.NoError(t, err)
+
+ assert.Equal(t, configJsonResolved, FindConfigFile("config.json"))
+ })
+ }
+ })
}
func TestConfigFromEnviroVars(t *testing.T) {