DockerCLI/cli/command/container/run_test.go

183 lines
5.2 KiB
Go
Raw Normal View History

package container
import (
"context"
"errors"
"io"
"net"
"os/signal"
"syscall"
"testing"
"time"
"github.com/creack/pty"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/streams"
"github.com/docker/cli/internal/test"
"github.com/docker/cli/internal/test/notary"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/spf13/pflag"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestRunLabel(t *testing.T) {
fakeCLI := test.NewFakeCli(&fakeClient{
createContainerFunc: func(_ *container.Config, _ *container.HostConfig, _ *network.NetworkingConfig, _ *specs.Platform, _ string) (container.CreateResponse, error) {
return container.CreateResponse{
ID: "id",
}, nil
},
Version: "1.36",
})
cmd := NewRunCommand(fakeCLI)
cmd.SetArgs([]string{"--detach=true", "--label", "foo", "busybox"})
assert.NilError(t, cmd.Execute())
}
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:
}
}
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 {
fakeCLI := test.NewFakeCli(&fakeClient{
createContainerFunc: func(config *container.Config,
hostConfig *container.HostConfig,
networkingConfig *network.NetworkingConfig,
platform *specs.Platform,
containerName string,
) (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")
},
}, test.EnableContentTrust)
fakeCLI.SetNotaryClient(tc.notaryFunc)
cmd := NewRunCommand(fakeCLI)
cmd.SetArgs(tc.args)
cmd.SetOut(io.Discard)
err := cmd.Execute()
assert.Assert(t, err != nil)
assert.Assert(t, is.Contains(fakeCLI.ErrBuffer().String(), tc.expectedError))
}
}
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(
context.TODO(),
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))
})
}
}