2 * Copyright (C) 2017-2018 "IoT.bzh"
3 * Author Sebastien Douheret <sebastien@iot.bzh>
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
27 common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git/golib"
28 "gerrit.automotivelinux.org/gerrit/src/xds/xds-server/lib/xdsconfig"
29 "gerrit.automotivelinux.org/gerrit/src/xds/xds-server/lib/xsapiv1"
32 // SDKs List of installed SDK
35 Sdks map[string]*CrossSDK
36 SdksFamilies map[string]*xsapiv1.SDKFamilyConfig
39 stop chan struct{} // signals intentional stop
42 // SDKsConstructor creates a new instance of SDKs
43 func SDKsConstructor(ctx *Context) (*SDKs, error) {
46 Sdks: make(map[string]*CrossSDK),
47 SdksFamilies: make(map[string]*xsapiv1.SDKFamilyConfig),
48 stop: make(chan struct{}),
51 scriptsDir := ctx.Config.FileConf.SdkScriptsDir
52 if !common.Exists(scriptsDir) {
53 // allow to use scripts/sdk in debug mode
54 scriptsDir = filepath.Join(filepath.Dir(ctx.Config.FileConf.SdkScriptsDir), "scripts", "sdks")
55 if !common.Exists(scriptsDir) {
56 return &s, fmt.Errorf("scripts directory doesn't exist (%v)", scriptsDir)
59 s.Log.Infof("SDK scripts dir: %s", scriptsDir)
61 dirs, err := filepath.Glob(path.Join(scriptsDir, "*"))
63 s.Log.Errorf("Error while retrieving SDK scripts: dir=%s, error=%s", scriptsDir, err.Error())
67 // Update SDK DB on startup by default (can be disable using config file)
69 if s.Config.FileConf.SdkDbUpdate != "startup" {
74 defer s.mutex.Unlock()
76 // Foreach directories in scripts/sdk
78 for _, d := range dirs {
83 sdksList, err := ListCrossSDK(d, update, s.Log)
85 // allow to use XDS even if error on list
86 s.Log.Errorf("Cannot retrieve SDK list: %v", err)
87 sdksList, _ = ListCrossSDK(d, false, s.Log)
89 s.LogSillyf("'%s' SDKs list: %v", d, sdksList)
91 for _, sdk := range sdksList {
92 cSdk, err := s._createNewCrossSDK(sdk, d, false, false)
94 s.Log.Debugf("Error while processing SDK sdk=%v\n err=%s", sdk, err.Error())
98 if cSdk.sdk.Status == xsapiv1.SdkStatusInstalled {
102 s.SdksFamilies[cSdk.sdk.FamilyConf.FamilyName] = &cSdk.sdk.FamilyConf
106 ctx.Log.Infof("Cross SDKs: %d defined, %d installed", len(s.Sdks), nbInstalled)
108 // Start monitor thread to detect new SDKs
109 sdksDirs := []string{}
110 for _, sf := range s.SdksFamilies {
111 sdksDirs = append(sdksDirs, sf.RootDir)
114 if len(s.SdksFamilies) == 0 {
115 s.Log.Warningf("No cross SDKs definition found")
116 /* TODO: used it or cleanup
118 go s.monitorSDKInstallation(sdksDirs)
125 // _createNewCrossSDK Private function to create a new Cross SDK
126 func (s *SDKs) _createNewCrossSDK(sdk xsapiv1.SDK, scriptDir string, installing bool, force bool) (*CrossSDK, error) {
128 cSdk, err := NewCrossSDK(s.Context, sdk, scriptDir)
133 // Allow to overwrite not installed SDK or when force is set
134 if _, exist := s.Sdks[cSdk.sdk.ID]; exist {
135 if !force && cSdk.sdk.Path != "" && common.Exists(cSdk.sdk.Path) {
136 return cSdk, fmt.Errorf("SDK ID %s already installed in %s", cSdk.sdk.ID, cSdk.sdk.Path)
138 if !force && cSdk.sdk.Status != xsapiv1.SdkStatusNotInstalled {
139 return cSdk, fmt.Errorf("Duplicate SDK ID %s (use force to overwrite)", cSdk.sdk.ID)
144 errMsg := "Invalid SDK definition "
145 if installing && cSdk.sdk.Path == "" {
146 return cSdk, fmt.Errorf(errMsg + "(path not set)")
148 if installing && cSdk.sdk.URL == "" {
149 return cSdk, fmt.Errorf(errMsg + "(url not set)")
153 s.Sdks[cSdk.sdk.ID] = cSdk
158 // Stop SDKs management
159 func (s *SDKs) Stop() {
163 // monitorSDKInstallation
164 /* TODO: used it or cleanup
165 import "github.com/zillode/notify"
167 func (s *SDKs) monitorSDKInstallation(watchingDirs []string) {
169 // Set up a watchpoint listening for inotify-specific events
170 c := make(chan notify.EventInfo, 1)
172 addWatcher := func(rootDir string) error {
173 s.Log.Debugf("SDK Register watcher: rootDir=%s", rootDir)
175 if err := notify.Watch(rootDir+"/...", c, notify.Create, notify.Remove); err != nil {
176 return fmt.Errorf("SDK monitor: rootDir=%v err=%v", rootDir, err)
181 // Add directory watchers
182 for _, dir := range watchingDirs {
183 if err := addWatcher(dir); err != nil {
184 s.Log.Errorln(err.Error())
188 // Wait inotify or stop events
192 s.Log.Debugln("Stop monitorSDKInstallation")
196 s.LogSillyf("monitorSDKInstallation SDKs event %v, path %v\n", ei.Event(), ei.Path())
198 // Filter out all event that doesn't match environment file
199 if !strings.Contains(ei.Path(), "environment-setup-") {
202 dir := path.Dir(ei.Path())
204 sdk, err := s.GetByPath(dir)
206 s.Log.Warningf("Cannot find SDK path to notify creation")
207 s.LogSillyf("event: %v", ei.Event())
213 sdkDef, err := GetSDKInfo(scriptDir, sdk.URL, "", "", s.Log)
215 s.Log.Warningf("Cannot get sdk info: %v", err)
218 sdk.Path = sdkDef.Path
219 sdk.Path = sdkDef.SetupFile
221 // Emit Folder state change event
222 if err := s.events.Emit(xsapiv1.EVTSDKAdd, sdk, ""); err != nil {
223 s.Log.Warningf("Cannot notify SDK install: %v", err)
226 case notify.Remove, notify.InMovedFrom:
227 // Emit Folder state change event
228 if err := s.events.Emit(xsapiv1.EVTSDKRemove, sdk, ""); err != nil {
229 s.Log.Warningf("Cannot notify SDK remove: %v", err)
237 // ResolveID Complete an SDK ID (helper for user that can use partial ID value)
238 func (s *SDKs) ResolveID(id string) (string, error) {
244 for iid := range s.Sdks {
245 if strings.HasPrefix(iid, id) {
246 match = append(match, iid)
252 } else if len(match) == 0 {
253 return id, fmt.Errorf("Unknown sdk id")
255 return id, fmt.Errorf("Multiple sdk IDs found: %v", match)
258 // Get returns an SDK from id
259 func (s *SDKs) Get(id string) *xsapiv1.SDK {
261 defer s.mutex.Unlock()
263 sc, exist := s.Sdks[id]
270 // GetByPath Find a SDK from path
271 func (s *SDKs) GetByPath(path string) (*xsapiv1.SDK, error) {
273 return nil, fmt.Errorf("can't found sdk (empty path)")
275 for _, ss := range s.Sdks {
276 if ss.sdk.Path == path {
280 return nil, fmt.Errorf("not found")
283 // GetAll returns all existing SDKs
284 func (s *SDKs) GetAll() []xsapiv1.SDK {
286 defer s.mutex.Unlock()
287 res := []xsapiv1.SDK{}
288 for _, v := range s.Sdks {
289 res = append(res, *(*v).Get())
294 // GetEnvCmd returns the command used to initialized the environment for an SDK
295 func (s *SDKs) GetEnvCmd(id string, defaultID string) []string {
296 if id == "" && defaultID == "" {
302 defer s.mutex.Unlock()
304 if iid, err := s.ResolveID(id); err == nil {
305 if sdk, exist := s.Sdks[iid]; exist {
306 return sdk.GetEnvCmd()
310 if sdk, exist := s.Sdks[defaultID]; defaultID != "" && exist {
311 return sdk.GetEnvCmd()
314 // Return default env that may be empty
318 // Install Used to install a new SDK
319 func (s *SDKs) Install(id, filepath string, force bool, timeout int, args []string, sess *ClientSession) (*xsapiv1.SDK, error) {
326 if id != "" && filepath != "" {
327 return nil, fmt.Errorf("invalid parameter, both id and filepath are set")
331 defer s.mutex.Unlock()
334 curSdk, exist := s.Sdks[id]
336 return nil, fmt.Errorf("unknown id")
340 scriptDir = sdk.FamilyConf.ScriptsDir
342 // Update path when not set
344 sdkDef, err := GetSDKInfo(scriptDir, sdk.URL, "", "", sdk.UUID, s.Log)
345 if err != nil || sdkDef.Path == "" {
346 return nil, fmt.Errorf("cannot retrieve sdk path %v", err)
348 sdk.Path = sdkDef.Path
351 } else if filepath != "" {
352 // FIXME support any location and also sharing either by pathmap or Syncthing
354 baseDir := path.Join(xdsconfig.WorkspaceRootDir(), "sdks")
355 sdkFilename, _ = common.ResolveEnvVar(path.Join(baseDir, path.Base(filepath)))
356 if !common.Exists(sdkFilename) {
357 return nil, fmt.Errorf("SDK file not accessible, must be in %s", baseDir)
360 for _, sf := range s.SdksFamilies {
361 sdkDef, err := GetSDKInfo(sf.ScriptsDir, "", sdkFilename, "", "", s.Log)
365 scriptDir = sf.ScriptsDir
369 s.Log.Debugf("GetSDKInfo error: family=%s, sdkFilename=%s, err=%v", sf.FamilyName, path.Base(sdkFilename), err)
372 return nil, fmt.Errorf("Cannot identify SDK family for %s", path.Base(filepath))
376 return nil, fmt.Errorf("invalid parameter, id or filepath must be set")
379 cSdk, err := s._createNewCrossSDK(*sdk, scriptDir, true, force)
384 // Launch script to install
385 // (note that add event will be generated by monitoring thread)
386 if err := cSdk.Install(sdkFilename, force, timeout, args, sess); err != nil {
387 return &cSdk.sdk, err
390 return &cSdk.sdk, nil
393 // AbortInstall Used to abort SDK installation
394 func (s *SDKs) AbortInstall(id string, timeout int) (*xsapiv1.SDK, error) {
397 return nil, fmt.Errorf("invalid parameter")
399 cSdk, exist := s.Sdks[id]
401 return nil, fmt.Errorf("unknown id")
405 defer s.mutex.Unlock()
407 err := cSdk.AbortInstallRemove(timeout)
409 return &cSdk.sdk, err
412 // Remove Used to uninstall a SDK
413 func (s *SDKs) Remove(id string, timeout int, sess *ClientSession) (*xsapiv1.SDK, error) {
415 cSdk, exist := s.Sdks[id]
417 return nil, fmt.Errorf("unknown id")
421 defer s.mutex.Unlock()
423 // Launch script to remove/uninstall
424 // (note that remove event will be generated by monitoring thread)
425 if err := cSdk.Remove(timeout, sess); err != nil {
426 return &cSdk.sdk, err
431 // Don't delete it from s.Sdks
432 // (always keep sdk reference to allow for example re-install)