github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/hack/integration-cli-on-swarm/agent/worker/executor.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"strings"
    10  
    11  	"github.com/docker/docker/api/types"
    12  	"github.com/docker/docker/api/types/container"
    13  	"github.com/docker/docker/api/types/mount"
    14  	"github.com/docker/docker/client"
    15  	"github.com/docker/docker/pkg/stdcopy"
    16  )
    17  
    18  // testChunkExecutor executes integration-cli binary.
    19  // image needs to be the worker image itself. testFlags are OR-set of regexp for filtering tests.
    20  type testChunkExecutor func(image string, tests []string) (int64, string, error)
    21  
    22  func dryTestChunkExecutor() testChunkExecutor {
    23  	return func(image string, tests []string) (int64, string, error) {
    24  		return 0, fmt.Sprintf("DRY RUN (image=%q, tests=%v)", image, tests), nil
    25  	}
    26  }
    27  
    28  // privilegedTestChunkExecutor invokes a privileged container from the worker
    29  // service via bind-mounted API socket so as to execute the test chunk
    30  func privilegedTestChunkExecutor(autoRemove bool) testChunkExecutor {
    31  	return func(image string, tests []string) (int64, string, error) {
    32  		cli, err := client.NewEnvClient()
    33  		if err != nil {
    34  			return 0, "", err
    35  		}
    36  		// propagate variables from the host (needs to be defined in the compose file)
    37  		experimental := os.Getenv("DOCKER_EXPERIMENTAL")
    38  		graphdriver := os.Getenv("DOCKER_GRAPHDRIVER")
    39  		if graphdriver == "" {
    40  			info, err := cli.Info(context.Background())
    41  			if err != nil {
    42  				return 0, "", err
    43  			}
    44  			graphdriver = info.Driver
    45  		}
    46  		// `daemon_dest` is similar to `$DEST` (e.g. `bundles/VERSION/test-integration`)
    47  		// but it exists outside of `bundles` so as to make `$DOCKER_GRAPHDRIVER` work.
    48  		//
    49  		// Without this hack, `$DOCKER_GRAPHDRIVER` fails because of (e.g.) `overlay2 is not supported over overlayfs`
    50  		//
    51  		// see integration-cli/daemon/daemon.go
    52  		daemonDest := "/daemon_dest"
    53  		config := container.Config{
    54  			Image: image,
    55  			Env: []string{
    56  				"TESTFLAGS=-check.f " + strings.Join(tests, "|"),
    57  				"KEEPBUNDLE=1",
    58  				"DOCKER_INTEGRATION_TESTS_VERIFIED=1", // for avoiding rebuilding integration-cli
    59  				"DOCKER_EXPERIMENTAL=" + experimental,
    60  				"DOCKER_GRAPHDRIVER=" + graphdriver,
    61  				"DOCKER_INTEGRATION_DAEMON_DEST=" + daemonDest,
    62  			},
    63  			Labels: map[string]string{
    64  				"org.dockerproject.integration-cli-on-swarm":         "",
    65  				"org.dockerproject.integration-cli-on-swarm.comment": "this non-service container is created for running privileged programs on Swarm. you can remove this container manually if the corresponding service is already stopped.",
    66  			},
    67  			Entrypoint: []string{"hack/dind"},
    68  			Cmd:        []string{"hack/make.sh", "test-integration"},
    69  		}
    70  		hostConfig := container.HostConfig{
    71  			AutoRemove: autoRemove,
    72  			Privileged: true,
    73  			Mounts: []mount.Mount{
    74  				{
    75  					Type:   mount.TypeVolume,
    76  					Target: daemonDest,
    77  				},
    78  			},
    79  		}
    80  		id, stream, err := runContainer(context.Background(), cli, config, hostConfig)
    81  		if err != nil {
    82  			return 0, "", err
    83  		}
    84  		var b bytes.Buffer
    85  		teeContainerStream(&b, os.Stdout, os.Stderr, stream)
    86  		resultC, errC := cli.ContainerWait(context.Background(), id, "")
    87  		select {
    88  		case err := <-errC:
    89  			return 0, "", err
    90  		case result := <-resultC:
    91  			return result.StatusCode, b.String(), nil
    92  		}
    93  	}
    94  }
    95  
    96  func runContainer(ctx context.Context, cli *client.Client, config container.Config, hostConfig container.HostConfig) (string, io.ReadCloser, error) {
    97  	created, err := cli.ContainerCreate(context.Background(),
    98  		&config, &hostConfig, nil, "")
    99  	if err != nil {
   100  		return "", nil, err
   101  	}
   102  	if err = cli.ContainerStart(ctx, created.ID, types.ContainerStartOptions{}); err != nil {
   103  		return "", nil, err
   104  	}
   105  	stream, err := cli.ContainerLogs(ctx,
   106  		created.ID,
   107  		types.ContainerLogsOptions{
   108  			ShowStdout: true,
   109  			ShowStderr: true,
   110  			Follow:     true,
   111  		})
   112  	return created.ID, stream, err
   113  }
   114  
   115  func teeContainerStream(w, stdout, stderr io.Writer, stream io.ReadCloser) {
   116  	stdcopy.StdCopy(io.MultiWriter(w, stdout), io.MultiWriter(w, stderr), stream)
   117  	stream.Close()
   118  }