86f39b5bdf64508603850822c86cb6542baefc43
[src/xds/xds-server.git] / lib / xdsconfig / fileconfig.go
1 /*
2  * Copyright (C) 2017-2018 "IoT.bzh"
3  * Author Sebastien Douheret <sebastien@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 package xdsconfig
19
20 import (
21         "encoding/json"
22         "os"
23         "os/user"
24         "path"
25         "path/filepath"
26         "strings"
27
28         common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git/golib"
29 )
30
31 // ConfigDir Directory in user HOME directory where xds config will be saved
32 var ConfigDir = path.Join(".xds", "server")
33
34 const (
35         // GlobalConfigFilename Global config filename
36         GlobalConfigFilename = "server-config.json"
37         // ServerDataFilename Server data filename
38         ServerDataFilename = "server-data.xml"
39         // FoldersConfigFilename Folders config filename
40         FoldersConfigFilename = "server-config_folders.xml"
41         // TargetsConfigFilename Targets config filename
42         TargetsConfigFilename = "server-config_targets.xml"
43 )
44
45 // SyncThingConf definition
46 type SyncThingConf struct {
47         BinDir          string `json:"binDir"`
48         Home            string `json:"home"`
49         GuiAddress      string `json:"gui-address"`
50         GuiAPIKey       string `json:"gui-apikey"`
51         RescanIntervalS int    `json:"rescanIntervalS"`
52 }
53
54 // FileConfig is the JSON structure of xds-server config file (server-config.json)
55 type FileConfig struct {
56         WebAppDir     string         `json:"webAppDir"`
57         ShareRootDir  string         `json:"shareRootDir"`
58         SdkScriptsDir string         `json:"sdkScriptsDir"`
59         SdkDbUpdate   string         `json:"sdkDbUpdate"`
60         HTTPPort      string         `json:"httpPort"`
61         SThgConf      *SyncThingConf `json:"syncthing"`
62         LogsDir       string         `json:"logsDir"`
63 }
64
65 // readGlobalConfig reads configuration from a config file.
66 // Order to determine which config file is used:
67 //  1/ from command line option: "--config myConfig.json"
68 //  2/ $HOME/.xds/server/server-config.json file
69 //  3/ /etc/xds/server/server-config.json file
70 //  4/ <xds-server executable dir>/server-config.json file
71 func readGlobalConfig(c *Config, confFile string) error {
72
73         searchIn := make([]string, 0, 3)
74         if confFile != "" {
75                 searchIn = append(searchIn, confFile)
76         }
77         if usr, err := user.Current(); err == nil {
78                 searchIn = append(searchIn, path.Join(usr.HomeDir, ConfigDir, GlobalConfigFilename))
79         }
80
81         searchIn = append(searchIn, "/etc/xds/server/server-config.json")
82
83         exePath := os.Args[0]
84         ee, _ := os.Executable()
85         exeAbsPath, err := filepath.Abs(ee)
86         if err == nil {
87                 exePath, err = filepath.EvalSymlinks(exeAbsPath)
88                 if err == nil {
89                         exePath = filepath.Dir(ee)
90                 } else {
91                         exePath = filepath.Dir(exeAbsPath)
92                 }
93         }
94         searchIn = append(searchIn, path.Join(exePath, "server-config.json"))
95
96         var cFile *string
97         for _, p := range searchIn {
98                 if _, err := os.Stat(p); err == nil {
99                         cFile = &p
100                         break
101                 }
102         }
103         if cFile == nil {
104                 // No config file found
105                 return nil
106         }
107         c.Log.Infof("Use config file:       %s", *cFile)
108
109         // TODO move on viper package to support comments in JSON and also
110         // bind with flags (command line options)
111         // see https://github.com/spf13/viper#working-with-flags
112         fd, _ := os.Open(*cFile)
113         defer fd.Close()
114         fCfg := FileConfig{}
115         if err := json.NewDecoder(fd).Decode(&fCfg); err != nil {
116                 return err
117         }
118
119         // Support environment variables (IOW ${MY_ENV_VAR} syntax) in server-config.json
120         vars := []*string{
121                 &fCfg.WebAppDir,
122                 &fCfg.ShareRootDir,
123                 &fCfg.SdkScriptsDir,
124                 &fCfg.LogsDir}
125         if fCfg.SThgConf != nil {
126                 vars = append(vars, &fCfg.SThgConf.Home, &fCfg.SThgConf.BinDir)
127         }
128         for _, field := range vars {
129                 var err error
130                 if *field, err = common.ResolveEnvVar(*field); err != nil {
131                         return err
132                 }
133         }
134
135         // Use config file settings else use default config
136         if fCfg.WebAppDir == "" {
137                 fCfg.WebAppDir = c.FileConf.WebAppDir
138         }
139         if fCfg.ShareRootDir == "" {
140                 fCfg.ShareRootDir = c.FileConf.ShareRootDir
141         }
142         if fCfg.SdkScriptsDir == "" {
143                 fCfg.SdkScriptsDir = c.FileConf.SdkScriptsDir
144         }
145         if fCfg.SdkDbUpdate == "" {
146                 fCfg.SdkDbUpdate = c.FileConf.SdkDbUpdate
147         }
148         if fCfg.HTTPPort == "" {
149                 fCfg.HTTPPort = c.FileConf.HTTPPort
150         }
151         if fCfg.LogsDir == "" {
152                 fCfg.LogsDir = c.FileConf.LogsDir
153         }
154
155         // Resolve webapp dir (support relative or full path)
156         fCfg.WebAppDir = strings.Trim(fCfg.WebAppDir, " ")
157         if !strings.HasPrefix(fCfg.WebAppDir, "/") && exePath != "" {
158                 cwd, _ := os.Getwd()
159
160                 // Check first from current directory
161                 for _, rootD := range []string{exePath, cwd} {
162                         ff := path.Join(rootD, fCfg.WebAppDir, "index.html")
163                         if common.Exists(ff) {
164                                 fCfg.WebAppDir = path.Join(rootD, fCfg.WebAppDir)
165                                 break
166                         }
167                 }
168         }
169
170         c.FileConf = fCfg
171         return nil
172 }
173
174 func configFilenameGet(cfgFile string) (string, error) {
175         usr, err := user.Current()
176         if err != nil {
177                 return "", err
178         }
179         return path.Join(usr.HomeDir, ConfigDir, cfgFile), nil
180 }
181
182 // FoldersConfigFilenameGet Return the FoldersConfig filename
183 func FoldersConfigFilenameGet() (string, error) {
184         return configFilenameGet(FoldersConfigFilename)
185 }
186
187 // TargetsConfigFilenameGet Return the TargetsConfig filename
188 func TargetsConfigFilenameGet() (string, error) {
189         return configFilenameGet(TargetsConfigFilename)
190 }
191
192 // ServerDataFilenameGet Return the ServerData filename
193 func ServerDataFilenameGet() (string, error) {
194         return configFilenameGet(ServerDataFilename)
195 }