mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #31683 from AkihiroSuda/it-on-swarm-easy-analysis
integration-cli-on-swarm: new CLI flag: -keep-executor
This commit is contained in:
commit
917cb0d9d0
5 changed files with 85 additions and 73 deletions
|
@ -64,3 +64,4 @@ Flags for debugging IT on Swarm itself:
|
||||||
- `-rand-seed N`: the random seed. This flag is useful for deterministic replaying. By default(0), the timestamp is used.
|
- `-rand-seed N`: the random seed. This flag is useful for deterministic replaying. By default(0), the timestamp is used.
|
||||||
- `-filters-file FILE`: the file contains `-check.f` strings. By default, the file is automatically generated.
|
- `-filters-file FILE`: the file contains `-check.f` strings. By default, the file is automatically generated.
|
||||||
- `-dry-run`: skip the actual workload
|
- `-dry-run`: skip the actual workload
|
||||||
|
- `keep-executor`: do not auto-remove executor containers, which is used for running privileged programs on Swarm
|
||||||
|
|
|
@ -19,69 +19,76 @@ import (
|
||||||
// image needs to be the worker image itself. testFlags are OR-set of regexp for filtering tests.
|
// image needs to be the worker image itself. testFlags are OR-set of regexp for filtering tests.
|
||||||
type testChunkExecutor func(image string, tests []string) (int64, string, error)
|
type testChunkExecutor func(image string, tests []string) (int64, string, error)
|
||||||
|
|
||||||
func dryTestChunkExecutor(image string, tests []string) (int64, string, error) {
|
func dryTestChunkExecutor() testChunkExecutor {
|
||||||
return 0, fmt.Sprintf("DRY RUN (image=%q, tests=%v)", image, tests), nil
|
return func(image string, tests []string) (int64, string, error) {
|
||||||
|
return 0, fmt.Sprintf("DRY RUN (image=%q, tests=%v)", image, tests), nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// privilegedTestChunkExecutor invokes a privileged container from the worker
|
// privilegedTestChunkExecutor invokes a privileged container from the worker
|
||||||
// service via bind-mounted API socket so as to execute the test chunk
|
// service via bind-mounted API socket so as to execute the test chunk
|
||||||
func privilegedTestChunkExecutor(image string, tests []string) (int64, string, error) {
|
func privilegedTestChunkExecutor(autoRemove bool) testChunkExecutor {
|
||||||
cli, err := client.NewEnvClient()
|
return func(image string, tests []string) (int64, string, error) {
|
||||||
if err != nil {
|
cli, err := client.NewEnvClient()
|
||||||
return 0, "", err
|
|
||||||
}
|
|
||||||
// propagate variables from the host (needs to be defined in the compose file)
|
|
||||||
experimental := os.Getenv("DOCKER_EXPERIMENTAL")
|
|
||||||
graphdriver := os.Getenv("DOCKER_GRAPHDRIVER")
|
|
||||||
if graphdriver == "" {
|
|
||||||
info, err := cli.Info(context.Background())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, "", err
|
return 0, "", err
|
||||||
}
|
}
|
||||||
graphdriver = info.Driver
|
// propagate variables from the host (needs to be defined in the compose file)
|
||||||
}
|
experimental := os.Getenv("DOCKER_EXPERIMENTAL")
|
||||||
// `daemon_dest` is similar to `$DEST` (e.g. `bundles/VERSION/test-integration-cli`)
|
graphdriver := os.Getenv("DOCKER_GRAPHDRIVER")
|
||||||
// but it exists outside of `bundles` so as to make `$DOCKER_GRAPHDRIVER` work.
|
if graphdriver == "" {
|
||||||
//
|
info, err := cli.Info(context.Background())
|
||||||
// Without this hack, `$DOCKER_GRAPHDRIVER` fails because of (e.g.) `overlay2 is not supported over overlayfs`
|
if err != nil {
|
||||||
//
|
return 0, "", err
|
||||||
// see integration-cli/daemon/daemon.go
|
}
|
||||||
daemonDest := "/daemon_dest"
|
graphdriver = info.Driver
|
||||||
config := container.Config{
|
}
|
||||||
Image: image,
|
// `daemon_dest` is similar to `$DEST` (e.g. `bundles/VERSION/test-integration-cli`)
|
||||||
Env: []string{
|
// but it exists outside of `bundles` so as to make `$DOCKER_GRAPHDRIVER` work.
|
||||||
"TESTFLAGS=-check.f " + strings.Join(tests, "|"),
|
//
|
||||||
"KEEPBUNDLE=1",
|
// Without this hack, `$DOCKER_GRAPHDRIVER` fails because of (e.g.) `overlay2 is not supported over overlayfs`
|
||||||
"DOCKER_INTEGRATION_TESTS_VERIFIED=1", // for avoiding rebuilding integration-cli
|
//
|
||||||
"DOCKER_EXPERIMENTAL=" + experimental,
|
// see integration-cli/daemon/daemon.go
|
||||||
"DOCKER_GRAPHDRIVER=" + graphdriver,
|
daemonDest := "/daemon_dest"
|
||||||
"DOCKER_INTEGRATION_DAEMON_DEST=" + daemonDest,
|
config := container.Config{
|
||||||
},
|
Image: image,
|
||||||
// TODO: set label?
|
Env: []string{
|
||||||
Entrypoint: []string{"hack/dind"},
|
"TESTFLAGS=-check.f " + strings.Join(tests, "|"),
|
||||||
Cmd: []string{"hack/make.sh", "test-integration-cli"},
|
"KEEPBUNDLE=1",
|
||||||
}
|
"DOCKER_INTEGRATION_TESTS_VERIFIED=1", // for avoiding rebuilding integration-cli
|
||||||
hostConfig := container.HostConfig{
|
"DOCKER_EXPERIMENTAL=" + experimental,
|
||||||
AutoRemove: true,
|
"DOCKER_GRAPHDRIVER=" + graphdriver,
|
||||||
Privileged: true,
|
"DOCKER_INTEGRATION_DAEMON_DEST=" + daemonDest,
|
||||||
Mounts: []mount.Mount{
|
|
||||||
{
|
|
||||||
Type: mount.TypeVolume,
|
|
||||||
Target: daemonDest,
|
|
||||||
},
|
},
|
||||||
},
|
Labels: map[string]string{
|
||||||
|
"org.dockerproject.integration-cli-on-swarm": "",
|
||||||
|
"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.",
|
||||||
|
},
|
||||||
|
Entrypoint: []string{"hack/dind"},
|
||||||
|
Cmd: []string{"hack/make.sh", "test-integration-cli"},
|
||||||
|
}
|
||||||
|
hostConfig := container.HostConfig{
|
||||||
|
AutoRemove: autoRemove,
|
||||||
|
Privileged: true,
|
||||||
|
Mounts: []mount.Mount{
|
||||||
|
{
|
||||||
|
Type: mount.TypeVolume,
|
||||||
|
Target: daemonDest,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
id, stream, err := runContainer(context.Background(), cli, config, hostConfig)
|
||||||
|
if err != nil {
|
||||||
|
return 0, "", err
|
||||||
|
}
|
||||||
|
var b bytes.Buffer
|
||||||
|
teeContainerStream(&b, os.Stdout, os.Stderr, stream)
|
||||||
|
rc, err := cli.ContainerWait(context.Background(), id)
|
||||||
|
if err != nil {
|
||||||
|
return 0, "", err
|
||||||
|
}
|
||||||
|
return rc, b.String(), nil
|
||||||
}
|
}
|
||||||
id, stream, err := runContainer(context.Background(), cli, config, hostConfig)
|
|
||||||
if err != nil {
|
|
||||||
return 0, "", err
|
|
||||||
}
|
|
||||||
var b bytes.Buffer
|
|
||||||
teeContainerStream(&b, os.Stdout, os.Stderr, stream)
|
|
||||||
rc, err := cli.ContainerWait(context.Background(), id)
|
|
||||||
if err != nil {
|
|
||||||
return 0, "", err
|
|
||||||
}
|
|
||||||
return rc, b.String(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runContainer(ctx context.Context, cli *client.Client, config container.Config, hostConfig container.HostConfig) (string, io.ReadCloser, error) {
|
func runContainer(ctx context.Context, cli *client.Client, config container.Config, hostConfig container.HostConfig) (string, io.ReadCloser, error) {
|
||||||
|
|
|
@ -24,6 +24,7 @@ func validImageDigest(s string) bool {
|
||||||
func xmain() error {
|
func xmain() error {
|
||||||
workerImageDigest := flag.String("worker-image-digest", "", "Needs to be the digest of this worker image itself")
|
workerImageDigest := flag.String("worker-image-digest", "", "Needs to be the digest of this worker image itself")
|
||||||
dryRun := flag.Bool("dry-run", false, "Dry run")
|
dryRun := flag.Bool("dry-run", false, "Dry run")
|
||||||
|
keepExecutor := flag.Bool("keep-executor", false, "Do not auto-remove executor containers, which is used for running privileged programs on Swarm")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if !validImageDigest(*workerImageDigest) {
|
if !validImageDigest(*workerImageDigest) {
|
||||||
// Because of issue #29582.
|
// Because of issue #29582.
|
||||||
|
@ -31,9 +32,9 @@ func xmain() error {
|
||||||
// So, `docker run localregistry.example.com/blahblah:latest` fails: `Unable to find image 'localregistry.example.com/blahblah:latest' locally`
|
// So, `docker run localregistry.example.com/blahblah:latest` fails: `Unable to find image 'localregistry.example.com/blahblah:latest' locally`
|
||||||
return fmt.Errorf("worker-image-digest must be a digest, got %q", *workerImageDigest)
|
return fmt.Errorf("worker-image-digest must be a digest, got %q", *workerImageDigest)
|
||||||
}
|
}
|
||||||
executor := privilegedTestChunkExecutor
|
executor := privilegedTestChunkExecutor(!*keepExecutor)
|
||||||
if *dryRun {
|
if *dryRun {
|
||||||
executor = dryTestChunkExecutor
|
executor = dryTestChunkExecutor()
|
||||||
}
|
}
|
||||||
return handle(*workerImageDigest, executor)
|
return handle(*workerImageDigest, executor)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ version: "3"
|
||||||
services:
|
services:
|
||||||
worker:
|
worker:
|
||||||
image: "{{.WorkerImage}}"
|
image: "{{.WorkerImage}}"
|
||||||
command: ["-worker-image-digest={{.WorkerImageDigest}}", "-dry-run={{.DryRun}}"]
|
command: ["-worker-image-digest={{.WorkerImageDigest}}", "-dry-run={{.DryRun}}", "-keep-executor={{.KeepExecutor}}"]
|
||||||
networks:
|
networks:
|
||||||
- net
|
- net
|
||||||
volumes:
|
volumes:
|
||||||
|
@ -57,14 +57,15 @@ volumes:
|
||||||
`
|
`
|
||||||
|
|
||||||
type composeOptions struct {
|
type composeOptions struct {
|
||||||
Replicas int
|
Replicas int
|
||||||
Chunks int
|
Chunks int
|
||||||
MasterImage string
|
MasterImage string
|
||||||
WorkerImage string
|
WorkerImage string
|
||||||
Volume string
|
Volume string
|
||||||
Shuffle bool
|
Shuffle bool
|
||||||
RandSeed int64
|
RandSeed int64
|
||||||
DryRun bool
|
DryRun bool
|
||||||
|
KeepExecutor bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type composeTemplateOptions struct {
|
type composeTemplateOptions struct {
|
||||||
|
|
|
@ -42,6 +42,7 @@ func xmain() (int, error) {
|
||||||
randSeed := flag.Int64("rand-seed", int64(0), "Random seed used for shuffling (0 == curent time)")
|
randSeed := flag.Int64("rand-seed", int64(0), "Random seed used for shuffling (0 == curent time)")
|
||||||
filtersFile := flag.String("filters-file", "", "Path to optional file composed of `-check.f` filter strings")
|
filtersFile := flag.String("filters-file", "", "Path to optional file composed of `-check.f` filter strings")
|
||||||
dryRun := flag.Bool("dry-run", false, "Dry run")
|
dryRun := flag.Bool("dry-run", false, "Dry run")
|
||||||
|
keepExecutor := flag.Bool("keep-executor", false, "Do not auto-remove executor containers, which is used for running privileged programs on Swarm")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if *chunks == 0 {
|
if *chunks == 0 {
|
||||||
*chunks = *replicas
|
*chunks = *replicas
|
||||||
|
@ -73,14 +74,15 @@ func xmain() (int, error) {
|
||||||
workerImageForStack = *pushWorkerImage
|
workerImageForStack = *pushWorkerImage
|
||||||
}
|
}
|
||||||
compose, err := createCompose("", cli, composeOptions{
|
compose, err := createCompose("", cli, composeOptions{
|
||||||
Replicas: *replicas,
|
Replicas: *replicas,
|
||||||
Chunks: *chunks,
|
Chunks: *chunks,
|
||||||
MasterImage: defaultMasterImageName,
|
MasterImage: defaultMasterImageName,
|
||||||
WorkerImage: workerImageForStack,
|
WorkerImage: workerImageForStack,
|
||||||
Volume: defaultVolumeName,
|
Volume: defaultVolumeName,
|
||||||
Shuffle: *shuffle,
|
Shuffle: *shuffle,
|
||||||
RandSeed: *randSeed,
|
RandSeed: *randSeed,
|
||||||
DryRun: *dryRun,
|
DryRun: *dryRun,
|
||||||
|
KeepExecutor: *keepExecutor,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 1, err
|
return 1, err
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue