2018-02-27 06:14:07 -05:00
|
|
|
package container
|
|
|
|
|
|
|
|
import (
|
2023-09-09 18:27:44 -04:00
|
|
|
"context"
|
2022-06-27 11:16:44 -04:00
|
|
|
"errors"
|
2022-02-25 07:05:59 -05:00
|
|
|
"io"
|
2024-04-08 04:11:09 -04:00
|
|
|
"net"
|
|
|
|
"os/signal"
|
|
|
|
"syscall"
|
2018-02-27 06:14:07 -05:00
|
|
|
"testing"
|
2024-04-08 04:11:09 -04:00
|
|
|
"time"
|
2018-02-27 06:14:07 -05:00
|
|
|
|
2024-04-08 04:11:09 -04:00
|
|
|
"github.com/creack/pty"
|
2022-06-27 11:16:44 -04:00
|
|
|
"github.com/docker/cli/cli"
|
2024-04-08 04:11:09 -04:00
|
|
|
"github.com/docker/cli/cli/streams"
|
2018-02-27 06:14:07 -05:00
|
|
|
"github.com/docker/cli/internal/test"
|
2018-03-06 05:15:18 -05:00
|
|
|
"github.com/docker/cli/internal/test/notary"
|
2024-04-08 04:11:09 -04:00
|
|
|
"github.com/docker/docker/api/types"
|
2018-02-27 06:14:07 -05:00
|
|
|
"github.com/docker/docker/api/types/container"
|
|
|
|
"github.com/docker/docker/api/types/network"
|
2020-05-27 14:32:22 -04:00
|
|
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
2022-06-27 11:16:44 -04:00
|
|
|
"github.com/spf13/pflag"
|
2020-02-22 12:12:14 -05:00
|
|
|
"gotest.tools/v3/assert"
|
|
|
|
is "gotest.tools/v3/assert/cmp"
|
2018-02-27 06:14:07 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestRunLabel(t *testing.T) {
|
2023-11-20 11:38:50 -05:00
|
|
|
fakeCLI := test.NewFakeCli(&fakeClient{
|
2022-04-29 13:26:50 -04:00
|
|
|
createContainerFunc: func(_ *container.Config, _ *container.HostConfig, _ *network.NetworkingConfig, _ *specs.Platform, _ string) (container.CreateResponse, error) {
|
|
|
|
return container.CreateResponse{
|
2018-02-27 06:14:07 -05:00
|
|
|
ID: "id",
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
Version: "1.36",
|
|
|
|
})
|
2023-11-20 11:38:50 -05:00
|
|
|
cmd := NewRunCommand(fakeCLI)
|
2018-12-13 07:22:38 -05:00
|
|
|
cmd.SetArgs([]string{"--detach=true", "--label", "foo", "busybox"})
|
2018-03-06 15:13:00 -05:00
|
|
|
assert.NilError(t, cmd.Execute())
|
2018-02-27 06:14:07 -05:00
|
|
|
}
|
2018-03-06 05:15:18 -05:00
|
|
|
|
2024-04-08 04:11:09 -04:00
|
|
|
func TestRunAttachTermination(t *testing.T) {
|
|
|
|
p, tty, err := pty.Open()
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
_ = tty.Close()
|
|
|
|
_ = p.Close()
|
|
|
|
}()
|
|
|
|
|
|
|
|
killCh := make(chan struct{})
|
|
|
|
attachCh := make(chan struct{})
|
|
|
|
fakeCLI := test.NewFakeCli(&fakeClient{
|
|
|
|
createContainerFunc: func(_ *container.Config, _ *container.HostConfig, _ *network.NetworkingConfig, _ *specs.Platform, _ string) (container.CreateResponse, error) {
|
|
|
|
return container.CreateResponse{
|
|
|
|
ID: "id",
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
containerKillFunc: func(ctx context.Context, containerID, signal string) error {
|
|
|
|
killCh <- struct{}{}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
containerAttachFunc: func(ctx context.Context, containerID string, options container.AttachOptions) (types.HijackedResponse, error) {
|
|
|
|
server, client := net.Pipe()
|
|
|
|
t.Cleanup(func() {
|
|
|
|
_ = server.Close()
|
|
|
|
})
|
|
|
|
attachCh <- struct{}{}
|
|
|
|
return types.NewHijackedResponse(client, types.MediaTypeRawStream), nil
|
|
|
|
},
|
|
|
|
Version: "1.36",
|
|
|
|
}, func(fc *test.FakeCli) {
|
|
|
|
fc.SetOut(streams.NewOut(tty))
|
|
|
|
fc.SetIn(streams.NewIn(tty))
|
|
|
|
})
|
|
|
|
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
assert.Equal(t, fakeCLI.In().IsTerminal(), true)
|
|
|
|
assert.Equal(t, fakeCLI.Out().IsTerminal(), true)
|
|
|
|
|
|
|
|
cmd := NewRunCommand(fakeCLI)
|
|
|
|
cmd.SetArgs([]string{"-it", "busybox"})
|
|
|
|
cmd.SilenceUsage = true
|
|
|
|
go func() {
|
|
|
|
assert.ErrorIs(t, cmd.ExecuteContext(ctx), context.Canceled)
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-time.After(5 * time.Second):
|
|
|
|
t.Fatal("containerAttachFunc was not called before the 5 second timeout")
|
|
|
|
case <-attachCh:
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.NilError(t, syscall.Kill(syscall.Getpid(), syscall.SIGTERM))
|
|
|
|
select {
|
|
|
|
case <-time.After(5 * time.Second):
|
|
|
|
cancel()
|
|
|
|
t.Fatal("containerKillFunc was not called before the 5 second timeout")
|
|
|
|
case <-killCh:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-06 05:15:18 -05:00
|
|
|
func TestRunCommandWithContentTrustErrors(t *testing.T) {
|
|
|
|
testCases := []struct {
|
|
|
|
name string
|
|
|
|
args []string
|
|
|
|
expectedError string
|
|
|
|
notaryFunc test.NotaryClientFuncType
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "offline-notary-server",
|
|
|
|
notaryFunc: notary.GetOfflineNotaryRepository,
|
|
|
|
expectedError: "client is offline",
|
|
|
|
args: []string{"image:tag"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "uninitialized-notary-server",
|
|
|
|
notaryFunc: notary.GetUninitializedNotaryRepository,
|
|
|
|
expectedError: "remote trust data does not exist",
|
|
|
|
args: []string{"image:tag"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "empty-notary-server",
|
|
|
|
notaryFunc: notary.GetEmptyTargetsNotaryRepository,
|
|
|
|
expectedError: "No valid trust data for tag",
|
|
|
|
args: []string{"image:tag"},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tc := range testCases {
|
2023-11-20 11:38:50 -05:00
|
|
|
fakeCLI := test.NewFakeCli(&fakeClient{
|
2018-03-06 05:15:18 -05:00
|
|
|
createContainerFunc: func(config *container.Config,
|
|
|
|
hostConfig *container.HostConfig,
|
|
|
|
networkingConfig *network.NetworkingConfig,
|
2020-05-27 14:32:22 -04:00
|
|
|
platform *specs.Platform,
|
2018-03-06 05:15:18 -05:00
|
|
|
containerName string,
|
2022-04-29 13:26:50 -04:00
|
|
|
) (container.CreateResponse, error) {
|
linting: fmt.Errorf can be replaced with errors.New (perfsprint)
internal/test/cli.go:175:14: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("no notary client available unless defined")
^
cli/command/cli.go:318:29: fmt.Errorf can be replaced with errors.New (perfsprint)
return docker.Endpoint{}, fmt.Errorf("no context store initialized")
^
cli/command/container/attach.go:161:11: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf(result.Error.Message)
^
cli/command/container/opts.go:577:16: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("--health-start-period cannot be negative")
^
cli/command/container/opts.go:580:16: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("--health-start-interval cannot be negative")
^
cli/command/container/stats.go:221:11: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("filtering is not supported when specifying a list of containers")
^
cli/command/container/attach_test.go:82:17: fmt.Errorf can be replaced with errors.New (perfsprint)
expectedErr = fmt.Errorf("unexpected error")
^
cli/command/container/create_test.go:234:40: fmt.Errorf can be replaced with errors.New (perfsprint)
return container.CreateResponse{}, fmt.Errorf("shouldn't try to pull image")
^
cli/command/container/list_test.go:150:17: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("error listing containers")
^
cli/command/container/rm_test.go:40:31: fmt.Errorf can be replaced with errors.New (perfsprint)
return errdefs.NotFound(fmt.Errorf("Error: no such container: " + container))
^
cli/command/container/run_test.go:138:40: fmt.Errorf can be replaced with errors.New (perfsprint)
return container.CreateResponse{}, fmt.Errorf("shouldn't try to pull image")
^
cli/command/image/pull_test.go:115:49: fmt.Errorf can be replaced with errors.New (perfsprint)
return io.NopCloser(strings.NewReader("")), fmt.Errorf("shouldn't try to pull image")
^
cli/command/network/connect.go:88:16: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("invalid key/value pair format in driver options")
^
cli/command/plugin/create_test.go:96:11: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("Error creating plugin")
^
cli/command/plugin/disable_test.go:32:12: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("Error disabling plugin")
^
cli/command/plugin/enable_test.go:32:12: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("failed to enable plugin")
^
cli/command/plugin/inspect_test.go:55:22: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, nil, fmt.Errorf("error inspecting plugin")
^
cli/command/plugin/install_test.go:43:17: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("Error installing plugin")
^
cli/command/plugin/install_test.go:51:17: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("(image) when fetching")
^
cli/command/plugin/install_test.go:95:17: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("should not try to install plugin")
^
cli/command/plugin/list_test.go:35:41: fmt.Errorf can be replaced with errors.New (perfsprint)
return types.PluginsListResponse{}, fmt.Errorf("error listing plugins")
^
cli/command/plugin/remove_test.go:27:12: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("Error removing plugin")
^
cli/command/registry/login_test.go:36:46: fmt.Errorf can be replaced with errors.New (perfsprint)
return registrytypes.AuthenticateOKBody{}, fmt.Errorf("Invalid Username or Password")
^
cli/command/registry/login_test.go:44:46: fmt.Errorf can be replaced with errors.New (perfsprint)
return registrytypes.AuthenticateOKBody{}, fmt.Errorf(errUnknownUser)
^
cli/command/system/info.go:190:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("errors pretty printing info")
^
cli/command/system/prune.go:77:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf(`ERROR: The "until" filter is not supported with "--volumes"`)
^
cli/command/system/version_test.go:19:28: fmt.Errorf can be replaced with errors.New (perfsprint)
return types.Version{}, fmt.Errorf("no server")
^
cli/command/trust/key_load.go:112:22: fmt.Errorf can be replaced with errors.New (perfsprint)
return []byte{}, fmt.Errorf("could not decrypt key")
^
cli/command/trust/revoke.go:44:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("cannot use a digest reference for IMAGE:TAG")
^
cli/command/trust/revoke.go:105:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("no signed tags to remove")
^
cli/command/trust/signer_add.go:56:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("releases is a reserved keyword, please use a different signer name")
^
cli/command/trust/signer_add.go:60:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("path to a public key must be provided using the `--key` flag")
^
opts/config.go:71:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("source is required")
^
opts/mount.go:168:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("type is required")
^
opts/mount.go:172:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("target is required")
^
opts/network.go:90:11: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("network name/id is not specified")
^
opts/network.go:129:18: fmt.Errorf can be replaced with errors.New (perfsprint)
return "", "", fmt.Errorf("invalid key value pair format in driver options")
^
opts/opts.go:404:13: fmt.Errorf can be replaced with errors.New (perfsprint)
return 0, fmt.Errorf("value is too precise")
^
opts/opts.go:412:18: fmt.Errorf can be replaced with errors.New (perfsprint)
return "", "", fmt.Errorf("empty string specified for links")
^
opts/parse.go:84:37: fmt.Errorf can be replaced with errors.New (perfsprint)
return container.RestartPolicy{}, fmt.Errorf("invalid restart policy format: no policy provided before colon")
^
opts/parse.go:89:38: fmt.Errorf can be replaced with errors.New (perfsprint)
return container.RestartPolicy{}, fmt.Errorf("invalid restart policy format: maximum retry count must be an integer")
^
opts/port.go:105:13: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("hostip is not supported")
^
opts/secret.go:70:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("source is required")
^
opts/env_test.go:57:11: fmt.Errorf can be replaced with errors.New (perfsprint)
err: fmt.Errorf("invalid environment variable: =a"),
^
opts/env_test.go:93:11: fmt.Errorf can be replaced with errors.New (perfsprint)
err: fmt.Errorf("invalid environment variable: ="),
^
cli-plugins/manager/error_test.go:16:11: fmt.Errorf can be replaced with errors.New (perfsprint)
inner := fmt.Errorf("testing")
^
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-06-10 14:47:07 -04:00
|
|
|
return container.CreateResponse{}, errors.New("shouldn't try to pull image")
|
2018-03-06 05:15:18 -05:00
|
|
|
},
|
|
|
|
}, test.EnableContentTrust)
|
2023-11-20 11:38:50 -05:00
|
|
|
fakeCLI.SetNotaryClient(tc.notaryFunc)
|
|
|
|
cmd := NewRunCommand(fakeCLI)
|
2018-03-06 05:15:18 -05:00
|
|
|
cmd.SetArgs(tc.args)
|
2022-02-25 07:05:59 -05:00
|
|
|
cmd.SetOut(io.Discard)
|
2018-03-06 05:15:18 -05:00
|
|
|
err := cmd.Execute()
|
|
|
|
assert.Assert(t, err != nil)
|
2023-11-20 11:38:50 -05:00
|
|
|
assert.Assert(t, is.Contains(fakeCLI.ErrBuffer().String(), tc.expectedError))
|
2018-03-06 05:15:18 -05:00
|
|
|
}
|
|
|
|
}
|
2022-06-27 11:16:44 -04:00
|
|
|
|
|
|
|
func TestRunContainerImagePullPolicyInvalid(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
PullPolicy string
|
|
|
|
ExpectedErrMsg string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
PullPolicy: "busybox:latest",
|
|
|
|
ExpectedErrMsg: `invalid pull option: 'busybox:latest': must be one of "always", "missing" or "never"`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
PullPolicy: "--network=foo",
|
|
|
|
ExpectedErrMsg: `invalid pull option: '--network=foo': must be one of "always", "missing" or "never"`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
|
|
tc := tc
|
|
|
|
t.Run(tc.PullPolicy, func(t *testing.T) {
|
|
|
|
dockerCli := test.NewFakeCli(&fakeClient{})
|
|
|
|
err := runRun(
|
2023-09-09 18:27:44 -04:00
|
|
|
context.TODO(),
|
2022-06-27 11:16:44 -04:00
|
|
|
dockerCli,
|
|
|
|
&pflag.FlagSet{},
|
|
|
|
&runOptions{createOptions: createOptions{pull: tc.PullPolicy}},
|
|
|
|
&containerOptions{},
|
|
|
|
)
|
|
|
|
|
|
|
|
statusErr := cli.StatusError{}
|
|
|
|
assert.Check(t, errors.As(err, &statusErr))
|
|
|
|
assert.Equal(t, statusErr.StatusCode, 125)
|
|
|
|
assert.Check(t, is.Contains(dockerCli.ErrBuffer().String(), tc.ExpectedErrMsg))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|