Implement download for WinRM communicator

Signed-off-by: Mark DeLillo <mdelillo@pivotal.io>
This commit is contained in:
Paul Nikonowicz 2017-04-26 12:15:30 -04:00 committed by Mark DeLillo
parent 9f992b8f80
commit 0614fc234f
3 changed files with 85 additions and 5 deletions

View File

@ -1,8 +1,10 @@
package winrm
import (
"encoding/base64"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
@ -143,7 +145,20 @@ func (c *Communicator) UploadDir(dst string, src string, exclude []string) error
}
func (c *Communicator) Download(src string, dst io.Writer) error {
return fmt.Errorf("WinRM doesn't support download.")
endpoint := winrm.NewEndpoint(c.endpoint.Host, c.endpoint.Port, c.config.Https, c.config.Insecure, nil, nil, nil, c.config.Timeout)
client, err := winrm.NewClient(endpoint, c.config.Username, c.config.Password)
if err != nil {
return err
}
encodeScript := `$file=[System.IO.File]::ReadAllBytes("%s"); Write-Output $([System.Convert]::ToBase64String($file))`
base64DecodePipe := &Base64Pipe{w: dst}
cmd := winrm.Powershell(fmt.Sprintf(encodeScript, src))
_, err = client.Run(cmd, base64DecodePipe, ioutil.Discard)
return err
}
func (c *Communicator) DownloadDir(src string, dst string, exclude []string) error {
@ -164,3 +179,33 @@ func (c *Communicator) newCopyClient() (*winrmcp.Winrmcp, error) {
TransportDecorator: c.config.TransportDecorator,
})
}
type Base64Pipe struct {
w io.Writer // underlying writer (file, buffer)
}
func (d *Base64Pipe) ReadFrom(r io.Reader) (int64, error) {
b, err := ioutil.ReadAll(r)
if err != nil {
return 0, err
}
var i int
i, err = d.Write(b)
if err != nil {
return 0, err
}
return int64(i), err
}
func (d *Base64Pipe) Write(p []byte) (int, error) {
dst := make([]byte, base64.StdEncoding.DecodedLen(len(p)))
if _, err := base64.StdEncoding.Decode(dst, p); err != nil {
return 0, err
}
return d.w.Write(dst)
}

View File

@ -3,6 +3,7 @@ package winrm
import (
"bytes"
"io"
"strings"
"testing"
"time"
@ -10,6 +11,9 @@ import (
"github.com/hashicorp/packer/packer"
)
const PAYLOAD = "stuff"
const BASE64_ENCODED_PAYLOAD = "c3R1ZmY="
func newMockWinRMServer(t *testing.T) *winrmtest.Remote {
wrm := winrmtest.NewRemote()
@ -26,9 +30,16 @@ func newMockWinRMServer(t *testing.T) *winrmtest.Remote {
return 0
})
wrm.CommandFunc(
winrmtest.MatchPattern(`^echo `+BASE64_ENCODED_PAYLOAD+` >> ".*"$`),
func(out, err io.Writer) int {
return 0
})
wrm.CommandFunc(
winrmtest.MatchPattern(`^powershell.exe -EncodedCommand .*$`),
func(out, err io.Writer) int {
out.Write([]byte(BASE64_ENCODED_PAYLOAD))
return 0
})
@ -86,9 +97,21 @@ func TestUpload(t *testing.T) {
if err != nil {
t.Fatalf("error creating communicator: %s", err)
}
err = c.Upload("C:/Temp/packer.cmd", bytes.NewReader([]byte("something")), nil)
file := "C:/Temp/packer.cmd"
err = c.Upload(file, strings.NewReader(PAYLOAD), nil)
if err != nil {
t.Fatalf("error uploading file: %s", err)
}
dest := new(bytes.Buffer)
err = c.Download(file, dest)
if err != nil {
t.Fatalf("error downloading file: %s", err)
}
downloadedPayload := strings.TrimRight(dest.String(), "\x00")
if downloadedPayload != PAYLOAD {
t.Fatalf("files are not equal: expected [%s] length: %v, got [%s] length %v", PAYLOAD, len(PAYLOAD), downloadedPayload, len(downloadedPayload))
}
}

View File

@ -5,6 +5,7 @@ import (
"crypto/x509"
"fmt"
"io"
"sync"
"github.com/masterzen/winrm/soap"
)
@ -114,10 +115,21 @@ func (c *Client) Run(command string, stdout io.Writer, stderr io.Writer) (int, e
return 1, err
}
go io.Copy(stdout, cmd.Stdout)
go io.Copy(stderr, cmd.Stderr)
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
io.Copy(stdout, cmd.Stdout)
}()
go func() {
defer wg.Done()
io.Copy(stderr, cmd.Stderr)
}()
cmd.Wait()
wg.Wait()
return cmd.ExitCode(), cmd.err
}