Add logsDir setting and more
[src/xds/xds-agent.git] / lib / xdsconfig / fileconfig.go
1 package xdsconfig
2
3 import (
4         "encoding/json"
5         "fmt"
6         "os"
7         "os/user"
8         "path"
9         "path/filepath"
10         "regexp"
11 )
12
13 type SyncThingConf struct {
14         BinDir     string `json:"binDir"`
15         Home       string `json:"home"`
16         GuiAddress string `json:"gui-address"`
17         GuiAPIKey  string `json:"gui-apikey"`
18 }
19
20 type FileConfig struct {
21         HTTPPort string         `json:"httpPort"`
22         LogsDir  string         `json:"logsDir"`
23         SThgConf *SyncThingConf `json:"syncthing"`
24 }
25
26 // getConfigFromFile reads configuration from a config file.
27 // Order to determine which config file is used:
28 //  1/ from command line option: "--config myConfig.json"
29 //  2/ $HOME/.xds/agent-config.json file
30 //  3/ <current_dir>/agent-config.json file
31 //  4/ <executable dir>/agent-config.json file
32
33 func updateConfigFromFile(c *Config, confFile string) (*FileConfig, error) {
34
35         searchIn := make([]string, 0, 3)
36         if confFile != "" {
37                 searchIn = append(searchIn, confFile)
38         }
39         if usr, err := user.Current(); err == nil {
40                 searchIn = append(searchIn, path.Join(usr.HomeDir, ".xds", "agent-config.json"))
41         }
42         cwd, err := os.Getwd()
43         if err == nil {
44                 searchIn = append(searchIn, path.Join(cwd, "agent-config.json"))
45         }
46         exePath, err := filepath.Abs(filepath.Dir(os.Args[0]))
47         if err == nil {
48                 searchIn = append(searchIn, path.Join(exePath, "agent-config.json"))
49         }
50
51         var cFile *string
52         for _, p := range searchIn {
53                 if _, err := os.Stat(p); err == nil {
54                         cFile = &p
55                         break
56                 }
57         }
58         fCfg := FileConfig{}
59         if cFile == nil {
60                 // No config file found
61                 return &fCfg, nil
62         }
63
64         c.Log.Infof("Use config file: %s", *cFile)
65
66         // TODO move on viper package to support comments in JSON and also
67         // bind with flags (command line options)
68         // see https://github.com/spf13/viper#working-with-flags
69
70         fd, _ := os.Open(*cFile)
71         defer fd.Close()
72         if err := json.NewDecoder(fd).Decode(&fCfg); err != nil {
73                 return nil, err
74         }
75
76         // Support environment variables (IOW ${MY_ENV_VAR} syntax) in agent-config.json
77         for _, field := range []*string{
78                 &fCfg.LogsDir,
79                 &fCfg.SThgConf.Home,
80                 &fCfg.SThgConf.BinDir} {
81
82                 rep, err := resolveEnvVar(*field)
83                 if err != nil {
84                         return nil, err
85                 }
86                 *field = path.Clean(rep)
87         }
88
89         // Config file settings overwrite default config
90         if fCfg.HTTPPort != "" {
91                 c.HTTPPort = fCfg.HTTPPort
92         }
93
94         return &fCfg, nil
95 }
96
97 // resolveEnvVar Resolved environment variable regarding the syntax ${MYVAR}
98 func resolveEnvVar(s string) (string, error) {
99         re := regexp.MustCompile("\\${(.*)}")
100         vars := re.FindAllStringSubmatch(s, -1)
101         res := s
102         for _, v := range vars {
103                 val := os.Getenv(v[1])
104                 if val == "" {
105                         return res, fmt.Errorf("ERROR: %s env variable not defined", v[1])
106                 }
107
108                 rer := regexp.MustCompile("\\${" + v[1] + "}")
109                 res = rer.ReplaceAllString(res, val)
110         }
111
112         return res, nil
113 }