From 0614fc234f0288723f67fedecf73901af7fbf73b Mon Sep 17 00:00:00 2001 From: Paul Nikonowicz Date: Wed, 26 Apr 2017 12:15:30 -0400 Subject: [PATCH 1/3] Implement download for WinRM communicator Signed-off-by: Mark DeLillo --- communicator/winrm/communicator.go | 47 ++++++++++++++++++++- communicator/winrm/communicator_test.go | 27 +++++++++++- vendor/github.com/masterzen/winrm/client.go | 16 ++++++- 3 files changed, 85 insertions(+), 5 deletions(-) diff --git a/communicator/winrm/communicator.go b/communicator/winrm/communicator.go index 69f89194e..355548715 100644 --- a/communicator/winrm/communicator.go +++ b/communicator/winrm/communicator.go @@ -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) +} diff --git a/communicator/winrm/communicator_test.go b/communicator/winrm/communicator_test.go index 5f61680f9..b80f8316b 100644 --- a/communicator/winrm/communicator_test.go +++ b/communicator/winrm/communicator_test.go @@ -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)) + } + } diff --git a/vendor/github.com/masterzen/winrm/client.go b/vendor/github.com/masterzen/winrm/client.go index 03755ff4e..732dd61cb 100644 --- a/vendor/github.com/masterzen/winrm/client.go +++ b/vendor/github.com/masterzen/winrm/client.go @@ -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 } From 9a16b6768effc9e40a3c445ea8d085c7f277e643 Mon Sep 17 00:00:00 2001 From: Mark DeLillo Date: Wed, 26 Apr 2017 12:15:57 -0400 Subject: [PATCH 2/3] Fix decode - Write decoded bytes without padding Signed-off-by: Natalie Arellano --- communicator/winrm/communicator.go | 5 +++-- communicator/winrm/communicator_test.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/communicator/winrm/communicator.go b/communicator/winrm/communicator.go index 355548715..d1f7d2ab6 100644 --- a/communicator/winrm/communicator.go +++ b/communicator/winrm/communicator.go @@ -203,9 +203,10 @@ func (d *Base64Pipe) ReadFrom(r io.Reader) (int64, error) { 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 { + decodedBytes, err := base64.StdEncoding.Decode(dst, p) + if err != nil { return 0, err } - return d.w.Write(dst) + return d.w.Write(dst[0:decodedBytes]) } diff --git a/communicator/winrm/communicator_test.go b/communicator/winrm/communicator_test.go index b80f8316b..d5eb974ac 100644 --- a/communicator/winrm/communicator_test.go +++ b/communicator/winrm/communicator_test.go @@ -108,7 +108,7 @@ func TestUpload(t *testing.T) { if err != nil { t.Fatalf("error downloading file: %s", err) } - downloadedPayload := strings.TrimRight(dest.String(), "\x00") + downloadedPayload := dest.String() if downloadedPayload != PAYLOAD { t.Fatalf("files are not equal: expected [%s] length: %v, got [%s] length %v", PAYLOAD, len(PAYLOAD), downloadedPayload, len(downloadedPayload)) From 42656ce2c726888acbb247543c74c57a66b54a6f Mon Sep 17 00:00:00 2001 From: David Jahn Date: Wed, 26 Apr 2017 12:16:12 -0400 Subject: [PATCH 3/3] Bump winrm library - Fix race condition in io.copy (https://github.com/masterzen/winrm/pull/67) Signed-off-by: Amin Jamali --- vendor/vendor.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendor/vendor.json b/vendor/vendor.json index 01a407f75..65fb99551 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -576,10 +576,10 @@ "revision": "95ba30457eb1121fa27753627c774c7cd4e90083" }, { - "checksumSHA1": "6uNzRk3ScsTC+olpCXkW4M/L3fg=", + "checksumSHA1": "kWWDtHJJp7Hiq0zj0xecRGqMoeU=", "path": "github.com/masterzen/winrm", - "revision": "b7e3d2de4979ce5eae5c1c1ef450040c5d675a89", - "revisionTime": "2017-01-26T07:02:57Z" + "revision": "acf371f6aff113fc0104a61cd72db45a7c27d310", + "revisionTime": "2017-03-29T23:04:57Z" }, { "checksumSHA1": "KTsgWipT3ennAAtaKxEZairxero=",