Initial main commit.
[src/xds/xds-server.git] / lib / xdsconfig / config.go
1 package xdsconfig
2
3 import (
4         "fmt"
5         "strings"
6
7         "os"
8
9         "time"
10
11         "github.com/Sirupsen/logrus"
12         "github.com/codegangsta/cli"
13         "github.com/iotbzh/xds-server/lib/syncthing"
14 )
15
16 // Config parameters (json format) of /config command
17 type Config struct {
18         Version       string        `json:"version"`
19         APIVersion    string        `json:"apiVersion"`
20         VersionGitTag string        `json:"gitTag"`
21         Builder       BuilderConfig `json:"builder"`
22         Folders       FoldersConfig `json:"folders"`
23
24         // Private / un-exported fields
25         progName     string
26         fileConf     FileConfig
27         WebAppDir    string         `json:"-"`
28         HTTPPort     string         `json:"-"`
29         ShareRootDir string         `json:"-"`
30         Log          *logrus.Logger `json:"-"`
31         SThg         *st.SyncThing  `json:"-"`
32 }
33
34 // Config default values
35 const (
36         DefaultAPIVersion = "1"
37         DefaultPort       = "8000"
38         DefaultShareDir   = "/mnt/share"
39         DefaultLogLevel   = "error"
40 )
41
42 // Init loads the configuration on start-up
43 func Init(ctx *cli.Context) (Config, error) {
44         var err error
45
46         // Set logger level and formatter
47         log := ctx.App.Metadata["logger"].(*logrus.Logger)
48
49         logLevel := ctx.GlobalString("log")
50         if logLevel == "" {
51                 logLevel = DefaultLogLevel
52         }
53         if log.Level, err = logrus.ParseLevel(logLevel); err != nil {
54                 fmt.Printf("Invalid log level : \"%v\"\n", logLevel)
55                 os.Exit(1)
56         }
57         log.Formatter = &logrus.TextFormatter{}
58
59         // Define default configuration
60         c := Config{
61                 Version:       ctx.App.Metadata["version"].(string),
62                 APIVersion:    DefaultAPIVersion,
63                 VersionGitTag: ctx.App.Metadata["git-tag"].(string),
64                 Builder:       BuilderConfig{},
65                 Folders:       FoldersConfig{},
66
67                 progName:     ctx.App.Name,
68                 WebAppDir:    "webapp/dist",
69                 HTTPPort:     DefaultPort,
70                 ShareRootDir: DefaultShareDir,
71                 Log:          log,
72                 SThg:         nil,
73         }
74
75         // config file settings overwrite default config
76         err = updateConfigFromFile(&c, ctx.GlobalString("config"))
77         if err != nil {
78                 return Config{}, err
79         }
80
81         // Update location of shared dir if needed
82         if !dirExists(c.ShareRootDir) {
83                 if err := os.MkdirAll(c.ShareRootDir, 0770); err != nil {
84                         c.Log.Fatalf("No valid shared directory found (err=%v)", err)
85                 }
86         }
87         c.Log.Infoln("Share root directory: ", c.ShareRootDir)
88
89         // FIXME - add a builder interface and support other builder type (eg. native)
90         builderType := "syncthing"
91
92         switch builderType {
93         case "syncthing":
94                 // Syncthing settings only configurable from config.json file
95                 stGuiAddr := c.fileConf.SThgConf.GuiAddress
96                 stGuiApikey := c.fileConf.SThgConf.GuiAPIKey
97                 if stGuiAddr == "" {
98                         stGuiAddr = "http://localhost:8384"
99                 }
100                 if stGuiAddr[0:7] != "http://" {
101                         stGuiAddr = "http://" + stGuiAddr
102                 }
103
104                 // Retry if connection fail
105                 retry := 5
106                 for retry > 0 {
107                         c.SThg = st.NewSyncThing(stGuiAddr, stGuiApikey, c.Log)
108                         if c.SThg != nil {
109                                 break
110                         }
111                         c.Log.Warningf("Establishing connection to Syncthing (retry %d/5)", retry)
112                         time.Sleep(time.Second)
113                         retry--
114                 }
115                 if c.SThg == nil {
116                         c.Log.Fatalf("ERROR: cannot connect to Syncthing (url: %s)", stGuiAddr)
117                 }
118
119                 // Retrieve Syncthing config
120                 id, err := c.SThg.IDGet()
121                 if err != nil {
122                         return Config{}, err
123                 }
124
125                 if c.Builder, err = NewBuilderConfig(id); err != nil {
126                         c.Log.Fatalln(err)
127                 }
128
129                 // Retrieve initial Syncthing config
130                 stCfg, err := c.SThg.ConfigGet()
131                 if err != nil {
132                         return Config{}, err
133                 }
134                 for _, stFld := range stCfg.Folders {
135                         relativePath := strings.TrimPrefix(stFld.RawPath, c.ShareRootDir)
136                         if relativePath == "" {
137                                 relativePath = stFld.RawPath
138                         }
139                         newFld := NewFolderConfig(stFld.ID, stFld.Label, c.ShareRootDir, strings.Trim(relativePath, "/"))
140                         c.Folders = c.Folders.Update(FoldersConfig{newFld})
141                 }
142
143         default:
144                 log.Fatalln("Unsupported builder type")
145         }
146
147         return c, nil
148 }
149
150 // GetFolderFromID retrieves the Folder config from id
151 func (c *Config) GetFolderFromID(id string) *FolderConfig {
152         if idx := c.Folders.GetIdx(id); idx != -1 {
153                 return &c.Folders[idx]
154         }
155         return nil
156 }
157
158 // UpdateAll updates all the current configuration
159 func (c *Config) UpdateAll(newCfg Config) error {
160         return fmt.Errorf("Not Supported")
161         /*
162                 if err := VerifyConfig(newCfg); err != nil {
163                         return err
164                 }
165
166                 // TODO: c.Builder = c.Builder.Update(newCfg.Builder)
167                 c.Folders = c.Folders.Update(newCfg.Folders)
168
169                 // SEB A SUP model.NotifyListeners(c, NotifyFoldersChange, FolderConfig{})
170                 // FIXME To be tested & improved error handling
171                 for _, f := range c.Folders {
172                         if err := c.SThg.FolderChange(st.FolderChangeArg{
173                                 ID:           f.ID,
174                                 Label:        f.Label,
175                                 RelativePath: f.RelativePath,
176                                 SyncThingID:  f.SyncThingID,
177                                 ShareRootDir: c.ShareRootDir,
178                         }); err != nil {
179                                 return err
180                         }
181                 }
182
183                 return nil
184         */
185 }
186
187 // UpdateFolder updates a specific folder into the current configuration
188 func (c *Config) UpdateFolder(newFolder FolderConfig) (FolderConfig, error) {
189         if err := FolderVerify(newFolder); err != nil {
190                 return FolderConfig{}, err
191         }
192
193         c.Folders = c.Folders.Update(FoldersConfig{newFolder})
194
195         // SEB A SUP model.NotifyListeners(c, NotifyFolderAdd, newFolder)
196         err := c.SThg.FolderChange(st.FolderChangeArg{
197                 ID:           newFolder.ID,
198                 Label:        newFolder.Label,
199                 RelativePath: newFolder.RelativePath,
200                 SyncThingID:  newFolder.SyncThingID,
201                 ShareRootDir: c.ShareRootDir,
202         })
203
204         newFolder.BuilderSThgID = c.Builder.SyncThingID // FIXME - should be removed after local ST config rework
205         newFolder.Status = FolderStatusEnable
206
207         return newFolder, err
208 }
209
210 // DeleteFolder deletes a specific folder
211 func (c *Config) DeleteFolder(id string) (FolderConfig, error) {
212         var fld FolderConfig
213         var err error
214
215         //SEB A SUP model.NotifyListeners(c, NotifyFolderDelete, fld)
216         if err = c.SThg.FolderDelete(id); err != nil {
217                 return fld, err
218         }
219
220         c.Folders, fld, err = c.Folders.Delete(id)
221
222         return fld, err
223 }
224
225 func dirExists(path string) bool {
226         _, err := os.Stat(path)
227         if os.IsNotExist(err) {
228                 return false
229         }
230         return true
231 }