Use go module as dependency tool instead of glide
[src/xds/xds-server.git] / lib / xdsconfig / config.go
index 801891b..ad3346a 100644 (file)
+/*
+ * Copyright (C) 2017-2018 "IoT.bzh"
+ * Author Sebastien Douheret <sebastien@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package xdsconfig
 
 import (
        "fmt"
-       "strings"
-
+       "io"
        "os"
+       "os/user"
+       "path"
+       "path/filepath"
 
-       "time"
-
+       common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git"
+       "gerrit.automotivelinux.org/gerrit/src/xds/xds-server.git/lib/xsapiv1"
        "github.com/Sirupsen/logrus"
        "github.com/codegangsta/cli"
-       "github.com/iotbzh/xds-server/lib/syncthing"
 )
 
 // Config parameters (json format) of /config command
 type Config struct {
-       Version       string        `json:"version"`
-       APIVersion    string        `json:"apiVersion"`
-       VersionGitTag string        `json:"gitTag"`
-       Builder       BuilderConfig `json:"builder"`
-       Folders       FoldersConfig `json:"folders"`
-
-       // Private / un-exported fields
-       progName     string
-       fileConf     FileConfig
-       WebAppDir    string         `json:"-"`
-       HTTPPort     string         `json:"-"`
-       ShareRootDir string         `json:"-"`
-       Log          *logrus.Logger `json:"-"`
-       SThg         *st.SyncThing  `json:"-"`
+       // Public APIConfig fields
+       xsapiv1.APIConfig
+
+       // Private (un-exported fields in REST GET /config route)
+       Options       Options        `json:"-"`
+       FileConf      FileConfig     `json:"-"`
+       Log           *logrus.Logger `json:"-"`
+       LogVerboseOut io.Writer      `json:"-"`
+}
+
+// Options set at the command line
+type Options struct {
+       ConfigFile     string
+       LogLevel       string
+       LogFile        string
+       NoFolderConfig bool
 }
 
 // Config default values
 const (
-       DefaultAPIVersion = "1"
-       DefaultPort       = "8000"
-       DefaultShareDir   = "/mnt/share"
-       DefaultLogLevel   = "error"
+       DefaultAPIVersion         = "1"
+       DefaultPort               = "8000"
+       DefaultShareDir           = "projects"
+       DefaultSTHomeDir          = "syncthing-config"
+       DefaultSdkScriptsDir      = "${EXEPATH}/sdks"
+       DefaultXdsUtilsScriptsDir = "${EXEPATH}/xds-utils"
+       DefaultSdkDbUpdate        = "startup"
+       DefaultXdsSrvUpdateTime   = "24h"
 )
 
 // Init loads the configuration on start-up
-func Init(ctx *cli.Context) (Config, error) {
+func Init(cliCtx *cli.Context, log *logrus.Logger) (*Config, error) {
        var err error
 
-       // Set logger level and formatter
-       log := ctx.App.Metadata["logger"].(*logrus.Logger)
-
-       logLevel := ctx.GlobalString("log")
-       if logLevel == "" {
-               logLevel = DefaultLogLevel
+       dfltShareDir := path.Join(ConfigRootDir(), DefaultShareDir)
+       dfltSTHomeDir := path.Join(ConfigRootDir(), DefaultSTHomeDir)
+       if resDir, err := common.ResolveEnvVar(dfltShareDir); err == nil {
+               dfltShareDir = resDir
        }
-       if log.Level, err = logrus.ParseLevel(logLevel); err != nil {
-               fmt.Printf("Invalid log level : \"%v\"\n", logLevel)
-               os.Exit(1)
+       if resDir, err := common.ResolveEnvVar(dfltSTHomeDir); err == nil {
+               dfltSTHomeDir = resDir
+       }
+
+       // Retrieve Server ID (or create one the first time)
+       uuid, err := ServerIDGet()
+       if err != nil {
+               return nil, err
        }
-       log.Formatter = &logrus.TextFormatter{}
 
        // Define default configuration
        c := Config{
-               Version:       ctx.App.Metadata["version"].(string),
-               APIVersion:    DefaultAPIVersion,
-               VersionGitTag: ctx.App.Metadata["git-tag"].(string),
-               Builder:       BuilderConfig{},
-               Folders:       FoldersConfig{},
-
-               progName:     ctx.App.Name,
-               WebAppDir:    "webapp/dist",
-               HTTPPort:     DefaultPort,
-               ShareRootDir: DefaultShareDir,
-               Log:          log,
-               SThg:         nil,
+               APIConfig: xsapiv1.APIConfig{
+                       ServerUID:        uuid,
+                       Version:          cliCtx.App.Metadata["version"].(string),
+                       APIVersion:       DefaultAPIVersion,
+                       VersionGitTag:    cliCtx.App.Metadata["git-tag"].(string),
+                       Builder:          xsapiv1.BuilderConfig{},
+                       SupportedSharing: map[string]bool{xsapiv1.TypePathMap: true},
+               },
+
+               Options: Options{
+                       ConfigFile:     cliCtx.GlobalString("config"),
+                       LogLevel:       cliCtx.GlobalString("log"),
+                       LogFile:        cliCtx.GlobalString("logfile"),
+                       NoFolderConfig: cliCtx.GlobalBool("no-folderconfig"),
+               },
+               FileConf: FileConfig{
+                       WebAppDir:          "www",
+                       ShareRootDir:       dfltShareDir,
+                       SdkScriptsDir:      DefaultSdkScriptsDir,
+                       XdsUtilsScriptsDir: DefaultXdsUtilsScriptsDir,
+                       SdkDbUpdate:        DefaultSdkDbUpdate,
+                       HTTPPort:           DefaultPort,
+                       SThgConf:           &SyncThingConf{Home: dfltSTHomeDir},
+                       LogsDir:            "",
+                       XdsSrvUpdateTime:   DefaultXdsSrvUpdateTime,
+               },
+               Log: log,
        }
 
+       c.Log.Infoln("Server UUID:          ", uuid)
+
        // config file settings overwrite default config
-       err = updateConfigFromFile(&c, ctx.GlobalString("config"))
+       err = readGlobalConfig(&c, c.Options.ConfigFile)
        if err != nil {
-               return Config{}, err
+               return nil, err
        }
 
        // Update location of shared dir if needed
-       if !dirExists(c.ShareRootDir) {
-               if err := os.MkdirAll(c.ShareRootDir, 0770); err != nil {
-                       c.Log.Fatalf("No valid shared directory found (err=%v)", err)
+       if !common.Exists(c.FileConf.ShareRootDir) {
+               if err := os.MkdirAll(c.FileConf.ShareRootDir, 0770); err != nil {
+                       return nil, fmt.Errorf("No valid shared directory found: %v", err)
                }
        }
-       c.Log.Infoln("Share root directory: ", c.ShareRootDir)
-
-       // FIXME - add a builder interface and support other builder type (eg. native)
-       builderType := "syncthing"
-
-       switch builderType {
-       case "syncthing":
-               // Syncthing settings only configurable from config.json file
-               stGuiAddr := c.fileConf.SThgConf.GuiAddress
-               stGuiApikey := c.fileConf.SThgConf.GuiAPIKey
-               if stGuiAddr == "" {
-                       stGuiAddr = "http://localhost:8384"
-               }
-               if stGuiAddr[0:7] != "http://" {
-                       stGuiAddr = "http://" + stGuiAddr
-               }
-
-               // Retry if connection fail
-               retry := 5
-               for retry > 0 {
-                       c.SThg = st.NewSyncThing(stGuiAddr, stGuiApikey, c.Log)
-                       if c.SThg != nil {
-                               break
+       c.Log.Infoln("Share root directory: ", c.FileConf.ShareRootDir)
+
+       // Where Logs are redirected:
+       //  default 'stdout' (logfile option default value)
+       //  else use file (or filepath) set by --logfile option
+       //  that may be overwritten by LogsDir field of config file
+       logF := c.Options.LogFile
+       logD := c.FileConf.LogsDir
+       if logF != "stdout" {
+               if logD != "" {
+                       lf := filepath.Base(logF)
+                       if lf == "" || lf == "." {
+                               lf = "xds-server.log"
                        }
-                       c.Log.Warningf("Establishing connection to Syncthing (retry %d/5)", retry)
-                       time.Sleep(time.Second)
-                       retry--
-               }
-               if c.SThg == nil {
-                       c.Log.Fatalf("ERROR: cannot connect to Syncthing (url: %s)", stGuiAddr)
-               }
-
-               // Retrieve Syncthing config
-               id, err := c.SThg.IDGet()
-               if err != nil {
-                       return Config{}, err
-               }
-
-               if c.Builder, err = NewBuilderConfig(id); err != nil {
-                       c.Log.Fatalln(err)
-               }
-
-               // Retrieve initial Syncthing config
-               stCfg, err := c.SThg.ConfigGet()
-               if err != nil {
-                       return Config{}, err
+                       logF = filepath.Join(logD, lf)
+               } else {
+                       logD = filepath.Dir(logF)
                }
-               for _, stFld := range stCfg.Folders {
-                       relativePath := strings.TrimPrefix(stFld.RawPath, c.ShareRootDir)
-                       if relativePath == "" {
-                               relativePath = stFld.RawPath
-                       }
-                       newFld := NewFolderConfig(stFld.ID, stFld.Label, c.ShareRootDir, strings.Trim(relativePath, "/"))
-                       c.Folders = c.Folders.Update(FoldersConfig{newFld})
-               }
-
-       default:
-               log.Fatalln("Unsupported builder type")
        }
-
-       return c, nil
-}
-
-// GetFolderFromID retrieves the Folder config from id
-func (c *Config) GetFolderFromID(id string) *FolderConfig {
-       if idx := c.Folders.GetIdx(id); idx != -1 {
-               return &c.Folders[idx]
+       if logD == "" || logD == "." {
+               logD = "/tmp/xds/logs"
        }
-       return nil
-}
+       c.Options.LogFile = logF
+       c.FileConf.LogsDir = logD
 
-// UpdateAll updates all the current configuration
-func (c *Config) UpdateAll(newCfg Config) error {
-       return fmt.Errorf("Not Supported")
-       /*
-               if err := VerifyConfig(newCfg); err != nil {
-                       return err
+       if c.FileConf.LogsDir != "" && !common.Exists(c.FileConf.LogsDir) {
+               if err := os.MkdirAll(c.FileConf.LogsDir, 0770); err != nil {
+                       return nil, fmt.Errorf("Cannot create logs dir: %v", err)
                }
+       }
 
-               // TODO: c.Builder = c.Builder.Update(newCfg.Builder)
-               c.Folders = c.Folders.Update(newCfg.Folders)
-
-               // SEB A SUP model.NotifyListeners(c, NotifyFoldersChange, FolderConfig{})
-               // FIXME To be tested & improved error handling
-               for _, f := range c.Folders {
-                       if err := c.SThg.FolderChange(st.FolderChangeArg{
-                               ID:           f.ID,
-                               Label:        f.Label,
-                               RelativePath: f.RelativePath,
-                               SyncThingID:  f.SyncThingID,
-                               ShareRootDir: c.ShareRootDir,
-                       }); err != nil {
-                               return err
-                       }
-               }
+       c.Log.Infoln("Logs file:            ", c.Options.LogFile)
+       c.Log.Infoln("Logs directory:       ", c.FileConf.LogsDir)
 
-               return nil
-       */
+       return &c, nil
 }
 
-// UpdateFolder updates a specific folder into the current configuration
-func (c *Config) UpdateFolder(newFolder FolderConfig) (FolderConfig, error) {
-       // rootPath should not be empty
-       if newFolder.rootPath == "" {
-               newFolder.rootPath = c.ShareRootDir
+// ConfigRootDir return the root directory where xds server save all config files
+func ConfigRootDir() string {
+       root := "$HOME"
+       if usr, err := user.Current(); err == nil {
+               root = usr.HomeDir
        }
 
-       // Sanity check of folder settings
-       if err := FolderVerify(newFolder); err != nil {
-               return FolderConfig{}, err
+       // Default $HOME/.xds/server but may be changed by an env variable
+       if envVar, envDef := os.LookupEnv("XDS_SERVER_ROOT_CFG_DIR"); envDef {
+               root = envVar
        }
 
-       c.Folders = c.Folders.Update(FoldersConfig{newFolder})
-
-       // SEB A SUP model.NotifyListeners(c, NotifyFolderAdd, newFolder)
-       err := c.SThg.FolderChange(st.FolderChangeArg{
-               ID:           newFolder.ID,
-               Label:        newFolder.Label,
-               RelativePath: newFolder.RelativePath,
-               SyncThingID:  newFolder.SyncThingID,
-               ShareRootDir: c.ShareRootDir,
-       })
-
-       newFolder.BuilderSThgID = c.Builder.SyncThingID // FIXME - should be removed after local ST config rework
-       newFolder.Status = FolderStatusEnable
-
-       return newFolder, err
+       return path.Join(root, "/.xds/server")
 }
 
-// DeleteFolder deletes a specific folder
-func (c *Config) DeleteFolder(id string) (FolderConfig, error) {
-       var fld FolderConfig
-       var err error
-
-       //SEB A SUP model.NotifyListeners(c, NotifyFolderDelete, fld)
-       if err = c.SThg.FolderDelete(id); err != nil {
-               return fld, err
+// WorkspaceRootDir return the path on server side where user xds-workspace dir is accessible
+func WorkspaceRootDir() string {
+       // May be overloaded by an env variable
+       if envVar, envDef := os.LookupEnv("XDS_SERVER_WORKSPACE_DIR"); envDef {
+               return envVar
        }
 
-       c.Folders, fld, err = c.Folders.Delete(id)
-
-       return fld, err
-}
-
-func dirExists(path string) bool {
-       _, err := os.Stat(path)
-       if os.IsNotExist(err) {
-               return false
+       home := "${HOME}"
+       if usr, err := user.Current(); err == nil {
+               home = usr.HomeDir
        }
-       return true
+
+       // Default value $HOME/xds-workspace
+       return path.Join(home, "xds-workspace")
 }