+/*
+ * 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/golib"
+ "gerrit.automotivelinux.org/gerrit/src/xds/xds-server/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: "webapp/dist",
+ 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) {
- if err := FolderVerify(newFolder); err != nil {
- return FolderConfig{}, err
+// 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
}
- 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
+ // 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
+ }
- 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")
}