Use go module as dependency tool instead of glide
[src/xds/xds-server.git] / test / target_test.go
1 /*
2  * Copyright (C) 2017-2018 "IoT.bzh"
3  * Author Clément Bénier <clement.benier@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 package xdsservertest
18
19 import (
20         "bytes"
21         "fmt"
22         "log"
23         "os"
24         "os/exec"
25         "path"
26         "regexp"
27         "strconv"
28         "strings"
29         "testing"
30         "time"
31
32         common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git"
33         "gerrit.automotivelinux.org/gerrit/src/xds/xds-server.git/lib/xsapiv1"
34         "github.com/stretchr/testify/require"
35 )
36
37 func launchSSHd(sshDir string, proc **os.Process, sshdCmd string) (*os.File, string) {
38         port := "22222"
39         argsProcessSSHd := []string{
40                 sshdCmd,
41                 "-f",
42                 sshDir + "/sshd_config",
43                 "-D",
44                 "-h",
45                 sshDir + "/ssh_host_rsa_key",
46                 "-o",
47                 "AuthorizedKeysFile=" + sshDir + "/authorized_keys",
48                 "-p",
49                 port,
50         }
51         logFile := logDir + logFileSSHd
52         file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY, 0644)
53         if err != nil {
54                 log.Fatal(err)
55         }
56         defer file.Close()
57         tmpProc, err := os.StartProcess(argsProcessSSHd[0], argsProcessSSHd, &os.ProcAttr{
58                 Files: []*os.File{os.Stdin, file, file},
59         })
60         if err != nil {
61                 log.Fatal(err)
62         }
63         *proc = tmpProc
64         return file, port
65 }
66
67 func InitSSH(t *testing.T, procSSHd **os.Process) (string, string) {
68         Debug(t, "Initialise ssh with local user")
69         sshDir := path.Join(os.Getenv(envRootCfgDir), "ssh")
70         cmd := exec.Command("cp", "-r", sshFixturesDir, sshDir)
71         var out bytes.Buffer
72         cmd.Stdout = &out
73         require.Nil(t, cmd.Run())
74
75         cmd = exec.Command("ls", sshDir)
76         cmd.Stdout = &out
77         require.Nil(t, cmd.Run())
78
79         files := strings.Split(fmt.Sprint(cmd.Stdout), "\n")
80
81         for _, f := range files {
82                 if f != "" {
83                         file := sshDir + "/" + f
84                         cmd = exec.Command("chmod", "600", file)
85                         cmd.Stdout = &out
86                         require.Nil(t, cmd.Run())
87                 }
88         }
89
90         var outSSHd bytes.Buffer
91         var sshdCmd string
92         cmd = exec.Command("which", "sshd")
93         cmd.Stdout = &outSSHd
94         if cmd.Run() != nil {
95                 if common.Exists("/usr/sbin/sshd") {
96                         sshdCmd = "/usr/sbin/sshd"
97                 } else if common.Exists("/usr/bin/sshd") {
98                         sshdCmd = "/usr/sbin/sshd"
99                 } else {
100                         require.FailNow(t, "Cannot find sshd command, please install it or set in your PATH")
101                 }
102         } else {
103                 sshdCmd = strings.TrimSpace(fmt.Sprint(cmd.Stdout))
104         }
105
106         var port string
107         _, port = launchSSHd(sshDir, procSSHd, sshdCmd)
108         go func(p *os.Process) {
109                 Debug(t, "sshd is launching")
110                 if status, err := p.Wait(); err != nil {
111                         log.Fatalf("status=%v\n err=%v\n", status, err)
112                 }
113         }(*procSSHd)
114         return sshDir, port
115 }
116
117 /*wait for terminal prompt*/
118 func waitForPrompt(t *testing.T, channel chan xsapiv1.TerminalOutMsg, prompt string) string {
119         step := 1 * time.Millisecond
120         timeout := 10 * time.Second
121         current := 0 * time.Second
122         out := ""
123         re := regexp.MustCompile("^" + prompt)
124
125         for {
126                 select {
127                 case outMsg := <-channel:
128                         out += string(outMsg.Stdout)
129                         if string(outMsg.Stderr) != "" {
130                                 out += string(outMsg.Stderr)
131                         }
132                         for _, line := range strings.Split(out, "\n") {
133                                 if re.MatchString(line) {
134                                         return line
135                                 }
136                         }
137                 case <-time.After(step):
138                         current = current + step
139                         if current >= timeout {
140                                 require.FailNow(t, "Never received prompt message from terminal (output:"+out+")")
141                         }
142                 }
143         }
144 }
145
146 func ConnectTargetEvents(t *testing.T, channel chan xsapiv1.TargetConfig) {
147         sCli.Conn.On(xsapiv1.EVTTargetAdd, func(e xsapiv1.EventMsg) {
148                 target, _ := e.DecodeTargetEvent()
149                 channel <- target
150         })
151
152         args := xsapiv1.EventRegisterArgs{Name: xsapiv1.EVTTargetAdd}
153         require.Nil(t, HTTPCli.Post("/events/register", args, nil))
154
155         sCli.Conn.On(xsapiv1.EVTTargetRemove, func(e xsapiv1.EventMsg) {
156                 target, _ := e.DecodeTargetEvent()
157                 channel <- target
158         })
159
160         args = xsapiv1.EventRegisterArgs{Name: xsapiv1.EVTTargetRemove}
161         require.Nil(t, HTTPCli.Post("/events/register", args, nil))
162 }
163
164 func DisconnectTargetEvents(t *testing.T) {
165         args := xsapiv1.EventRegisterArgs{Name: xsapiv1.EVTTargetAdd}
166         require.Nil(t, HTTPCli.Post("/events/unregister", args, nil))
167         args = xsapiv1.EventRegisterArgs{Name: xsapiv1.EVTTargetRemove}
168         require.Nil(t, HTTPCli.Post("/events/unregister", args, nil))
169 }
170
171 func ConnectTermEvents(t *testing.T, channel chan xsapiv1.TerminalConfig) {
172         sCli.Conn.On(xsapiv1.EVTTargetTerminalAdd, func(e xsapiv1.EventMsg) {
173                 termEvt, _ := e.DecodeTerminalEvent()
174                 channel <- termEvt
175         })
176
177         args := xsapiv1.EventRegisterArgs{Name: xsapiv1.EVTTargetTerminalAdd}
178         require.Nil(t, HTTPCli.Post("/events/register", args, nil))
179
180         sCli.Conn.On(xsapiv1.EVTTargetTerminalStateChange, func(e xsapiv1.EventMsg) {
181                 termEvt, _ := e.DecodeTerminalEvent()
182                 channel <- termEvt
183         })
184
185         args = xsapiv1.EventRegisterArgs{Name: xsapiv1.EVTTargetTerminalStateChange}
186         require.Nil(t, HTTPCli.Post("/events/register", args, nil))
187
188         sCli.Conn.On(xsapiv1.EVTTargetTerminalRemove, func(e xsapiv1.EventMsg) {
189                 termEvt, _ := e.DecodeTerminalEvent()
190                 channel <- termEvt
191         })
192
193         args = xsapiv1.EventRegisterArgs{Name: xsapiv1.EVTTargetTerminalRemove}
194         require.Nil(t, HTTPCli.Post("/events/register", args, nil))
195 }
196
197 func DisconnectTermEvents(t *testing.T) {
198         args := xsapiv1.EventRegisterArgs{Name: xsapiv1.EVTTargetTerminalAdd}
199         require.Nil(t, HTTPCli.Post("/events/unregister", args, nil))
200         args = xsapiv1.EventRegisterArgs{Name: xsapiv1.EVTTargetTerminalStateChange}
201         require.Nil(t, HTTPCli.Post("/events/unregister", args, nil))
202         args = xsapiv1.EventRegisterArgs{Name: xsapiv1.EVTTargetTerminalRemove}
203         require.Nil(t, HTTPCli.Post("/events/unregister", args, nil))
204 }
205
206 func AddTargets(t *testing.T, nbTargets int, chTarget chan xsapiv1.TargetConfig) []string {
207         listID := make([]string, nbTargets)
208         for i := 0; i < nbTargets; i++ {
209                 /*target is local*/
210                 target := xsapiv1.TargetConfig{
211                         Name: "fakeTarget" + strconv.Itoa(i),
212                         Type: xsapiv1.TypeTgtStandard,
213                         IP:   "127.0.0.1",
214                 }
215                 /*add target*/
216                 require.Nil(t, HTTPCli.Post("/targets", target, &target))
217                 Debugf(t, "add target %v", target.Name)
218                 targetEvt := <-chTarget //waiting for event targetAdd
219                 require.Equal(t, target.ID, targetEvt.ID)
220                 listID[i] = target.ID
221         }
222         for i := 0; i < nbTargets; i++ {
223                 var target xsapiv1.TargetConfig
224                 require.Nil(t, HTTPCli.Get("/targets/"+listID[i], &target))
225                 require.Equal(t, target.Status, "Enable")
226         }
227         return listID
228 }
229
230 func AddTerms(t *testing.T, nbTerms int, listID []string, chTermEvt chan xsapiv1.TerminalConfig, sshDir string, port string) {
231         for j := 0; j < len(listID); j++ {
232                 listTermsID := make([]string, nbTerms)
233                 for i := 0; i < nbTerms; i++ {
234                         term := xsapiv1.TerminalConfig{
235                                 Name: "terminal" + strconv.Itoa(i),
236                                 Type: xsapiv1.TypeTermSSH,
237                                 Options: []string{
238                                         "-p",
239                                         port,
240                                         "-i",
241                                         sshDir + "/ssh",
242                                         "-o",
243                                         "StrictHostKeyChecking=no",
244                                 },
245                         }
246                         /*add terminal on target*/
247                         require.Nil(t, HTTPCli.Post("/targets/"+listID[j]+"/terminals", term, &term))
248                         Debugf(t, "add terminal %v", term.Name)
249                         termEvt := <-chTermEvt //waiting for event terminalAdd*/
250                         require.Equal(t, term.ID, termEvt.ID)
251                         listTermsID[i] = term.ID
252                 }
253                 require.Equal(t, len(listTermsID), nbTerms)
254                 for i := 0; i < nbTerms; i++ {
255                         var term xsapiv1.TerminalConfig
256                         require.Nil(t, HTTPCli.Get("/targets/"+listID[j]+"/terminals/"+listTermsID[i], &term))
257                         require.Equal(t, term.Status, "Close")
258                 }
259         }
260 }
261
262 func PostTerms(t *testing.T, post string, chTermEvt chan xsapiv1.TerminalConfig, chTerm chan xsapiv1.TerminalOutMsg,
263         prompt string) {
264         var status string
265         switch post {
266         case "open":
267                 status = "Open"
268         case "close":
269                 status = "Closing"
270         }
271         var targets []xsapiv1.TargetConfig
272         require.Nil(t, HTTPCli.Get("/targets", &targets))
273         for i := 0; i < len(targets); i++ {
274                 var terms []xsapiv1.TerminalConfig
275                 require.Nil(t, HTTPCli.Get("/targets/"+targets[i].ID+"/terminals", &terms))
276                 listTermsID := make([]string, len(terms))
277                 for j := 0; j < len(terms); j++ {
278                         var term xsapiv1.TerminalConfig
279                         /*post action on term*/
280                         require.Nil(t, HTTPCli.Post("/targets/"+targets[i].ID+"/terminals/"+terms[j].ID+"/"+post, terms[j], &term))
281                         Debugf(t, "%v terminal %v", post, term.Name)
282                         termEvt := <-chTermEvt //waiting for event terminalStateChange
283                         if post == "open" {
284                                 data := []byte("PS1=" + prompt + " bash -norc\n")
285                                 require.Nil(t, sCli.Conn.Emit(xsapiv1.TerminalInEvent, data))
286                                 waitForPrompt(t, chTerm, prompt)
287                         }
288                         require.Equal(t, term.ID, termEvt.ID)
289                         require.Equal(t, term.Status, status)
290                         require.Equal(t, termEvt.Status, status)
291                         listTermsID[i] = term.ID
292                 }
293                 time.Sleep(10 * time.Millisecond)
294                 for j := 0; j < len(listTermsID); j++ {
295                         var term xsapiv1.TerminalConfig
296                         require.Nil(t, HTTPCli.Get("/targets/"+targets[i].ID+"/terminals/"+listTermsID[i], &term))
297                         require.True(t, strings.EqualFold(term.Status, post))
298                         Debugf(t, "check that term status %v is %v", term.Name, post)
299                 }
300         }
301 }
302
303 func RemoveTermsTargets(t *testing.T, chTarget chan xsapiv1.TargetConfig, chTermEvt chan xsapiv1.TerminalConfig) {
304         var targets []xsapiv1.TargetConfig
305         require.Nil(t, HTTPCli.Get("/targets", &targets))
306         for i := 0; i < len(targets); i++ {
307                 var terms []xsapiv1.TerminalConfig
308                 require.Nil(t, HTTPCli.Get("/targets/"+targets[i].ID+"/terminals", &terms))
309                 for j := 0; j < len(terms); j++ {
310                         var term xsapiv1.TerminalConfig
311                         require.Nil(t, HTTPCli.Delete("/targets/"+targets[i].ID+"/terminals/"+terms[j].ID, &term))
312                         termEvt := <-chTermEvt
313                         require.Equal(t, term.ID, termEvt.ID)
314                         require.NotNil(t, HTTPCli.Delete("/targets/"+targets[i].ID+"/terminals/"+terms[j].ID, &term))
315                         Debugf(t, "remove terminal %v", term.Name)
316                 }
317                 var tgtRes xsapiv1.TargetConfig
318                 require.Nil(t, HTTPCli.Delete("/targets/"+targets[i].ID, &tgtRes))
319                 targetEvt := <-chTarget //waiting for remove terminal event
320                 require.Equal(t, tgtRes.ID, targetEvt.ID)
321                 require.Equal(t, targets[i].ID, tgtRes.ID)
322         }
323 }
324 func TestTarget(t *testing.T) {
325         prompt := "--PROMPT--"
326         var procSSHd *os.Process
327         sshDir, port := InitSSH(t, &procSSHd)
328         defer procSSHd.Kill()
329
330         nbTargets := 2
331         nbTermsByTarget := 2
332         /*channel for target events*/
333         chTarget := make(chan xsapiv1.TargetConfig)
334         defer close(chTarget)
335         ConnectTargetEvents(t, chTarget)
336
337         /*channel for terminal events*/
338         chTermEvt := make(chan xsapiv1.TerminalConfig)
339         defer close(chTermEvt)
340         ConnectTermEvents(t, chTermEvt)
341
342         /*check that targetArray is empty at startup*/
343         var targetArray []xsapiv1.TargetConfig
344         require.Nil(t, HTTPCli.Get("/targets", &targetArray))
345         require.Equal(t, len(targetArray), 0)
346
347         listID := AddTargets(t, nbTargets, chTarget)
348         AddTerms(t, nbTermsByTarget, listID, chTermEvt, sshDir, port)
349
350         /*channel for TerminalOutMsg*/
351         chTerm := make(chan xsapiv1.TerminalOutMsg)
352         defer close(chTerm)
353
354         /*connect on terminalOutMsg event*/
355         sCli.Conn.On(xsapiv1.TerminalOutEvent, func(ev xsapiv1.TerminalOutMsg) {
356                 chTerm <- ev
357         })
358
359         /*open terminals*/
360         PostTerms(t, "open", chTermEvt, chTerm, prompt)
361
362         /*create toto file through terminals*/
363         rootCfgDir := os.Getenv(envRootCfgDir)
364         totoFile := path.Join(rootCfgDir, "toto")
365
366         /*test with 2 terminals*/
367         for i := 0; i < 2; i++ {
368                 totoFileCurrent := totoFile + strconv.Itoa(i)
369                 /*send cmd though term*/
370                 data := []byte("echo helloWorld" + strconv.Itoa(i) + " >> " + totoFileCurrent + "\n")
371                 Debugf(t, "send following command through terminal: %v", string(data))
372                 require.Nil(t, sCli.Conn.Emit(xsapiv1.TerminalInEvent, data))
373                 waitForPrompt(t, chTerm, prompt) //waiting for terminal prompt
374
375                 /*check that toto file is created*/
376                 _, err := os.Stat(totoFileCurrent)
377                 require.Nil(t, err)
378
379                 /*send cmd though term*/
380                 data = []byte("cat " + totoFileCurrent + "\n")
381                 Debugf(t, "send following command through terminal: %v", string(data))
382                 require.Nil(t, sCli.Conn.Emit(xsapiv1.TerminalInEvent, data))
383
384                 <-chTerm                         //cmd sent
385                 termOut := <-chTerm              //result of cat cmd
386                 waitForPrompt(t, chTerm, prompt) //wait for terminal prompt
387                 /*check that terminal msg is what was written before*/
388                 require.Equal(t, string(termOut.Stdout), "helloWorld"+strconv.Itoa(i)+"\r\n")
389                 Debugf(t, "check terminal output msg: %v", string(termOut.Stdout))
390         }
391
392         PostTerms(t, "close", chTermEvt, nil, prompt)
393
394         /*remove targets and terms*/
395         RemoveTermsTargets(t, chTarget, chTermEvt)
396         DisconnectTargetEvents(t)
397         DisconnectTermEvents(t)
398 }
399
400 func TestTargetErrors(t *testing.T) {
401         /*cannot create empty target*/
402         target := xsapiv1.TargetConfig{}
403         var targetRes xsapiv1.TargetConfig
404         require.NotNil(t, HTTPCli.Post("/targets", target, &targetRes))
405         Debugf(t, "error while creating empty target")
406         /*check cannot create target with no IP*/
407         target.Type = xsapiv1.TypeTgtStandard
408         require.NotNil(t, HTTPCli.Post("/targets", target, &targetRes))
409         Debugf(t, "error while creating target without IP")
410         target.IP = "127.0.0.1"
411         require.Nil(t, HTTPCli.Post("/targets", target, &targetRes))
412         Debugf(t, "create target %v", targetRes.Name)
413
414         /*cannot create empty terminal*/
415         term := xsapiv1.TerminalConfig{}
416         var termRes xsapiv1.TerminalConfig
417         require.NotNil(t, HTTPCli.Post("/targets/"+targetRes.ID+"/terminals", term, &termRes))
418         Debugf(t, "error while creating empty terminal")
419         term.Type = xsapiv1.TypeTermSSH
420         require.NotNil(t, HTTPCli.Post("/targets/"+"1010"+"/terminals", term, &termRes))
421         Debugf(t, "error while creating terminal on an non existing target")
422         require.Nil(t, HTTPCli.Post("/targets/"+targetRes.ID+"/terminals", term, &termRes))
423         require.Nil(t, HTTPCli.Post("/targets/"+targetRes.ID+"/terminals", term, &termRes))
424         require.Nil(t, HTTPCli.Post("/targets/"+targetRes.ID+"/terminals", term, &termRes))
425         require.Nil(t, HTTPCli.Post("/targets/"+targetRes.ID+"/terminals", term, &termRes))
426         Debugf(t, "create several terminals")
427
428         /*remove targets and terms*/
429         var targetArray []xsapiv1.TargetConfig
430         require.Nil(t, HTTPCli.Get("/targets", &targetArray))
431         for i := 0; i < len(targetArray); i++ {
432                 var termArray []xsapiv1.TerminalConfig
433                 require.Nil(t, HTTPCli.Get("/targets/"+targetArray[i].ID+"/terminals", &termArray))
434                 for j := 0; j < len(termArray); j++ {
435                         require.Nil(t, HTTPCli.Delete("/targets/"+targetArray[i].ID+"/terminals/"+termArray[j].ID, &termRes))
436                         Debugf(t, "delete terminal %v", termRes.Name)
437                         require.NotNil(t, HTTPCli.Delete("/targets/"+targetArray[i].ID+"/terminals/"+termArray[j].ID, &termRes))
438                         Debugf(t, "error while deleting an already deleted terminal %v", termRes.Name)
439                 }
440                 var tgtRes xsapiv1.TargetConfig
441                 require.Nil(t, HTTPCli.Delete("/targets/"+targetArray[i].ID, &tgtRes))
442                 Debugf(t, "delete target %v", tgtRes.Name)
443                 require.Equal(t, targetArray[i].ID, tgtRes.ID)
444                 require.NotNil(t, HTTPCli.Delete("/targets/"+targetArray[i].ID, &tgtRes))
445                 Debugf(t, "error while deleting an already deleted target %v", tgtRes.Name)
446         }
447 }