packer-cn/packer/rpc/communicator_test.go
Adrien Delorme f555e7a9f2 allow a provisioner to timeout
* I had to contextualise Communicator.Start and RemoteCmd.StartWithUi
NOTE: Communicator.Start starts a RemoteCmd but RemoteCmd.StartWithUi will run the cmd and wait for a return, so I renamed StartWithUi to RunWithUi so that the intent is clearer.
Ideally in the future RunWithUi will be named back to StartWithUi and the exit status or wait funcs of the command will allow to wait for a return. If you do so please read carrefully https://golang.org/pkg/os/exec/#Cmd.Stdout to avoid a deadlock
* cmd.ExitStatus to cmd.ExitStatus() is now blocking to avoid race conditions
* also had to simplify StartWithUi
2019-04-08 20:09:21 +02:00

170 lines
3.4 KiB
Go

package rpc
import (
"bufio"
"context"
"io"
"reflect"
"testing"
"github.com/hashicorp/packer/packer"
)
func TestCommunicatorRPC(t *testing.T) {
// Create the interface to test
c := new(packer.MockCommunicator)
// Start the server
client, server := testClientServer(t)
defer client.Close()
defer server.Close()
server.RegisterCommunicator(c)
remote := client.Communicator()
// The remote command we'll use
stdin_r, stdin_w := io.Pipe()
stdout_r, stdout_w := io.Pipe()
stderr_r, stderr_w := io.Pipe()
var cmd packer.RemoteCmd
cmd.Command = "foo"
cmd.Stdin = stdin_r
cmd.Stdout = stdout_w
cmd.Stderr = stderr_w
// Send some data on stdout and stderr from the mock
c.StartStdout = "outfoo\n"
c.StartStderr = "errfoo\n"
c.StartExitStatus = 42
ctx := context.Background()
// Test Start
err := remote.Start(ctx, &cmd)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test that we can read from stdout
bufOut := bufio.NewReader(stdout_r)
data, err := bufOut.ReadString('\n')
if err != nil {
t.Fatalf("err: %s", err)
}
if data != "outfoo\n" {
t.Fatalf("bad data: %s", data)
}
// Test that we can read from stderr
bufErr := bufio.NewReader(stderr_r)
data, err = bufErr.ReadString('\n')
if err != nil {
t.Fatalf("err: %s", err)
}
if data != "errfoo\n" {
t.Fatalf("bad data: %s", data)
}
// Test that we can write to stdin
stdin_w.Write([]byte("info\n"))
stdin_w.Close()
cmd.Wait()
if c.StartStdin != "info\n" {
t.Fatalf("bad data: %s", c.StartStdin)
}
// Test that we can get the exit status properly
if cmd.ExitStatus() != 42 {
t.Fatalf("bad exit: %d", cmd.ExitStatus())
}
// Test that we can upload things
uploadR, uploadW := io.Pipe()
go func() {
defer uploadW.Close()
uploadW.Write([]byte("uploadfoo\n"))
}()
err = remote.Upload("foo", uploadR, nil)
if err != nil {
t.Fatalf("err: %s", err)
}
if !c.UploadCalled {
t.Fatal("should have uploaded")
}
if c.UploadPath != "foo" {
t.Fatalf("path: %s", c.UploadPath)
}
if c.UploadData != "uploadfoo\n" {
t.Fatalf("bad: %s", c.UploadData)
}
// Test that we can upload directories
dirDst := "foo"
dirSrc := "bar"
dirExcl := []string{"foo"}
err = remote.UploadDir(dirDst, dirSrc, dirExcl)
if err != nil {
t.Fatalf("err: %s", err)
}
if c.UploadDirDst != dirDst {
t.Fatalf("bad: %s", c.UploadDirDst)
}
if c.UploadDirSrc != dirSrc {
t.Fatalf("bad: %s", c.UploadDirSrc)
}
if !reflect.DeepEqual(c.UploadDirExclude, dirExcl) {
t.Fatalf("bad: %#v", c.UploadDirExclude)
}
// Test that we can download things
downloadR, downloadW := io.Pipe()
downloadDone := make(chan bool)
var downloadData string
var downloadErr error
go func() {
bufDownR := bufio.NewReader(downloadR)
downloadData, downloadErr = bufDownR.ReadString('\n')
downloadDone <- true
}()
c.DownloadData = "download\n"
err = remote.Download("bar", downloadW)
if err != nil {
t.Fatalf("err: %s", err)
}
if !c.DownloadCalled {
t.Fatal("download should be called")
}
if c.DownloadPath != "bar" {
t.Fatalf("bad: %s", c.DownloadPath)
}
<-downloadDone
if downloadErr != nil {
t.Fatalf("err: %s", downloadErr)
}
if downloadData != "download\n" {
t.Fatalf("bad: %s", downloadData)
}
}
func TestCommunicator_ImplementsCommunicator(t *testing.T) {
var raw interface{}
raw = Communicator(nil)
if _, ok := raw.(packer.Communicator); !ok {
t.Fatal("should be a Communicator")
}
}