2 * Copyright (C) 2017 "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 "github.com/iotbzh/xds-common/golib"
28 "github.com/iotbzh/xds-server/lib/xsapiv1"
29 uuid "github.com/satori/go.uuid"
32 // SDKs List of installed SDK
35 Sdks map[string]*CrossSDK
38 stop chan struct{} // signals intentional stop
41 // NewSDKs creates a new instance of SDKs
42 func NewSDKs(ctx *Context) (*SDKs, error) {
45 Sdks: make(map[string]*CrossSDK),
46 stop: make(chan struct{}),
49 scriptsDir := ctx.Config.FileConf.SdkScriptsDir
50 if !common.Exists(scriptsDir) {
51 // allow to use scripts/sdk in debug mode
52 scriptsDir = filepath.Join(filepath.Dir(ctx.Config.FileConf.SdkScriptsDir), "scripts", "sdks")
53 if !common.Exists(scriptsDir) {
54 return &s, fmt.Errorf("scripts directory doesn't exist (%v)", scriptsDir)
57 s.Log.Infof("SDK scripts dir: %s", scriptsDir)
59 dirs, err := filepath.Glob(path.Join(scriptsDir, "*"))
61 s.Log.Errorf("Error while retrieving SDK scripts: dir=%s, error=%s", scriptsDir, err.Error())
66 defer s.mutex.Unlock()
68 // Foreach directories in scripts/sdk
70 monSdksPath := make(map[string]*xsapiv1.SDKFamilyConfig)
71 for _, d := range dirs {
76 sdksList, err := ListCrossSDK(d, s.Log)
78 // allow to use XDS even if error on list
79 s.Log.Errorf("Cannot retrieve SDK list: %v", err)
81 s.LogSillyf("'%s' SDKs list: %v", d, sdksList)
83 for _, sdk := range sdksList {
84 cSdk, err := NewCrossSDK(ctx, sdk, d)
86 s.Log.Debugf("Error while processing SDK sdk=%v\n err=%s", sdk, err.Error())
89 if _, exist := s.Sdks[cSdk.sdk.ID]; exist {
90 s.Log.Warningf("Duplicate SDK ID : %v", cSdk.sdk.ID)
91 cSdk.sdk.ID += "_DUPLICATE_" + uuid.NewV1().String()
93 s.Sdks[cSdk.sdk.ID] = cSdk
94 if cSdk.sdk.Status == xsapiv1.SdkStatusInstalled {
98 monSdksPath[cSdk.sdk.FamilyConf.RootDir] = &cSdk.sdk.FamilyConf
102 ctx.Log.Debugf("Cross SDKs: %d defined, %d installed", len(s.Sdks), nbInstalled)
104 // Start monitor thread to detect new SDKs
105 if len(monSdksPath) == 0 {
106 s.Log.Warningf("No cross SDKs definition found")
112 // Stop SDKs management
113 func (s *SDKs) Stop() {
117 // monitorSDKInstallation
119 func (s *SDKs) monitorSDKInstallation(monSDKs map[string]*xsapiv1.SDKFamilyConfig) {
121 // Set up a watchpoint listening for inotify-specific events
122 c := make(chan notify.EventInfo, 1)
124 addWatcher := func(rootDir string) error {
125 s.Log.Debugf("SDK Register watcher: rootDir=%s", rootDir)
127 if err := notify.Watch(rootDir+"/...", c, notify.Create, notify.Remove); err != nil {
128 return fmt.Errorf("SDK monitor: rootDir=%v err=%v", rootDir, err)
133 // Add directory watchers
134 for dir := range monSDKs {
135 if err := addWatcher(dir); err != nil {
136 s.Log.Errorln(err.Error())
140 // Wait inotify or stop events
144 s.Log.Debugln("Stop monitorSDKInstallation")
148 s.LogSillyf("monitorSDKInstallation SDKs event %v, path %v\n", ei.Event(), ei.Path())
150 // Filter out all event that doesn't match environment file
151 if !strings.Contains(ei.Path(), "environment-setup-") {
154 dir := path.Dir(ei.Path())
156 sdk, err := s.GetByPath(dir)
158 s.Log.Warningf("Cannot find SDK path to notify creation")
159 s.LogSillyf("event: %v", ei.Event())
165 // Emit Folder state change event
166 if err := s.events.Emit(xsapiv1.EVTSDKInstall, sdk, ""); err != nil {
167 s.Log.Warningf("Cannot notify SDK install: %v", err)
170 case notify.Remove, notify.InMovedFrom:
171 // Emit Folder state change event
172 if err := s.events.Emit(xsapiv1.EVTSDKRemove, sdk, ""); err != nil {
173 s.Log.Warningf("Cannot notify SDK remove: %v", err)
181 // ResolveID Complete an SDK ID (helper for user that can use partial ID value)
182 func (s *SDKs) ResolveID(id string) (string, error) {
188 for iid := range s.Sdks {
189 if strings.HasPrefix(iid, id) {
190 match = append(match, iid)
196 } else if len(match) == 0 {
197 return id, fmt.Errorf("Unknown sdk id")
199 return id, fmt.Errorf("Multiple sdk IDs found with provided prefix: " + id)
202 // Get returns an SDK from id
203 func (s *SDKs) Get(id string) *xsapiv1.SDK {
205 defer s.mutex.Unlock()
207 sc, exist := s.Sdks[id]
214 // GetByPath Find a SDK from path
215 func (s *SDKs) GetByPath(path string) (*xsapiv1.SDK, error) {
217 return nil, fmt.Errorf("can't found sdk (empty path)")
219 for _, ss := range s.Sdks {
220 if ss.sdk.Path == path {
224 return nil, fmt.Errorf("not found")
227 // GetAll returns all existing SDKs
228 func (s *SDKs) GetAll() []xsapiv1.SDK {
230 defer s.mutex.Unlock()
231 res := []xsapiv1.SDK{}
232 for _, v := range s.Sdks {
233 res = append(res, *(*v).Get())
238 // GetEnvCmd returns the command used to initialized the environment for an SDK
239 func (s *SDKs) GetEnvCmd(id string, defaultID string) []string {
240 if id == "" && defaultID == "" {
246 defer s.mutex.Unlock()
248 if iid, err := s.ResolveID(id); err == nil {
249 if sdk, exist := s.Sdks[iid]; exist {
250 return sdk.GetEnvCmd()
254 if sdk, exist := s.Sdks[defaultID]; defaultID != "" && exist {
255 return sdk.GetEnvCmd()
258 // Return default env that may be empty
262 // Install Used to install a new SDK
263 func (s *SDKs) Install(id, filepath string, force bool, timeout int, sess *ClientSession) (*xsapiv1.SDK, error) {
265 if id != "" && filepath != "" {
266 return nil, fmt.Errorf("invalid parameter, both id and filepath are set")
270 cSdk, exist = s.Sdks[id]
272 return nil, fmt.Errorf("unknown id")
274 } else if filepath != "" {
275 // TODO check that file is accessible
278 return nil, fmt.Errorf("invalid parameter, id or filepath must be set")
282 defer s.mutex.Unlock()
284 // Launch script to install
285 // (note that add event will be generated by monitoring thread)
286 if err := cSdk.Install(filepath, force, timeout, sess); err != nil {
287 return &cSdk.sdk, err
290 return &cSdk.sdk, nil
293 // AbortInstall Used to abort SDK installation
294 func (s *SDKs) AbortInstall(id string, timeout int) (*xsapiv1.SDK, error) {
297 return nil, fmt.Errorf("invalid parameter")
299 cSdk, exist := s.Sdks[id]
301 return nil, fmt.Errorf("unknown id")
305 defer s.mutex.Unlock()
307 err := cSdk.AbortInstallRemove(timeout)
309 return &cSdk.sdk, err
312 // Remove Used to uninstall a SDK
313 func (s *SDKs) Remove(id string, timeout int, sess *ClientSession) (*xsapiv1.SDK, error) {
315 cSdk, exist := s.Sdks[id]
317 return nil, fmt.Errorf("unknown id")
321 defer s.mutex.Unlock()
323 // Launch script to remove/uninstall
324 // (note that remove event will be generated by monitoring thread)
325 if err := cSdk.Remove(timeout, sess); err != nil {
326 return &cSdk.sdk, err
331 // Don't delete it from s.Sdks
332 // (always keep sdk reference to allow for example re-install)