Skip to content

Commit

Permalink
feat: support for -a and --attach in run
Browse files Browse the repository at this point in the history
Signed-off-by: CodeChanning <[email protected]>
  • Loading branch information
CodeChanning committed Jul 10, 2024
1 parent 77e6f18 commit 9479326
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 7 deletions.
11 changes: 10 additions & 1 deletion cmd/nerdctl/container_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func newRunCommand() *cobra.Command {
setCreateFlags(runCommand)

runCommand.Flags().BoolP("detach", "d", false, "Run container in background and print container ID")
runCommand.Flags().StringSliceP("attach", "a", []string{}, "Attach STDIN, STDOUT, or STDERR")

return runCommand
}
Expand Down Expand Up @@ -304,6 +305,10 @@ func processCreateCommandFlagsInRun(cmd *cobra.Command) (opt types.ContainerCrea
if err != nil {
return
}
opt.Attach, err = cmd.Flags().GetStringSlice("attach")
if err != nil {
return
}
return opt, nil
}

Expand All @@ -325,6 +330,10 @@ func runAction(cmd *cobra.Command, args []string) error {
return errors.New("flags -d and --rm cannot be specified together")
}

if len(createOpt.Attach) > 0 && createOpt.Detach {
return errors.New("flags -d and -a cannot be specified together")
}

netFlags, err := loadNetworkFlags(cmd)
if err != nil {
return fmt.Errorf("failed to load networking flags: %s", err)
Expand Down Expand Up @@ -381,7 +390,7 @@ func runAction(cmd *cobra.Command, args []string) error {
}
logURI := lab[labels.LogURI]
detachC := make(chan struct{})
task, err := taskutil.NewTask(ctx, client, c, false, createOpt.Interactive, createOpt.TTY, createOpt.Detach,
task, err := taskutil.NewTask(ctx, client, c, createOpt.Attach, createOpt.Interactive, createOpt.TTY, createOpt.Detach,
con, logURI, createOpt.DetachKeys, createOpt.GOptions.Namespace, detachC)
if err != nil {
return err
Expand Down
36 changes: 36 additions & 0 deletions cmd/nerdctl/container_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -533,3 +533,39 @@ func TestRunRmTime(t *testing.T) {
t.Fatalf("expected to have completed in %v, took %v", deadline, took)
}
}

func TestRunAttachStdin(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("run attach test is not yet implemented on Windows")
}

t.Parallel()
base := testutil.NewBase(t)
containerName := testutil.Identifier(t)

const testStr = "test-run-stdio"
opts := []func(*testutil.Cmd){
testutil.WithStdin(strings.NewReader("echo " + testStr + "\nexit\n")),
}

args := []string{"run", "--rm", "-i", "-a", "stdin", "-a", "stdout", "--name", containerName, testutil.CommonImage}
if testutil.GetTarget() == testutil.Docker {
args = append(args, "cat")
}

defer base.Cmd("rm", "-f", containerName).AssertOK()
base.Cmd(args...).CmdOption(opts...).AssertOutContains(testStr)
}

func TestRunAttachStdout(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("run attach test is not yet implemented on Windows")
}

t.Parallel()
base := testutil.NewBase(t)
containerName := testutil.Identifier(t)

defer base.Cmd("rm", "-f", containerName).AssertOK()
base.Cmd("run", "-a", "stdout", "--name", containerName, testutil.CommonImage, "sh", "-euxc", "echo foo").AssertOutContains("foo")
}
3 changes: 2 additions & 1 deletion docs/command-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ Usage: `nerdctl run [OPTIONS] IMAGE [COMMAND] [ARG...]`

Basic flags:

- :whale: `-a, --attach`: Attach STDIN, STDOUT, or STDERR
- :whale: :blue_square: `-i, --interactive`: Keep STDIN open even if not attached"
- :whale: :blue_square: `-t, --tty`: Allocate a pseudo-TTY
- :warning: WIP: currently `-t` conflicts with `-d`
Expand Down Expand Up @@ -387,7 +388,7 @@ IPFS flags:
- :nerd_face: `--ipfs-address`: Multiaddr of IPFS API (default uses `$IPFS_PATH` env variable if defined or local directory `~/.ipfs`)

Unimplemented `docker run` flags:
`--attach`, `--blkio-weight-device`, `--cpu-rt-*`, `--device-*`,
`--blkio-weight-device`, `--cpu-rt-*`, `--device-*`,
`--disable-content-trust`, `--domainname`, `--expose`, `--health-*`, `--isolation`, `--no-healthcheck`,
`--link*`, `--publish-all`, `--storage-opt`,
`--userns`, `--volume-driver`
Expand Down
2 changes: 2 additions & 0 deletions pkg/api/types/container_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ type ContainerCreateOptions struct {
Detach bool
// The key sequence for detaching a container.
DetachKeys string
// Attach STDIN, STDOUT, or STDERR
Attach []string
// Restart specifies the policy to apply when a container exits
Restart string
// Rm specifies whether to remove the container automatically when it exits
Expand Down
6 changes: 5 additions & 1 deletion pkg/containerutil/containerutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,11 @@ func Start(ctx context.Context, container containerd.Container, flagA bool, clie
}
}
detachC := make(chan struct{})
task, err := taskutil.NewTask(ctx, client, container, flagA, false, flagT, true, con, logURI, detachKeys, namespace, detachC)
flagAStreams := []string{}
if flagA {
flagAStreams = []string{"STDOUT", "STDERR"}
}
task, err := taskutil.NewTask(ctx, client, container, flagAStreams, false, flagT, true, con, logURI, detachKeys, namespace, detachC)
if err != nil {
return err
}
Expand Down
56 changes: 52 additions & 4 deletions pkg/taskutil/taskutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
"net/url"
"os"
"runtime"
"slices"
"strings"
"sync"
"syscall"

Expand All @@ -37,9 +39,9 @@ import (
"golang.org/x/term"
)

// NewTask is from https://github.com/containerd/containerd/blob/v1.4.3/cmd/ctr/commands/tasks/tasks_unix.go#L70-L108
func NewTask(ctx context.Context, client *containerd.Client, container containerd.Container,
flagA, flagI, flagT, flagD bool, con console.Console, logURI, detachKeys, namespace string, detachC chan<- struct{}) (containerd.Task, error) {
flagA []string, flagI, flagT, flagD bool, con console.Console, logURI, detachKeys, namespace string, detachC chan<- struct{}) (containerd.Task, error) {

var t containerd.Task
closer := func() {
if detachC != nil {
Expand All @@ -59,7 +61,7 @@ func NewTask(ctx context.Context, client *containerd.Client, container container
io.Cancel()
}
var ioCreator cio.Creator
if flagA {
if len(flagA) != 0 {
log.G(ctx).Debug("attaching output instead of using the log-uri")
if flagT {
in, err := consoleutil.NewDetachableStdin(con, detachKeys, closer)
Expand All @@ -68,7 +70,8 @@ func NewTask(ctx context.Context, client *containerd.Client, container container
}
ioCreator = cio.NewCreator(cio.WithStreams(in, con, nil), cio.WithTerminal)
} else {
ioCreator = cio.NewCreator(cio.WithStdio)
streams := flagAStreams(flagA)
ioCreator = cio.NewCreator(cio.WithStreams(streams.stdIn, streams.stdOut, streams.stdErr))
}

} else if flagT && flagD {
Expand Down Expand Up @@ -146,6 +149,51 @@ func NewTask(ctx context.Context, client *containerd.Client, container container
return t, nil
}

// struct used to store streams specified with flagA (-a, --attach)
type streams struct {
stdIn *os.File
stdOut *os.File
stdErr *os.File
}

func nullStream() *os.File {
devNull, err := os.Open("/dev/null")
if err != nil {
return nil
}
defer devNull.Close()

return devNull
}

func flagAStreams(streamsArr []string) streams {
stdIn := os.Stdin
stdOut := os.Stdout
stdErr := os.Stderr

for i, str := range streamsArr {
streamsArr[i] = strings.ToUpper(str)
}

if !slices.Contains(streamsArr, "STDIN") {
stdIn = nullStream()
}

if !slices.Contains(streamsArr, "STDOUT") {
stdOut = nullStream()
}

if !slices.Contains(streamsArr, "STDERR") {
stdErr = nullStream()
}

return streams{
stdIn: stdIn,
stdOut: stdOut,
stdErr: stdErr,
}
}

// StdinCloser is from https://github.com/containerd/containerd/blob/v1.4.3/cmd/ctr/commands/tasks/exec.go#L181-L194
type StdinCloser struct {
mu sync.Mutex
Expand Down

0 comments on commit 9479326

Please sign in to comment.