Update .gitreview file
[src/xds/xds-cli.git] / cmd-exec.go
1 /*
2  * Copyright (C) 2017-2018 "IoT.bzh"
3  * Author Sebastien Douheret <sebastien@iot.bzh>
4  *
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
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  */
18
19 package main
20
21 import (
22         "fmt"
23         "os"
24         "strings"
25
26         "gerrit.automotivelinux.org/gerrit/src/xds/xds-agent.git/lib/xaapiv1"
27         "github.com/urfave/cli"
28 )
29
30 func initCmdExec(cmdDef *[]cli.Command) {
31         *cmdDef = append(*cmdDef, cli.Command{
32                 Name:   "exec",
33                 Usage:  "execute a command in XDS",
34                 Action: execCmd,
35                 Flags: []cli.Flag{
36                         cli.StringFlag{
37                                 Name:   "id",
38                                 EnvVar: "XDS_PROJECT_ID",
39                                 Usage:  "project ID you want to build (mandatory variable)",
40                         },
41                         cli.StringFlag{
42                                 Name:   "rpath, p",
43                                 EnvVar: "XDS_RPATH",
44                                 Usage:  "relative path into project",
45                         },
46                         cli.StringFlag{
47                                 Name:   "sdkid, sdk",
48                                 EnvVar: "XDS_SDK_ID",
49                                 Usage:  "Cross Sdk ID to use to build project",
50                         },
51                         cli.BoolFlag{
52                                 Name:   "ldlibpath-no-reset",
53                                 Hidden: true,
54                                 EnvVar: "XDS_LD_LIBRARY_PATH_NO_RESET",
55                                 Usage:  "Don't reset LD_LIBRARY_PATH before executing command",
56                         },
57                 },
58         })
59 }
60
61 func execCmd(ctx *cli.Context) error {
62         prjID := ctx.String("id")
63         rPath := ctx.String("rpath")
64         sdkid := ctx.String("sdkid")
65
66         // Check mandatory args
67         if prjID == "" {
68                 return cli.NewExitError("project id must be set (see --id option)", 1)
69         }
70
71         argsCommand := make([]string, len(ctx.Args()))
72         copy(argsCommand, ctx.Args())
73         Log.Infof("Execute: /exec %v", argsCommand)
74
75         // Log useful info for debugging
76         ver := xaapiv1.XDSVersion{}
77         XdsVersionGet(&ver)
78         Log.Infof("XDS version: %v", ver)
79
80         // Process Socket IO events
81         type exitResult struct {
82                 error error
83                 code  int
84         }
85         exitChan := make(chan exitResult, 1)
86
87         IOSkClient.On("disconnection", func(err error) {
88                 Log.Debugf("WS disconnection event with err: %v\n", err)
89                 exitChan <- exitResult{err, 2}
90         })
91
92         outFunc := func(timestamp, stdout, stderr string) {
93                 tm := ""
94                 if ctx.Bool("WithTimestamp") {
95                         tm = timestamp + "| "
96                 }
97                 if stdout != "" {
98                         fmt.Printf("%s%s", tm, stdout)
99                 }
100                 if stderr != "" {
101                         fmt.Fprintf(os.Stderr, "%s%s", tm, stderr)
102                 }
103         }
104
105         IOSkClient.On(xaapiv1.ExecOutEvent, func(ev xaapiv1.ExecOutMsg) {
106                 outFunc(ev.Timestamp, ev.Stdout, ev.Stderr)
107         })
108
109         IOSkClient.On(xaapiv1.ExecExitEvent, func(ev xaapiv1.ExecExitMsg) {
110                 exitChan <- exitResult{ev.Error, ev.Code}
111         })
112
113         IOSkClient.On(xaapiv1.EVTProjectChange, func(ev xaapiv1.EventMsg) {
114                 prj, _ := ev.DecodeProjectConfig()
115                 Log.Infof("Event %v (%v): %v", ev.Type, ev.Time, prj)
116         })
117         evReg := xaapiv1.EventRegisterArgs{Name: xaapiv1.EVTProjectChange}
118         if err := HTTPCli.Post("/events/register", &evReg, nil); err != nil {
119                 return cli.NewExitError(err, 1)
120         }
121
122         // Retrieve the project definition
123         prj := xaapiv1.ProjectConfig{}
124         if err := HTTPCli.Get("/projects/"+prjID, &prj); err != nil {
125                 return cli.NewExitError(err, 1)
126         }
127
128         // Auto setup rPath if needed
129         if rPath == "" {
130                 cwd, err := os.Getwd()
131                 if err == nil {
132                         fldRp := prj.ClientPath
133                         if !strings.HasPrefix(fldRp, "/") {
134                                 fldRp = "/" + fldRp
135                         }
136                         Log.Debugf("Try to auto-setup rPath: cwd=%s ; ClientPath=%s", cwd, fldRp)
137                         if sp := strings.SplitAfter(cwd, fldRp); len(sp) == 2 {
138                                 rPath = strings.Trim(sp[1], "/")
139                                 Log.Debugf("Auto-setup rPath to: '%s'", rPath)
140                         }
141                 }
142         }
143
144         // Build env
145         Log.Debugf("Command env: %v", EnvConfFileMap)
146         env := []string{}
147         for k, v := range EnvConfFileMap {
148                 env = append(env, k+"="+v)
149         }
150
151         // Send build command
152         args := xaapiv1.ExecArgs{
153                 ID:               prjID,
154                 SdkID:            sdkid,
155                 Cmd:              strings.Trim(argsCommand[0], " "),
156                 Args:             argsCommand[1:],
157                 Env:              env,
158                 RPath:            rPath,
159                 LdLibPathNoReset: ctx.Bool("ldlibpath-no-reset"),
160                 CmdTimeout:       60,
161         }
162
163         LogPost("POST /exec %v", args)
164         if err := HTTPCli.Post("/exec", args, nil); err != nil {
165                 return cli.NewExitError(err.Error(), 1)
166         }
167
168         // Wait exit
169         select {
170         case res := <-IOSkClient.ServerDiscoChan:
171                 Log.Debugf("XDS Server disconnected %v", res)
172                 return cli.NewExitError(res.error, res.code)
173
174         case res := <-exitChan:
175                 errStr := ""
176                 if res.code == 0 {
177                         Log.Debugln("Exit successfully")
178                 }
179                 if res.error != nil {
180                         Log.Debugln("Exit with ERROR: ", res.error.Error())
181                         errStr = res.error.Error()
182                 }
183                 return cli.NewExitError(errStr, res.code)
184         }
185 }