mirror of
https://github.com/clearlinux/docker.git
synced 2026-09-05 13:11:30 +00:00
Merge pull request #5706 from vieux/remove_add_string
This commit is contained in:
+2
-1
@@ -3,11 +3,12 @@ package engine
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/dotcloud/docker/utils"
|
||||
)
|
||||
|
||||
// Installer is a standard interface for objects which can "install" themselves
|
||||
|
||||
+4
-3
@@ -1,6 +1,7 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
@@ -56,8 +57,8 @@ func (job *Job) Run() error {
|
||||
defer func() {
|
||||
job.Eng.Logf("-job %s%s", job.CallString(), job.StatusString())
|
||||
}()
|
||||
var errorMessage string
|
||||
job.Stderr.AddString(&errorMessage)
|
||||
var errorMessage = bytes.NewBuffer(nil)
|
||||
job.Stderr.Add(errorMessage)
|
||||
if job.handler == nil {
|
||||
job.Errorf("%s: command not found", job.Name)
|
||||
job.status = 127
|
||||
@@ -76,7 +77,7 @@ func (job *Job) Run() error {
|
||||
return err
|
||||
}
|
||||
if job.status != 0 {
|
||||
return fmt.Errorf("%s", errorMessage)
|
||||
return fmt.Errorf("%s", Tail(errorMessage, 1))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+9
-8
@@ -1,6 +1,8 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -40,13 +42,13 @@ func TestJobStdoutString(t *testing.T) {
|
||||
})
|
||||
|
||||
job := eng.Job("say_something_in_stdout")
|
||||
var output string
|
||||
if err := job.Stdout.AddString(&output); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var outputBuffer = bytes.NewBuffer(nil)
|
||||
job.Stdout.Add(outputBuffer)
|
||||
if err := job.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(outputBuffer)
|
||||
var output = Tail(outputBuffer, 1)
|
||||
if expectedOutput := "Hello world"; output != expectedOutput {
|
||||
t.Fatalf("Stdout last line:\nExpected: %v\nReceived: %v", expectedOutput, output)
|
||||
}
|
||||
@@ -61,13 +63,12 @@ func TestJobStderrString(t *testing.T) {
|
||||
})
|
||||
|
||||
job := eng.Job("say_something_in_stderr")
|
||||
var output string
|
||||
if err := job.Stderr.AddString(&output); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var outputBuffer = bytes.NewBuffer(nil)
|
||||
job.Stderr.Add(outputBuffer)
|
||||
if err := job.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var output = Tail(outputBuffer, 1)
|
||||
if expectedOutput := "Something happened"; output != expectedOutput {
|
||||
t.Fatalf("Stderr last line:\nExpected: %v\nReceived: %v", expectedOutput, output)
|
||||
}
|
||||
|
||||
+23
-58
@@ -1,8 +1,7 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"container/ring"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -16,6 +15,28 @@ type Output struct {
|
||||
used bool
|
||||
}
|
||||
|
||||
// Tail returns the n last lines of a buffer
|
||||
// stripped out of the last \n, if any
|
||||
// if n <= 0, returns an empty string
|
||||
func Tail(buffer *bytes.Buffer, n int) string {
|
||||
if n <= 0 {
|
||||
return ""
|
||||
}
|
||||
bytes := buffer.Bytes()
|
||||
if len(bytes) > 0 && bytes[len(bytes)-1] == '\n' {
|
||||
bytes = bytes[:len(bytes)-1]
|
||||
}
|
||||
for i := buffer.Len() - 2; i >= 0; i-- {
|
||||
if bytes[i] == '\n' {
|
||||
n--
|
||||
if n == 0 {
|
||||
return string(bytes[i+1:])
|
||||
}
|
||||
}
|
||||
}
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
// NewOutput returns a new Output object with no destinations attached.
|
||||
// Writing to an empty Output will cause the written data to be discarded.
|
||||
func NewOutput() *Output {
|
||||
@@ -58,42 +79,6 @@ func (o *Output) AddPipe() (io.Reader, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// AddTail starts a new goroutine which will read all subsequent data written to the output,
|
||||
// line by line, and append the last `n` lines to `dst`.
|
||||
func (o *Output) AddTail(dst *[]string, n int) error {
|
||||
src, err := o.AddPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.tasks.Add(1)
|
||||
go func() {
|
||||
defer o.tasks.Done()
|
||||
Tail(src, n, dst)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddString starts a new goroutine which will read all subsequent data written to the output,
|
||||
// line by line, and store the last line into `dst`.
|
||||
func (o *Output) AddString(dst *string) error {
|
||||
src, err := o.AddPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.tasks.Add(1)
|
||||
go func() {
|
||||
defer o.tasks.Done()
|
||||
lines := make([]string, 0, 1)
|
||||
Tail(src, 1, &lines)
|
||||
if len(lines) == 0 {
|
||||
*dst = ""
|
||||
} else {
|
||||
*dst = lines[0]
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes the same data to all registered destinations.
|
||||
// This method is thread-safe.
|
||||
func (o *Output) Write(p []byte) (n int, err error) {
|
||||
@@ -174,26 +159,6 @@ func (i *Input) Add(src io.Reader) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tail reads from `src` line per line, and returns the last `n` lines as an array.
|
||||
// A ring buffer is used to only store `n` lines at any time.
|
||||
func Tail(src io.Reader, n int, dst *[]string) {
|
||||
scanner := bufio.NewScanner(src)
|
||||
r := ring.New(n)
|
||||
for scanner.Scan() {
|
||||
if n == 0 {
|
||||
continue
|
||||
}
|
||||
r.Value = scanner.Text()
|
||||
r = r.Next()
|
||||
}
|
||||
r.Do(func(v interface{}) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
*dst = append(*dst, v.(string))
|
||||
})
|
||||
}
|
||||
|
||||
// AddEnv starts a new goroutine which will decode all subsequent data
|
||||
// as a stream of json-encoded objects, and point `dst` to the last
|
||||
// decoded object.
|
||||
|
||||
+14
-96
@@ -10,53 +10,6 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOutputAddString(t *testing.T) {
|
||||
var testInputs = [][2]string{
|
||||
{
|
||||
"hello, world!",
|
||||
"hello, world!",
|
||||
},
|
||||
|
||||
{
|
||||
"One\nTwo\nThree",
|
||||
"Three",
|
||||
},
|
||||
|
||||
{
|
||||
"",
|
||||
"",
|
||||
},
|
||||
|
||||
{
|
||||
"A line\nThen another nl-terminated line\n",
|
||||
"Then another nl-terminated line",
|
||||
},
|
||||
|
||||
{
|
||||
"A line followed by an empty line\n\n",
|
||||
"",
|
||||
},
|
||||
}
|
||||
for _, testData := range testInputs {
|
||||
input := testData[0]
|
||||
expectedOutput := testData[1]
|
||||
o := NewOutput()
|
||||
var output string
|
||||
if err := o.AddString(&output); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if n, err := o.Write([]byte(input)); err != nil {
|
||||
t.Error(err)
|
||||
} else if n != len(input) {
|
||||
t.Errorf("Expected %d, got %d", len(input), n)
|
||||
}
|
||||
o.Close()
|
||||
if output != expectedOutput {
|
||||
t.Errorf("Last line is not stored as return string.\nInput: '%s'\nExpected: '%s'\nGot: '%s'", input, expectedOutput, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type sentinelWriteCloser struct {
|
||||
calledWrite bool
|
||||
calledClose bool
|
||||
@@ -145,59 +98,24 @@ func TestOutputAddPipe(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTail(t *testing.T) {
|
||||
var tests = make(map[string][][]string)
|
||||
tests["hello, world!"] = [][]string{
|
||||
{},
|
||||
{"hello, world!"},
|
||||
{"hello, world!"},
|
||||
{"hello, world!"},
|
||||
var tests = make(map[string][]string)
|
||||
tests["hello, world!"] = []string{
|
||||
"",
|
||||
"hello, world!",
|
||||
"hello, world!",
|
||||
"hello, world!",
|
||||
}
|
||||
tests["One\nTwo\nThree"] = [][]string{
|
||||
{},
|
||||
{"Three"},
|
||||
{"Two", "Three"},
|
||||
{"One", "Two", "Three"},
|
||||
tests["One\nTwo\nThree"] = []string{
|
||||
"",
|
||||
"Three",
|
||||
"Two\nThree",
|
||||
"One\nTwo\nThree",
|
||||
}
|
||||
for input, outputs := range tests {
|
||||
for n, expectedOutput := range outputs {
|
||||
var output []string
|
||||
Tail(strings.NewReader(input), n, &output)
|
||||
if fmt.Sprintf("%v", output) != fmt.Sprintf("%v", expectedOutput) {
|
||||
t.Errorf("Tail n=%d returned wrong result.\nExpected: '%s'\nGot : '%s'", expectedOutput, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputAddTail(t *testing.T) {
|
||||
var tests = make(map[string][][]string)
|
||||
tests["hello, world!"] = [][]string{
|
||||
{},
|
||||
{"hello, world!"},
|
||||
{"hello, world!"},
|
||||
{"hello, world!"},
|
||||
}
|
||||
tests["One\nTwo\nThree"] = [][]string{
|
||||
{},
|
||||
{"Three"},
|
||||
{"Two", "Three"},
|
||||
{"One", "Two", "Three"},
|
||||
}
|
||||
for input, outputs := range tests {
|
||||
for n, expectedOutput := range outputs {
|
||||
o := NewOutput()
|
||||
var output []string
|
||||
if err := o.AddTail(&output, n); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if n, err := o.Write([]byte(input)); err != nil {
|
||||
t.Error(err)
|
||||
} else if n != len(input) {
|
||||
t.Errorf("Expected %d, got %d", len(input), n)
|
||||
}
|
||||
o.Close()
|
||||
if fmt.Sprintf("%v", output) != fmt.Sprintf("%v", expectedOutput) {
|
||||
t.Errorf("Tail(%d) returned wrong result.\nExpected: %v\nGot: %v", n, expectedOutput, output)
|
||||
output := Tail(bytes.NewBufferString(input), n)
|
||||
if output != expectedOutput {
|
||||
t.Errorf("Tail n=%d returned wrong result.\nExpected: '%s'\nGot : '%s'", n, expectedOutput, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user