Use fix commit id to fetch xds-agent while waiting rc2.
[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                 Log.Debugf("WS disconnection event with err: %v\n", err)
65                 exitChan <- exitResult{err, 2}
66         })
67
68         outFunc := func(timestamp, stdout, stderr string) {
69                 tm := ""
70                 if ctx.Bool("WithTimestamp") {
71                         tm = timestamp + "| "
72                 }
73                 if stdout != "" {
74                         fmt.Printf("%s%s", tm, stdout)
75                 }
76                 if stderr != "" {
77                         fmt.Fprintf(os.Stderr, "%s%s", tm, stderr)
78                 }
79         }
80
81         IOsk.On(apiv1.ExecOutEvent, func(ev apiv1.ExecOutMsg) {
82                 outFunc(ev.Timestamp, ev.Stdout, ev.Stderr)
83         })
84
85         IOsk.On(apiv1.ExecExitEvent, func(ev apiv1.ExecExitMsg) {
86                 exitChan <- exitResult{ev.Error, ev.Code}
87         })
88
89         IOsk.On(apiv1.EVTProjectChange, func(ev apiv1.EventMsg) {
90                 prj, _ := ev.DecodeProjectConfig()
91                 Log.Infof("Event %v (%v): %v", ev.Type, ev.Time, prj)
92         })
93         evReg := apiv1.EventRegisterArgs{Name: apiv1.EVTProjectChange}
94         if err := HTTPCli.Post("/events/register", &evReg, nil); err != nil {
95                 return cli.NewExitError(err, 1)
96         }
97
98         // Retrieve the project definition
99         prj := apiv1.ProjectConfig{}
100         if err := HTTPCli.Get("/projects/"+prjID, &prj); err != nil {
101                 return cli.NewExitError(err, 1)
102         }
103
104         // Auto setup rPath if needed
105         if rPath == "" {
106                 cwd, err := os.Getwd()
107                 if err == nil {
108                         fldRp := prj.ClientPath
109                         if !strings.HasPrefix(fldRp, "/") {
110                                 fldRp = "/" + fldRp
111                         }
112                         Log.Debugf("Try to auto-setup rPath: cwd=%s ; ClientPath=%s", cwd, fldRp)
113                         if sp := strings.SplitAfter(cwd, fldRp); len(sp) == 2 {
114                                 rPath = strings.Trim(sp[1], "/")
115                                 Log.Debugf("Auto-setup rPath to: '%s'", rPath)
116                         }
117                 }
118         }
119
120         // Build env
121         Log.Debugf("Command env: %v", EnvConfFileMap)
122         env := []string{}
123         for k, v := range EnvConfFileMap {
124                 env = append(env, k+"="+v)
125         }
126
127         // Send build command
128         args := apiv1.ExecArgs{
129                 ID:         prjID,
130                 SdkID:      sdkid,
131                 Cmd:        strings.Trim(argsCommand[0], " "),
132                 Args:       argsCommand[1:],
133                 Env:        env,
134                 RPath:      rPath,
135                 CmdTimeout: 60,
136         }
137
138         LogPost("POST /exec %v", args)
139         if err := HTTPCli.Post("/exec", args, nil); err != nil {
140                 return cli.NewExitError(err.Error(), 1)
141         }
142
143         // Wait exit
144         select {
145         case res := <-exitChan:
146                 errStr := ""
147                 if res.code == 0 {
148                         Log.Debugln("Exit successfully")
149                 }
150                 if res.error != nil {
151                         Log.Debugln("Exit with ERROR: ", res.error.Error())
152                         errStr = res.error.Error()
153                 }
154                 return cli.NewExitError(errStr, res.code)
155         }
156 }