+/*
+ * 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 xdsserver
import (
"strings"
"sync"
- common "github.com/iotbzh/xds-common/golib"
- "github.com/iotbzh/xds-server/lib/xsapiv1"
+ common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git"
+ "gerrit.automotivelinux.org/gerrit/src/xds/xds-server.git/lib/xdsconfig"
+ "gerrit.automotivelinux.org/gerrit/src/xds/xds-server.git/lib/xsapiv1"
)
// SDKs List of installed SDK
type SDKs struct {
*Context
- Sdks map[string]*CrossSDK
+ Sdks map[string]*CrossSDK
+ SdksFamilies map[string]*xsapiv1.SDKFamilyConfig
mutex sync.Mutex
+ stop chan struct{} // signals intentional stop
}
-// NewSDKs creates a new instance of SDKs
-func NewSDKs(ctx *Context) (*SDKs, error) {
+// SDKsConstructor creates a new instance of SDKs
+func SDKsConstructor(ctx *Context) (*SDKs, error) {
s := SDKs{
- Context: ctx,
- Sdks: make(map[string]*CrossSDK),
+ Context: ctx,
+ Sdks: make(map[string]*CrossSDK),
+ SdksFamilies: make(map[string]*xsapiv1.SDKFamilyConfig),
+ stop: make(chan struct{}),
+ }
+
+ scriptsDir := ctx.Config.FileConf.SdkScriptsDir
+ if !common.Exists(scriptsDir) {
+ // allow to use scripts/sdk when debugging with vscode(EXEPATH=WORKSPACE)
+ scriptsDir = filepath.Join(filepath.Dir(ctx.Config.FileConf.SdkScriptsDir), "scripts", "sdks")
+ if !common.Exists(scriptsDir) {
+ return &s, fmt.Errorf("scripts directory doesn't exist (%v)", scriptsDir)
+ }
}
+ s.Log.Infof("SDK scripts dir: %s", scriptsDir)
- // Retrieve installed sdks
- sdkRD := ctx.Config.FileConf.SdkRootDir
+ dirs, err := filepath.Glob(path.Join(scriptsDir, "*"))
+ if err != nil {
+ s.Log.Errorf("Error while retrieving SDK scripts: dir=%s, error=%s", scriptsDir, err.Error())
+ return &s, err
+ }
- if common.Exists(sdkRD) {
+ // Update SDK DB on startup by default (can be disable using config file)
+ update := true
+ if s.Config.FileConf.SdkDbUpdate != "startup" {
+ update = false
+ }
- // Assume that SDK install tree is <rootdir>/<profile>/<version>/<arch>
- dirs, err := filepath.Glob(path.Join(sdkRD, "*", "*", "*"))
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ // Foreach directories in scripts/sdk
+ nbInstalled := 0
+ for _, d := range dirs {
+ if !common.IsDir(d) {
+ continue
+ }
+
+ sdksList, err := ListCrossSDK(d, update, s.Log)
if err != nil {
- ctx.Log.Debugf("Error while retrieving SDKs: dir=%s, error=%s", sdkRD, err.Error())
- return &s, err
+ // allow to use XDS even if error on list
+ s.Log.Errorf("Cannot retrieve SDK list: %v", err)
+ sdksList, _ = ListCrossSDK(d, false, s.Log)
}
- s.mutex.Lock()
- defer s.mutex.Unlock()
+ s.LogSillyf("'%s' SDKs list: %v", d, sdksList)
- for _, d := range dirs {
- if !common.IsDir(d) {
- continue
- }
- cSdk, err := NewCrossSDK(d)
+ for _, sdk := range sdksList {
+ cSdk, err := s._createNewCrossSDK(sdk, d, false, false)
if err != nil {
- ctx.Log.Debugf("Error while processing SDK dir=%s, err=%s", d, err.Error())
+ s.Log.Debugf("Error while processing SDK sdk=%v\n err=%s", sdk, err.Error())
continue
}
- s.Sdks[cSdk.sdk.ID] = cSdk
+
+ if cSdk.sdk.Status == xsapiv1.SdkStatusInstalled {
+ nbInstalled++
+ }
+
+ s.SdksFamilies[cSdk.sdk.FamilyConf.FamilyName] = &cSdk.sdk.FamilyConf
}
}
- ctx.Log.Debugf("SDKs: %d cross sdks found", len(s.Sdks))
+ ctx.Log.Infof("Cross SDKs: %d defined, %d installed", len(s.Sdks), nbInstalled)
+
+ // Start monitor thread to detect new SDKs
+ sdksDirs := []string{}
+ for _, sf := range s.SdksFamilies {
+ sdksDirs = append(sdksDirs, sf.RootDir)
+ }
+
+ if len(s.SdksFamilies) == 0 {
+ s.Log.Warningf("No cross SDKs definition found")
+ /* TODO: used it or cleanup
+ } else {
+ go s.monitorSDKInstallation(sdksDirs)
+ */
+ }
return &s, nil
}
+// _createNewCrossSDK Private function to create a new Cross SDK
+func (s *SDKs) _createNewCrossSDK(sdk xsapiv1.SDK, scriptDir string, installing bool, force bool) (*CrossSDK, error) {
+
+ cSdk, err := NewCrossSDK(s.Context, sdk, scriptDir)
+ if err != nil {
+ return cSdk, err
+ }
+
+ // Allow to overwrite not installed SDK or when force is set
+ if _, exist := s.Sdks[cSdk.sdk.ID]; exist {
+ if !force && cSdk.sdk.Path != "" && common.Exists(cSdk.sdk.Path) {
+ return cSdk, fmt.Errorf("SDK ID %s already installed in %s", cSdk.sdk.ID, cSdk.sdk.Path)
+ }
+ if !force && cSdk.sdk.Status != xsapiv1.SdkStatusNotInstalled {
+ return cSdk, fmt.Errorf("Duplicate SDK ID %s (use force to overwrite)", cSdk.sdk.ID)
+ }
+ }
+
+ // Sanity check
+ errMsg := "Invalid SDK definition "
+ if installing && cSdk.sdk.Path == "" {
+ return cSdk, fmt.Errorf(errMsg + "(path not set)")
+ }
+ if installing && cSdk.sdk.URL == "" {
+ return cSdk, fmt.Errorf(errMsg + "(url not set)")
+ }
+
+ // Add to list
+ s.Sdks[cSdk.sdk.ID] = cSdk
+
+ return cSdk, err
+}
+
+// Stop SDKs management
+func (s *SDKs) Stop() {
+ close(s.stop)
+}
+
+// monitorSDKInstallation
+/* TODO: used it or cleanup
+import "github.com/zillode/notify"
+
+func (s *SDKs) monitorSDKInstallation(watchingDirs []string) {
+
+ // Set up a watchpoint listening for inotify-specific events
+ c := make(chan notify.EventInfo, 1)
+
+ addWatcher := func(rootDir string) error {
+ s.Log.Debugf("SDK Register watcher: rootDir=%s", rootDir)
+
+ if err := notify.Watch(rootDir+"/...", c, notify.Create, notify.Remove); err != nil {
+ return fmt.Errorf("SDK monitor: rootDir=%v err=%v", rootDir, err)
+ }
+ return nil
+ }
+
+ // Add directory watchers
+ for _, dir := range watchingDirs {
+ if err := addWatcher(dir); err != nil {
+ s.Log.Errorln(err.Error())
+ }
+ }
+
+ // Wait inotify or stop events
+ for {
+ select {
+ case <-s.stop:
+ s.Log.Debugln("Stop monitorSDKInstallation")
+ notify.Stop(c)
+ return
+ case ei := <-c:
+ s.LogSillyf("monitorSDKInstallation SDKs event %v, path %v\n", ei.Event(), ei.Path())
+
+ // Filter out all event that doesn't match environment file
+ if !strings.Contains(ei.Path(), "environment-setup-") {
+ continue
+ }
+ dir := path.Dir(ei.Path())
+
+ sdk, err := s.GetByPath(dir)
+ if err != nil {
+ s.Log.Warningf("Cannot find SDK path to notify creation")
+ s.LogSillyf("event: %v", ei.Event())
+ continue
+ }
+
+ switch ei.Event() {
+ case notify.Create:
+ sdkDef, err := GetSDKInfo(scriptDir, sdk.URL, "", "", s.Log)
+ if err != nil {
+ s.Log.Warningf("Cannot get sdk info: %v", err)
+ continue
+ }
+ sdk.Path = sdkDef.Path
+ sdk.Path = sdkDef.SetupFile
+
+ // Emit Folder state change event
+ if err := s.events.Emit(xsapiv1.EVTSDKAdd, sdk, ""); err != nil {
+ s.Log.Warningf("Cannot notify SDK install: %v", err)
+ }
+
+ case notify.Remove, notify.InMovedFrom:
+ // Emit Folder state change event
+ if err := s.events.Emit(xsapiv1.EVTSDKRemove, sdk, ""); err != nil {
+ s.Log.Warningf("Cannot notify SDK remove: %v", err)
+ }
+ }
+ }
+ }
+}
+*/
+
// ResolveID Complete an SDK ID (helper for user that can use partial ID value)
func (s *SDKs) ResolveID(id string) (string, error) {
if id == "" {
} else if len(match) == 0 {
return id, fmt.Errorf("Unknown sdk id")
}
- return id, fmt.Errorf("Multiple sdk IDs found with provided prefix: " + id)
+ return id, fmt.Errorf("Multiple sdk IDs found: %v", match)
}
// Get returns an SDK from id
return (*sc).Get()
}
+// GetByPath Find a SDK from path
+func (s *SDKs) GetByPath(path string) (*xsapiv1.SDK, error) {
+ if path == "" {
+ return nil, fmt.Errorf("can't found sdk (empty path)")
+ }
+ for _, ss := range s.Sdks {
+ if ss.sdk.Path == path {
+ return ss.Get(), nil
+ }
+ }
+ return nil, fmt.Errorf("not found")
+}
+
// GetAll returns all existing SDKs
func (s *SDKs) GetAll() []xsapiv1.SDK {
s.mutex.Lock()
// Return default env that may be empty
return []string{}
}
+
+// Install Used to install a new SDK
+func (s *SDKs) Install(id, filepath string, force bool, timeout int, args []string, sess *ClientSession) (*xsapiv1.SDK, error) {
+
+ var sdk *xsapiv1.SDK
+ var err error
+ scriptDir := ""
+ sdkFilename := ""
+
+ if id != "" && filepath != "" {
+ return nil, fmt.Errorf("invalid parameter, both id and filepath are set")
+ }
+
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ if id != "" {
+ curSdk, exist := s.Sdks[id]
+ if !exist {
+ return nil, fmt.Errorf("unknown id")
+ }
+
+ sdk = &curSdk.sdk
+ scriptDir = sdk.FamilyConf.ScriptsDir
+
+ // Update path when not set
+ if sdk.Path == "" {
+ sdkDef, err := GetSDKInfo(scriptDir, sdk.URL, "", "", sdk.UUID, s.Log)
+ if err != nil || sdkDef.Path == "" {
+ return nil, fmt.Errorf("cannot retrieve sdk path %v", err)
+ }
+ sdk.Path = sdkDef.Path
+ }
+
+ } else if filepath != "" {
+ // FIXME support any location and also sharing either by pathmap or Syncthing
+
+ baseDir := path.Join(xdsconfig.WorkspaceRootDir(), "sdks")
+ sdkFilename, _ = common.ResolveEnvVar(path.Join(baseDir, path.Base(filepath)))
+ if !common.Exists(sdkFilename) {
+ return nil, fmt.Errorf("SDK file not accessible, must be in %s", baseDir)
+ }
+
+ for _, sf := range s.SdksFamilies {
+ sdkDef, err := GetSDKInfo(sf.ScriptsDir, "", sdkFilename, "", "", s.Log)
+ if err == nil {
+ // OK, sdk found
+ sdk = &sdkDef
+ scriptDir = sf.ScriptsDir
+ break
+ }
+
+ s.Log.Debugf("GetSDKInfo error: family=%s, sdkFilename=%s, err=%v", sf.FamilyName, path.Base(sdkFilename), err)
+ }
+ if sdk == nil {
+ return nil, fmt.Errorf("Cannot identify SDK family for %s", path.Base(filepath))
+ }
+
+ } else {
+ return nil, fmt.Errorf("invalid parameter, id or filepath must be set")
+ }
+
+ cSdk, err := s._createNewCrossSDK(*sdk, scriptDir, true, force)
+ if err != nil {
+ return nil, err
+ }
+
+ // Launch script to install
+ // (note that add event will be generated by monitoring thread)
+ if err := cSdk.Install(sdkFilename, force, timeout, args, sess); err != nil {
+ return &cSdk.sdk, err
+ }
+
+ return &cSdk.sdk, nil
+}
+
+// AbortInstall Used to abort SDK installation
+func (s *SDKs) AbortInstall(id string, timeout int) (*xsapiv1.SDK, error) {
+
+ if id == "" {
+ return nil, fmt.Errorf("invalid parameter")
+ }
+ cSdk, exist := s.Sdks[id]
+ if !exist {
+ return nil, fmt.Errorf("unknown id")
+ }
+
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ err := cSdk.AbortInstallRemove(timeout)
+
+ return &cSdk.sdk, err
+}
+
+// Remove Used to uninstall a SDK
+func (s *SDKs) Remove(id string, timeout int, sess *ClientSession) (*xsapiv1.SDK, error) {
+
+ cSdk, exist := s.Sdks[id]
+ if !exist {
+ return nil, fmt.Errorf("unknown id")
+ }
+
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ // Launch script to remove/uninstall
+ // (note that remove event will be generated by monitoring thread)
+ if err := cSdk.Remove(timeout, sess); err != nil {
+ return &cSdk.sdk, err
+ }
+
+ sdk := cSdk.sdk
+
+ // Don't delete it from s.Sdks
+ // (always keep sdk reference to allow for example re-install)
+
+ return &sdk, nil
+}