1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
package xdsconfig
import (
"encoding/json"
"fmt"
"os"
"os/user"
"path"
"path/filepath"
"regexp"
"strings"
)
type SyncThingConf struct {
Home string `json:"home"`
GuiAddress string `json:"gui-address"`
GuiAPIKey string `json:"gui-apikey"`
}
type FileConfig struct {
WebAppDir string `json:"webAppDir"`
ShareRootDir string `json:"shareRootDir"`
HTTPPort string `json:"httpPort"`
SThgConf SyncThingConf `json:"syncthing"`
}
// getConfigFromFile reads configuration from a config file.
// Order to determine which config file is used:
// 1/ from command line option: "--config myConfig.json"
// 2/ $HOME/.xds/config.json file
// 3/ <current_dir>/agent-config.json file
// 4/ <xds-server executable dir>/config.json file
func updateConfigFromFile(c *Config, confFile string) error {
searchIn := make([]string, 0, 3)
if confFile != "" {
searchIn = append(searchIn, confFile)
}
if usr, err := user.Current(); err == nil {
searchIn = append(searchIn, path.Join(usr.HomeDir, ".xds", "config.json"))
}
cwd, err := os.Getwd()
if err == nil {
searchIn = append(searchIn, path.Join(cwd, "config.json"))
}
exePath, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err == nil {
searchIn = append(searchIn, path.Join(exePath, "config.json"))
}
var cFile *string
for _, p := range searchIn {
if _, err := os.Stat(p); err == nil {
cFile = &p
break
}
}
if cFile == nil {
// No config file found
return nil
}
c.Log.Infof("Use config file: %s", *cFile)
// TODO move on viper package to support comments in JSON and also
// bind with flags (command line options)
// see https://github.com/spf13/viper#working-with-flags
fd, _ := os.Open(*cFile)
defer fd.Close()
fCfg := FileConfig{}
if err := json.NewDecoder(fd).Decode(&fCfg); err != nil {
return err
}
c.fileConf = fCfg
// Support environment variables (IOW ${MY_ENV_VAR} syntax) in config.json
// TODO: better to use reflect package to iterate on fields and be more generic
var rep string
if rep, err = resolveEnvVar(fCfg.WebAppDir); err != nil {
return err
}
fCfg.WebAppDir = path.Clean(rep)
if rep, err = resolveEnvVar(fCfg.ShareRootDir); err != nil {
return err
}
fCfg.ShareRootDir = path.Clean(rep)
if rep, err = resolveEnvVar(fCfg.SThgConf.Home); err != nil {
return err
}
fCfg.SThgConf.Home = path.Clean(rep)
// Config file settings overwrite default config
if fCfg.WebAppDir != "" {
c.WebAppDir = strings.Trim(fCfg.WebAppDir, " ")
}
// Is it a full path ?
if !strings.HasPrefix(c.WebAppDir, "/") && exePath != "" {
// Check first from current directory
for _, rootD := range []string{cwd, exePath} {
ff := path.Join(rootD, c.WebAppDir, "index.html")
if exists(ff) {
c.WebAppDir = path.Join(rootD, c.WebAppDir)
break
}
}
}
if fCfg.ShareRootDir != "" {
c.ShareRootDir = fCfg.ShareRootDir
}
if fCfg.HTTPPort != "" {
c.HTTPPort = fCfg.HTTPPort
}
return nil
}
// resolveEnvVar Resolved environment variable regarding the syntax ${MYVAR}
func resolveEnvVar(s string) (string, error) {
re := regexp.MustCompile("\\${(.*)}")
vars := re.FindAllStringSubmatch(s, -1)
res := s
for _, v := range vars {
val := os.Getenv(v[1])
if val == "" {
return res, fmt.Errorf("ERROR: %s env variable not defined", v[1])
}
rer := regexp.MustCompile("\\${" + v[1] + "}")
res = rer.ReplaceAllString(res, val)
}
return res, nil
}
// exists returns whether the given file or directory exists or not
func exists(path string) bool {
_, err := os.Stat(path)
if err == nil {
return true
}
if os.IsNotExist(err) {
return false
}
return true
}
|