Selaa lähdekoodia

Merge pull request #36326 from kolyshkin/integration-exec

Add/use container.Exec() to integration
Sebastiaan van Stijn 7 vuotta sitten
vanhempi
commit
ee9abc2120
2 muutettua tiedostoa jossa 113 lisäystä ja 90 poistoa
  1. 27 90
      integration/container/update_linux_test.go
  2. 86 0
      integration/internal/container/exec.go

+ 27 - 90
integration/container/update_linux_test.go

@@ -1,21 +1,15 @@
 package container // import "github.com/docker/docker/integration/container"
 
 import (
-	"bytes"
 	"context"
-	"io/ioutil"
 	"strconv"
 	"strings"
 	"testing"
 	"time"
 
-	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
-	"github.com/docker/docker/api/types/strslice"
-	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration/internal/container"
 	"github.com/docker/docker/integration/internal/request"
-	"github.com/docker/docker/pkg/stdcopy"
 	"github.com/gotestyourself/gotestyourself/poll"
 	"github.com/gotestyourself/gotestyourself/skip"
 	"github.com/stretchr/testify/assert"
@@ -39,26 +33,37 @@ func TestUpdateMemory(t *testing.T) {
 
 	poll.WaitOn(t, containerIsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
 
+	const (
+		setMemory     int64 = 314572800
+		setMemorySwap       = 524288000
+	)
+
 	_, err := client.ContainerUpdate(ctx, cID, containertypes.UpdateConfig{
 		Resources: containertypes.Resources{
-			Memory:     314572800,
-			MemorySwap: 524288000,
+			Memory:     setMemory,
+			MemorySwap: setMemorySwap,
 		},
 	})
 	require.NoError(t, err)
 
 	inspect, err := client.ContainerInspect(ctx, cID)
 	require.NoError(t, err)
-	assert.Equal(t, inspect.HostConfig.Memory, int64(314572800))
-	assert.Equal(t, inspect.HostConfig.MemorySwap, int64(524288000))
+	assert.Equal(t, setMemory, inspect.HostConfig.Memory)
+	assert.Equal(t, setMemorySwap, inspect.HostConfig.MemorySwap)
 
-	body, err := getContainerSysFSValue(ctx, client, cID, "/sys/fs/cgroup/memory/memory.limit_in_bytes")
+	res, err := container.Exec(ctx, client, cID,
+		[]string{"cat", "/sys/fs/cgroup/memory/memory.limit_in_bytes"})
 	require.NoError(t, err)
-	assert.Equal(t, strings.TrimSpace(body), "314572800")
+	require.Empty(t, res.Stderr())
+	require.Equal(t, 0, res.ExitCode)
+	assert.Equal(t, strconv.FormatInt(setMemory, 10), strings.TrimSpace(res.Stdout()))
 
-	body, err = getContainerSysFSValue(ctx, client, cID, "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes")
+	res, err = container.Exec(ctx, client, cID,
+		[]string{"cat", "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"})
 	require.NoError(t, err)
-	assert.Equal(t, strings.TrimSpace(body), "524288000")
+	require.Empty(t, res.Stderr())
+	require.Equal(t, 0, res.ExitCode)
+	assert.Equal(t, strconv.FormatInt(setMemorySwap, 10), strings.TrimSpace(res.Stdout()))
 }
 
 func TestUpdateCPUQUota(t *testing.T) {
@@ -88,83 +93,15 @@ func TestUpdateCPUQUota(t *testing.T) {
 		}
 
 		inspect, err := client.ContainerInspect(ctx, cID)
-		if err != nil {
-			t.Fatal(err)
-		}
-
-		if inspect.HostConfig.CPUQuota != test.update {
-			t.Fatalf("quota not updated in the API, expected %d, got: %d", test.update, inspect.HostConfig.CPUQuota)
-		}
-
-		execCreate, err := client.ContainerExecCreate(ctx, cID, types.ExecConfig{
-			Cmd:          []string{"/bin/cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"},
-			AttachStdout: true,
-			AttachStderr: true,
-		})
-		if err != nil {
-			t.Fatal(err)
-		}
-
-		attach, err := client.ContainerExecAttach(ctx, execCreate.ID, types.ExecStartCheck{})
-		if err != nil {
-			t.Fatal(err)
-		}
-
-		if err := client.ContainerExecStart(ctx, execCreate.ID, types.ExecStartCheck{}); err != nil {
-			t.Fatal(err)
-		}
+		require.NoError(t, err)
+		assert.Equal(t, test.update, inspect.HostConfig.CPUQuota)
 
-		buf := bytes.NewBuffer(nil)
-		ready := make(chan error)
-
-		go func() {
-			_, err := stdcopy.StdCopy(buf, buf, attach.Reader)
-			ready <- err
-		}()
-
-		select {
-		case <-time.After(60 * time.Second):
-			t.Fatal("timeout waiting for exec to complete")
-		case err := <-ready:
-			if err != nil {
-				t.Fatal(err)
-			}
-		}
+		res, err := container.Exec(ctx, client, cID,
+			[]string{"/bin/cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"})
+		require.NoError(t, err)
+		require.Empty(t, res.Stderr())
+		require.Equal(t, 0, res.ExitCode)
 
-		actual := strings.TrimSpace(buf.String())
-		if actual != strconv.Itoa(int(test.update)) {
-			t.Fatalf("expected cgroup value %d, got: %s", test.update, actual)
-		}
+		assert.Equal(t, strconv.FormatInt(test.update, 10), strings.TrimSpace(res.Stdout()))
 	}
-
-}
-
-func getContainerSysFSValue(ctx context.Context, client client.APIClient, cID string, path string) (string, error) {
-	var b bytes.Buffer
-
-	ex, err := client.ContainerExecCreate(ctx, cID,
-		types.ExecConfig{
-			AttachStdout: true,
-			Cmd:          strslice.StrSlice([]string{"cat", path}),
-		},
-	)
-	if err != nil {
-		return "", err
-	}
-
-	resp, err := client.ContainerExecAttach(ctx, ex.ID,
-		types.ExecStartCheck{
-			Detach: false,
-			Tty:    false,
-		},
-	)
-	if err != nil {
-		return "", err
-	}
-
-	defer resp.Close()
-
-	b.Reset()
-	_, err = stdcopy.StdCopy(&b, ioutil.Discard, resp.Reader)
-	return b.String(), err
 }

+ 86 - 0
integration/internal/container/exec.go

@@ -0,0 +1,86 @@
+package container
+
+import (
+	"bytes"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/client"
+	"github.com/docker/docker/pkg/stdcopy"
+	"golang.org/x/net/context"
+)
+
+// ExecResult represents a result returned from Exec()
+type ExecResult struct {
+	ExitCode  int
+	outBuffer *bytes.Buffer
+	errBuffer *bytes.Buffer
+}
+
+// Stdout returns stdout output of a command run by Exec()
+func (res *ExecResult) Stdout() string {
+	return res.outBuffer.String()
+}
+
+// Stderr returns stderr output of a command run by Exec()
+func (res *ExecResult) Stderr() string {
+	return res.errBuffer.String()
+}
+
+// Combined returns combined stdout and stderr output of a command run by Exec()
+func (res *ExecResult) Combined() string {
+	return res.outBuffer.String() + res.errBuffer.String()
+}
+
+// Exec executes a command inside a container, returning the result
+// containing stdout, stderr, and exit code. Note:
+//  - this is a synchronous operation;
+//  - cmd stdin is closed.
+func Exec(ctx context.Context, cli client.APIClient, id string, cmd []string) (ExecResult, error) {
+	// prepare exec
+	execConfig := types.ExecConfig{
+		AttachStdout: true,
+		AttachStderr: true,
+		Cmd:          cmd,
+	}
+	cresp, err := cli.ContainerExecCreate(ctx, id, execConfig)
+	if err != nil {
+		return ExecResult{}, err
+	}
+	execID := cresp.ID
+
+	// run it, with stdout/stderr attached
+	aresp, err := cli.ContainerExecAttach(ctx, execID, types.ExecStartCheck{})
+	if err != nil {
+		return ExecResult{}, err
+	}
+	defer aresp.Close()
+
+	// read the output
+	var outBuf, errBuf bytes.Buffer
+	outputDone := make(chan error)
+
+	go func() {
+		// StdCopy demultiplexes the stream into two buffers
+		_, err = stdcopy.StdCopy(&outBuf, &errBuf, aresp.Reader)
+		outputDone <- err
+	}()
+
+	select {
+	case err := <-outputDone:
+		if err != nil {
+			return ExecResult{}, err
+		}
+		break
+
+	case <-ctx.Done():
+		return ExecResult{}, ctx.Err()
+	}
+
+	// get the exit code
+	iresp, err := cli.ContainerExecInspect(ctx, execID)
+	if err != nil {
+		return ExecResult{}, err
+	}
+
+	return ExecResult{ExitCode: iresp.ExitCode, outBuffer: &outBuf, errBuffer: &errBuf}, nil
+}