1d121c3aad54fa2465abb9ba80f7db6298cd346d
[src/xds/xds-cli.git] / cmd-exec.go
1 package main
2
3 import (
4         "fmt"
5         "os"
6         "strings"
7
8         "github.com/iotbzh/xds-agent/lib/apiv1"
9         "github.com/urfave/cli"
10 )
11
12 func initCmdExec(cmdDef *[]cli.Command) {
13         *cmdDef = append(*cmdDef, cli.Command{
14                 Name:   "exec",
15                 Usage:  "execute a command in XDS",
16                 Action: exec,
17                 Flags: []cli.Flag{
18                         cli.StringFlag{
19                                 Name:   "id",
20                                 EnvVar: "XDS_PROJECT_ID",
21                                 Usage:  "project ID you want to build (mandatory variable)",
22                         },
23                         cli.StringFlag{
24                                 Name:   "rpath, p",
25                                 EnvVar: "XDS_RPATH",
26                                 Usage:  "relative path into project",
27                         },
28                         cli.StringFlag{
29                                 Name:   "sdkid, sdk",
30                                 EnvVar: "XDS_SDK_ID",
31                                 Usage:  "Cross Sdk ID to use to build project",
32                         },
33                 },
34         })
35 }
36
37 func exec(ctx *cli.Context) error {
38         prjID := ctx.String("id")
39         rPath := ctx.String("rPath")
40         sdkid := ctx.String("sdkid")
41
42         // Check mandatory args
43         if prjID == "" {
44                 return cli.NewExitError("project id must be set (see --id option)", 1)
45         }
46
47         argsCommand := make([]string, len(ctx.Args()))
48         copy(argsCommand, ctx.Args())
49         Log.Infof("Execute: /exec %v", argsCommand)
50
51         // Log useful info for debugging
52         ver := apiv1.XDSVersion{}
53         XdsVersionGet(&ver)
54         Log.Infof("XDS version: %v", ver)
55
56         // Process Socket IO events
57         type exitResult struct {
58                 error error
59                 code  int
60         }
61         exitChan := make(chan exitResult, 1)
62
63         IOsk.On("disconnection", func(err error) {
64                 exitChan <- exitResult{err, 2}
65         })
66
67         outFunc := func(timestamp, stdout, stderr string) {
68                 tm := ""
69                 if ctx.Bool("WithTimestamp") {
70                         tm = timestamp + "| "
71                 }
72                 if stdout != "" {
73                         fmt.Printf("%s%s", tm, stdout)
74                 }
75                 if stderr != "" {
76                         fmt.Fprintf(os.Stderr, "%s%s", tm, stderr)
77                 }
78         }
79
80         IOsk.On(apiv1.ExecOutEvent, func(ev apiv1.ExecOutMsg) {
81                 outFunc(ev.Timestamp, ev.Stdout, ev.Stderr)
82         })
83
84         IOsk.On(apiv1.ExecExitEvent, func(ev apiv1.ExecExitMsg) {
85                 exitChan <- exitResult{ev.Error, ev.Code}
86         })
87
88         // Retrieve the project definition
89         prj := apiv1.ProjectConfig{}
90         if err := HTTPCli.Get("/projects/"+prjID, &prj); err != nil {
91                 return cli.NewExitError(err, 1)
92         }
93
94         // Auto setup rPath if needed
95         if rPath == "" {
96                 cwd, err := os.Getwd()
97                 if err == nil {
98                         fldRp := prj.ClientPath
99                         if !strings.HasPrefix(fldRp, "/") {
100                                 fldRp = "/" + fldRp
101                         }
102                         Log.Debugf("Try to auto-setup rPath: cwd=%s ; ClientPath=%s", cwd, fldRp)
103                         if sp := strings.SplitAfter(cwd, fldRp); len(sp) == 2 {
104                                 rPath = strings.Trim(sp[1], "/")
105                                 Log.Debugf("Auto-setup rPath to: '%s'", rPath)
106                         }
107                 }
108         }
109
110         // Build env
111         Log.Debugf("Command env: %v", EnvConfFileMap)
112         env := []string{}
113         for k, v := range EnvConfFileMap {
114                 env = append(env, k+"="+v)
115         }
116
117         // Send build command
118         args := apiv1.ExecArgs{
119                 ID:         prjID,
120                 SdkID:      sdkid,
121                 Cmd:        strings.Trim(argsCommand[0], " "),
122                 Args:       argsCommand[1:],
123                 Env:        env,
124                 RPath:      rPath,
125                 CmdTimeout: 60,
126         }
127
128         LogPost("POST /exec %v", args)
129         if err := HTTPCli.Post("/exec", args, nil); err != nil {
130                 return cli.NewExitError(err.Error(), 1)
131         }
132
133         // Wait exit
134         select {
135         case res := <-exitChan:
136                 errStr := ""
137                 if res.code == 0 {
138                         Log.Debugln("Exit successfully")
139                 }
140                 if res.error != nil {
141                         Log.Debugln("Exit with ERROR: ", res.error.Error())
142                         errStr = res.error.Error()
143                 }
144                 return cli.NewExitError(errStr, res.code)
145         }
146 }