819b911e42997a064671f09ad17e361c90880ad8
[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                 },
52         })
53 }
54
55 func execCmd(ctx *cli.Context) error {
56         prjID := ctx.String("id")
57         rPath := ctx.String("rpath")
58         sdkid := ctx.String("sdkid")
59
60         // Check mandatory args
61         if prjID == "" {
62                 return cli.NewExitError("project id must be set (see --id option)", 1)
63         }
64
65         argsCommand := make([]string, len(ctx.Args()))
66         copy(argsCommand, ctx.Args())
67         Log.Infof("Execute: /exec %v", argsCommand)
68
69         // Log useful info for debugging
70         ver := xaapiv1.XDSVersion{}
71         XdsVersionGet(&ver)
72         Log.Infof("XDS version: %v", ver)
73
74         // Process Socket IO events
75         type exitResult struct {
76                 error error
77                 code  int
78         }
79         exitChan := make(chan exitResult, 1)
80
81         IOSkClient.On("disconnection", func(err error) {
82                 Log.Debugf("WS disconnection event with err: %v\n", err)
83                 exitChan <- exitResult{err, 2}
84         })
85
86         outFunc := func(timestamp, stdout, stderr string) {
87                 tm := ""
88                 if ctx.Bool("WithTimestamp") {
89                         tm = timestamp + "| "
90                 }
91                 if stdout != "" {
92                         fmt.Printf("%s%s", tm, stdout)
93                 }
94                 if stderr != "" {
95                         fmt.Fprintf(os.Stderr, "%s%s", tm, stderr)
96                 }
97         }
98
99         IOSkClient.On(xaapiv1.ExecOutEvent, func(ev xaapiv1.ExecOutMsg) {
100                 outFunc(ev.Timestamp, ev.Stdout, ev.Stderr)
101         })
102
103         IOSkClient.On(xaapiv1.ExecExitEvent, func(ev xaapiv1.ExecExitMsg) {
104                 exitChan <- exitResult{ev.Error, ev.Code}
105         })
106
107         IOSkClient.On(xaapiv1.EVTProjectChange, func(ev xaapiv1.EventMsg) {
108                 prj, _ := ev.DecodeProjectConfig()
109                 Log.Infof("Event %v (%v): %v", ev.Type, ev.Time, prj)
110         })
111         evReg := xaapiv1.EventRegisterArgs{Name: xaapiv1.EVTProjectChange}
112         if err := HTTPCli.Post("/events/register", &evReg, nil); err != nil {
113                 return cli.NewExitError(err, 1)
114         }
115
116         // Retrieve the project definition
117         prj := xaapiv1.ProjectConfig{}
118         if err := HTTPCli.Get("/projects/"+prjID, &prj); err != nil {
119                 return cli.NewExitError(err, 1)
120         }
121
122         // Auto setup rPath if needed
123         if rPath == "" {
124                 cwd, err := os.Getwd()
125                 if err == nil {
126                         fldRp := prj.ClientPath
127                         if !strings.HasPrefix(fldRp, "/") {
128                                 fldRp = "/" + fldRp
129                         }
130                         Log.Debugf("Try to auto-setup rPath: cwd=%s ; ClientPath=%s", cwd, fldRp)
131                         if sp := strings.SplitAfter(cwd, fldRp); len(sp) == 2 {
132                                 rPath = strings.Trim(sp[1], "/")
133                                 Log.Debugf("Auto-setup rPath to: '%s'", rPath)
134                         }
135                 }
136         }
137
138         // Build env
139         Log.Debugf("Command env: %v", EnvConfFileMap)
140         env := []string{}
141         for k, v := range EnvConfFileMap {
142                 env = append(env, k+"="+v)
143         }
144
145         // Send build command
146         args := xaapiv1.ExecArgs{
147                 ID:         prjID,
148                 SdkID:      sdkid,
149                 Cmd:        strings.Trim(argsCommand[0], " "),
150                 Args:       argsCommand[1:],
151                 Env:        env,
152                 RPath:      rPath,
153                 CmdTimeout: 60,
154         }
155
156         LogPost("POST /exec %v", args)
157         if err := HTTPCli.Post("/exec", args, nil); err != nil {
158                 return cli.NewExitError(err.Error(), 1)
159         }
160
161         // Wait exit
162         select {
163         case res := <-IOSkClient.ServerDiscoChan:
164                 Log.Debugf("XDS Server disconnected %v", res)
165                 return cli.NewExitError(res.error, res.code)
166
167         case res := <-exitChan:
168                 errStr := ""
169                 if res.code == 0 {
170                         Log.Debugln("Exit successfully")
171                 }
172                 if res.error != nil {
173                         Log.Debugln("Exit with ERROR: ", res.error.Error())
174                         errStr = res.error.Error()
175                 }
176                 return cli.NewExitError(errStr, res.code)
177         }
178 }