provisioner/shell: convert windows line endings to Unix [GH-277]
This commit is contained in:
parent
dfb4e80d57
commit
abbac36796
|
@ -13,6 +13,9 @@ IMPROVEMENTS:
|
||||||
* core: User variables can now be used for integer, boolean, etc.
|
* core: User variables can now be used for integer, boolean, etc.
|
||||||
values. [GH-418]
|
values. [GH-418]
|
||||||
* builder/amazon/all: Interrupts work while waiting for AMI to be ready.
|
* builder/amazon/all: Interrupts work while waiting for AMI to be ready.
|
||||||
|
* provisioner/shell: Script line-endings are automatically converted to
|
||||||
|
Unix-style line-endings. Can be disabled by setting "binary" to "true".
|
||||||
|
[GH-277]
|
||||||
|
|
||||||
BUG FIXES:
|
BUG FIXES:
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/mitchellh/packer/common"
|
"github.com/mitchellh/packer/common"
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
@ -20,6 +21,10 @@ const DefaultRemotePath = "/tmp/script.sh"
|
||||||
type config struct {
|
type config struct {
|
||||||
common.PackerConfig `mapstructure:",squash"`
|
common.PackerConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
|
// If true, the script contains binary and line endings will not be
|
||||||
|
// converted from Windows to Unix-style.
|
||||||
|
Binary bool
|
||||||
|
|
||||||
// An inline script to execute. Multiple strings are all executed
|
// An inline script to execute. Multiple strings are all executed
|
||||||
// in the context of a single shell.
|
// in the context of a single shell.
|
||||||
Inline []string
|
Inline []string
|
||||||
|
@ -259,6 +264,11 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var r io.Reader = f
|
||||||
|
if !p.config.Binary {
|
||||||
|
r = &UnixReader{Reader: r}
|
||||||
|
}
|
||||||
|
|
||||||
if err := comm.Upload(p.config.RemotePath, f); err != nil {
|
if err := comm.Upload(p.config.RemotePath, f); err != nil {
|
||||||
return fmt.Errorf("Error uploading script: %s", err)
|
return fmt.Errorf("Error uploading script: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
package shell
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnixReader is a Reader implementation that automatically converts
|
||||||
|
// Windows line endings to Unix line endings.
|
||||||
|
type UnixReader struct {
|
||||||
|
Reader io.Reader
|
||||||
|
|
||||||
|
buf []byte
|
||||||
|
once sync.Once
|
||||||
|
scanner *bufio.Scanner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *UnixReader) Read(p []byte) (n int, err error) {
|
||||||
|
// Create the buffered reader once
|
||||||
|
r.once.Do(func() {
|
||||||
|
r.scanner = bufio.NewScanner(r.Reader)
|
||||||
|
r.scanner.Split(scanUnixLine)
|
||||||
|
})
|
||||||
|
|
||||||
|
// If we have no data in our buffer, scan to the next token
|
||||||
|
if len(r.buf) == 0 {
|
||||||
|
if !r.scanner.Scan() {
|
||||||
|
err = r.scanner.Err()
|
||||||
|
if err == nil {
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.buf = r.scanner.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out as much data as we can to the buffer, storing the rest
|
||||||
|
// for the next read.
|
||||||
|
n = len(p)
|
||||||
|
if n > len(r.buf) {
|
||||||
|
n = len(r.buf)
|
||||||
|
}
|
||||||
|
copy(p, r.buf)
|
||||||
|
r.buf = r.buf[n:]
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// scanUnixLine is a bufio.Scanner SplitFunc. It tokenizes on lines, but
|
||||||
|
// only returns unix-style lines. So even if the line is "one\r\n", the
|
||||||
|
// token returned will be "one\n".
|
||||||
|
func scanUnixLine(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||||
|
if atEOF && len(data) == 0 {
|
||||||
|
return 0, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if i := bytes.IndexByte(data, '\n'); i >= 0 {
|
||||||
|
// We have a new-line terminated line. Return the line with the newline
|
||||||
|
return i + 1, dropCR(data[0 : i+1]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if atEOF {
|
||||||
|
// We have a final, non-terminated line
|
||||||
|
return len(data), dropCR(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if data[len(data)-1] != '\r' {
|
||||||
|
// We have a normal line, just let it tokenize
|
||||||
|
return len(data), data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need more data
|
||||||
|
return 0, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dropCR(data []byte) []byte {
|
||||||
|
if len(data) > 0 && data[len(data)-2] == '\r' {
|
||||||
|
// Trim off the last byte and replace it with a '\n'
|
||||||
|
data = data[0 : len(data)-1]
|
||||||
|
data[len(data)-1] = '\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package shell
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUnixReader_impl(t *testing.T) {
|
||||||
|
var raw interface{}
|
||||||
|
raw = new(UnixReader)
|
||||||
|
if _, ok := raw.(io.Reader); !ok {
|
||||||
|
t.Fatal("should be reader")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnixReader(t *testing.T) {
|
||||||
|
input := "one\r\ntwo\nthree\r\n"
|
||||||
|
expected := "one\ntwo\nthree\n"
|
||||||
|
|
||||||
|
r := &UnixReader{
|
||||||
|
Reader: bytes.NewReader([]byte(input)),
|
||||||
|
}
|
||||||
|
|
||||||
|
result := new(bytes.Buffer)
|
||||||
|
if _, err := io.Copy(result, r); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.String() != expected {
|
||||||
|
t.Fatalf("bad: %#v", result.String())
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,6 +47,10 @@ Exactly _one_ of the following is required:
|
||||||
|
|
||||||
Optional parameters:
|
Optional parameters:
|
||||||
|
|
||||||
|
* `binary` (boolean) - If true, specifies that the script(s) are binary
|
||||||
|
files, and Packer should therefore not convert Windows line endings to
|
||||||
|
Unix line endings (if there are any). By default this is false.
|
||||||
|
|
||||||
* `environment_vars` (array of strings) - An array of key/value pairs
|
* `environment_vars` (array of strings) - An array of key/value pairs
|
||||||
to inject prior to the execute_command. The format should be
|
to inject prior to the execute_command. The format should be
|
||||||
`key=value`. Packer injects some environmental variables by default
|
`key=value`. Packer injects some environmental variables by default
|
||||||
|
|
Loading…
Reference in New Issue