Example #1
0
func (c *CommunicatorServer) Start(args *CommunicatorStartArgs, reply *interface{}) (err error) {
	// Build the RemoteCmd on this side so that it all pipes over
	// to the remote side.
	var cmd packer.RemoteCmd
	cmd.Command = args.Command

	if args.StdinAddress != "" {
		stdinC, err := net.Dial("tcp", args.StdinAddress)
		if err != nil {
			return err
		}

		cmd.Stdin = stdinC
	}

	if args.StdoutAddress != "" {
		stdoutC, err := net.Dial("tcp", args.StdoutAddress)
		if err != nil {
			return err
		}

		cmd.Stdout = stdoutC
	}

	if args.StderrAddress != "" {
		stderrC, err := net.Dial("tcp", args.StderrAddress)
		if err != nil {
			return err
		}

		cmd.Stderr = stderrC
	}

	// Connect to the response address so we can write our result to it
	// when ready.
	responseC, err := net.Dial("tcp", args.ResponseAddress)
	if err != nil {
		return err
	}

	responseWriter := gob.NewEncoder(responseC)

	// Start the actual command
	err = c.c.Start(&cmd)

	// Start a goroutine to spin and wait for the process to actual
	// exit. When it does, report it back to caller...
	go func() {
		defer responseC.Close()

		for !cmd.Exited {
			time.Sleep(50 * time.Millisecond)
		}

		responseWriter.Encode(&CommandFinished{cmd.ExitStatus})
	}()

	return
}
Example #2
0
func (c *CommunicatorServer) Start(args *CommunicatorStartArgs, reply *interface{}) error {
	// Build the RemoteCmd on this side so that it all pipes over
	// to the remote side.
	var cmd packer.RemoteCmd
	cmd.Command = args.Command

	// Create a channel to signal we're done so that we can close
	// our stdin/stdout/stderr streams
	toClose := make([]io.Closer, 0)
	doneCh := make(chan struct{})
	go func() {
		<-doneCh
		for _, conn := range toClose {
			defer conn.Close()
		}
	}()

	if args.StdinStreamId > 0 {
		conn, err := c.mux.Dial(args.StdinStreamId)
		if err != nil {
			close(doneCh)
			return NewBasicError(err)
		}

		toClose = append(toClose, conn)
		cmd.Stdin = conn
	}

	if args.StdoutStreamId > 0 {
		conn, err := c.mux.Dial(args.StdoutStreamId)
		if err != nil {
			close(doneCh)
			return NewBasicError(err)
		}

		toClose = append(toClose, conn)
		cmd.Stdout = conn
	}

	if args.StderrStreamId > 0 {
		conn, err := c.mux.Dial(args.StderrStreamId)
		if err != nil {
			close(doneCh)
			return NewBasicError(err)
		}

		toClose = append(toClose, conn)
		cmd.Stderr = conn
	}

	// Connect to the response address so we can write our result to it
	// when ready.
	responseC, err := c.mux.Dial(args.ResponseStreamId)
	if err != nil {
		close(doneCh)
		return NewBasicError(err)
	}
	responseWriter := gob.NewEncoder(responseC)

	// Start the actual command
	err = c.c.Start(&cmd)
	if err != nil {
		close(doneCh)
		return NewBasicError(err)
	}

	// Start a goroutine to spin and wait for the process to actual
	// exit. When it does, report it back to caller...
	go func() {
		defer close(doneCh)
		defer responseC.Close()
		cmd.Wait()
		log.Printf("[INFO] RPC endpoint: Communicator ended with: %d", cmd.ExitStatus)
		responseWriter.Encode(&CommandFinished{cmd.ExitStatus})
	}()

	return nil
}
Example #3
0
func TestCommunicatorRPC(t *testing.T) {
	// Create the interface to test
	c := new(packer.MockCommunicator)

	// Start the server
	server := rpc.NewServer()
	RegisterCommunicator(server, c)
	address := serveSingleConn(server)

	// Create the client over RPC and run some methods to verify it works
	client, err := rpc.Dial("tcp", address)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	remote := Communicator(client)

	// 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

	// Test Start
	err = remote.Start(&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", data)
	}

	// 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)
	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 TestCommunicatorRPC(t *testing.T) {
	assert := asserts.NewTestingAsserts(t, true)

	// Create the interface to test
	c := new(testCommunicator)

	// Start the server
	server := rpc.NewServer()
	RegisterCommunicator(server, c)
	address := serveSingleConn(server)

	// Create the client over RPC and run some methods to verify it works
	client, err := rpc.Dial("tcp", address)
	assert.Nil(err, "should be able to connect")
	remote := Communicator(client)

	// 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

	// Test Start
	err = remote.Start(&cmd)
	assert.Nil(err, "should not have an error")

	// Test that we can read from stdout
	c.startCmd.Stdout.Write([]byte("outfoo\n"))
	bufOut := bufio.NewReader(stdout_r)
	data, err := bufOut.ReadString('\n')
	assert.Nil(err, "should have no problem reading stdout")
	assert.Equal(data, "outfoo\n", "should be correct stdout")

	// Test that we can read from stderr
	c.startCmd.Stderr.Write([]byte("errfoo\n"))
	bufErr := bufio.NewReader(stderr_r)
	data, err = bufErr.ReadString('\n')
	assert.Nil(err, "should have no problem reading stderr")
	assert.Equal(data, "errfoo\n", "should be correct stderr")

	// Test that we can write to stdin
	stdin_w.Write([]byte("infoo\n"))
	bufIn := bufio.NewReader(c.startCmd.Stdin)
	data, err = bufIn.ReadString('\n')
	assert.Nil(err, "should have no problem reading stdin")
	assert.Equal(data, "infoo\n", "should be correct stdin")

	// Test that we can get the exit status properly
	c.startCmd.ExitStatus = 42
	c.startCmd.Exited = true

	for i := 0; i < 5; i++ {
		if cmd.Exited {
			assert.Equal(cmd.ExitStatus, 42, "should have proper exit status")
			break
		}

		time.Sleep(50 * time.Millisecond)
	}

	assert.True(cmd.Exited, "should have exited")

	// Test that we can upload things
	uploadR, uploadW := io.Pipe()
	go uploadW.Write([]byte("uploadfoo\n"))
	err = remote.Upload("foo", uploadR)
	assert.Nil(err, "should not error")
	assert.True(c.uploadCalled, "should be called")
	assert.Equal(c.uploadPath, "foo", "should be correct path")
	assert.Equal(c.uploadData, "uploadfoo\n", "should have the proper data")

	// 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
	}()

	err = remote.Download("bar", downloadW)
	assert.Nil(err, "should not error")
	assert.True(c.downloadCalled, "should have called download")
	assert.Equal(c.downloadPath, "bar", "should have correct download path")

	<-downloadDone
	assert.Nil(downloadErr, "should not error reading download data")
	assert.Equal(downloadData, "download\n", "should have the proper data")
}
Example #5
0
func (c *CommunicatorServer) Start(args *CommunicatorStartArgs, reply *interface{}) (err error) {
	// Build the RemoteCmd on this side so that it all pipes over
	// to the remote side.
	var cmd packer.RemoteCmd
	cmd.Command = args.Command

	toClose := make([]net.Conn, 0)
	if args.StdinAddress != "" {
		stdinC, err := tcpDial(args.StdinAddress)
		if err != nil {
			return err
		}

		toClose = append(toClose, stdinC)
		cmd.Stdin = stdinC
	}

	if args.StdoutAddress != "" {
		stdoutC, err := tcpDial(args.StdoutAddress)
		if err != nil {
			return err
		}

		toClose = append(toClose, stdoutC)
		cmd.Stdout = stdoutC
	}

	if args.StderrAddress != "" {
		stderrC, err := tcpDial(args.StderrAddress)
		if err != nil {
			return err
		}

		toClose = append(toClose, stderrC)
		cmd.Stderr = stderrC
	}

	// Connect to the response address so we can write our result to it
	// when ready.
	responseC, err := tcpDial(args.ResponseAddress)
	if err != nil {
		return err
	}

	responseWriter := gob.NewEncoder(responseC)

	// Start the actual command
	err = c.c.Start(&cmd)

	// Start a goroutine to spin and wait for the process to actual
	// exit. When it does, report it back to caller...
	go func() {
		defer responseC.Close()
		for _, conn := range toClose {
			defer conn.Close()
		}

		cmd.Wait()
		responseWriter.Encode(&CommandFinished{cmd.ExitStatus})
	}()

	return
}