X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?p=src%2Fxds%2Fxds-server.git;a=blobdiff_plain;f=lib%2Fxdsserver%2Fsdks.go;h=47d48d505ebfbbcac7d14b5cc5b1e9e0585df4b5;hp=1a40ab58c003e906941f67680d5d195faeb7f5b6;hb=HEAD;hpb=2f7828d01f4c4ca2909f95f098627cd5475ed225 diff --git a/lib/xdsserver/sdks.go b/lib/xdsserver/sdks.go index 1a40ab5..1accc60 100644 --- a/lib/xdsserver/sdks.go +++ b/lib/xdsserver/sdks.go @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2017-2018 "IoT.bzh" + * Author Sebastien Douheret + * + * 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 ( @@ -7,57 +24,216 @@ 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 /// - 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 == "" { @@ -76,7 +252,7 @@ func (s *SDKs) ResolveID(id string) (string, error) { } 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 @@ -91,6 +267,19 @@ func (s *SDKs) Get(id string) *xsapiv1.SDK { 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() @@ -125,3 +314,122 @@ func (s *SDKs) GetEnvCmd(id string, defaultID string) []string { // 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 +}