Skip to content

Commit 91739b7

Browse files
authored
Merge pull request #3896 from Shubhranshu153/fix-logs-on-attach
fix: logs updated on starting of a stopped container
2 parents 4af3a5d + 2c91198 commit 91739b7

File tree

10 files changed

+109
-25
lines changed

10 files changed

+109
-25
lines changed

cmd/nerdctl/compose/compose_start.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ func startContainers(ctx context.Context, client *containerd.Client, containers
112112
}
113113

114114
// in compose, always disable attach
115-
if err := containerutil.Start(ctx, c, false, client, ""); err != nil {
115+
if err := containerutil.Start(ctx, c, false, false, client, ""); err != nil {
116116
return err
117117
}
118118
info, err := c.Info(ctx, containerd.WithoutRefreshedMetadata)

cmd/nerdctl/container/container_logs_test.go

+73
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ package container
1919
import (
2020
"errors"
2121
"fmt"
22+
"io"
2223
"os/exec"
2324
"runtime"
25+
"strconv"
2426
"strings"
2527
"testing"
2628
"time"
@@ -391,3 +393,74 @@ func TestLogsWithDetails(t *testing.T) {
391393

392394
testCase.Run(t)
393395
}
396+
397+
func TestLogsWithStartContainer(t *testing.T) {
398+
testCase := nerdtest.Setup()
399+
400+
// For windows we havent added support for dual logging so not adding the test.
401+
testCase.Require = require.Not(require.Windows)
402+
403+
testCase.SubTests = []*test.Case{
404+
{
405+
Description: "Test logs are directed correctly for container start of a interactive container",
406+
Setup: func(data test.Data, helpers test.Helpers) {
407+
cmd := helpers.Command("run", "-it", "--name", data.Identifier(), testutil.CommonImage)
408+
cmd.WithPseudoTTY()
409+
cmd.WithFeeder(func() io.Reader {
410+
return strings.NewReader("echo foo\nexit\n")
411+
})
412+
413+
cmd.Run(&test.Expected{
414+
ExitCode: 0,
415+
})
416+
417+
},
418+
Cleanup: func(data test.Data, helpers test.Helpers) {
419+
helpers.Anyhow("rm", "-f", data.Identifier())
420+
},
421+
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
422+
cmd := helpers.Command("start", "-ia", data.Identifier())
423+
cmd.WithPseudoTTY()
424+
cmd.WithFeeder(func() io.Reader {
425+
return strings.NewReader("echo bar\nexit\n")
426+
})
427+
cmd.Run(&test.Expected{
428+
ExitCode: 0,
429+
})
430+
cmd = helpers.Command("logs", data.Identifier())
431+
432+
return cmd
433+
},
434+
Expected: test.Expects(0, nil, expect.Contains("foo", "bar")),
435+
},
436+
{
437+
Description: "Test logs are captured after stopping and starting a non-interactive container and continue capturing new logs",
438+
Setup: func(data test.Data, helpers test.Helpers) {
439+
helpers.Ensure("run", "-d", "--name", data.Identifier(), testutil.CommonImage, "sh", "-c", "while true; do echo foo; sleep 1; done")
440+
},
441+
Cleanup: func(data test.Data, helpers test.Helpers) {
442+
helpers.Anyhow("rm", "-f", data.Identifier())
443+
},
444+
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
445+
helpers.Ensure("stop", data.Identifier())
446+
initialLogs := helpers.Capture("logs", data.Identifier())
447+
initialFooCount := strings.Count(initialLogs, "foo")
448+
data.Labels().Set("initialFooCount", strconv.Itoa(initialFooCount))
449+
helpers.Ensure("start", data.Identifier())
450+
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
451+
return helpers.Command("logs", data.Identifier())
452+
},
453+
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
454+
return &test.Expected{
455+
ExitCode: 0,
456+
Output: func(stdout string, info string, t *testing.T) {
457+
finalLogsCount := strings.Count(stdout, "foo")
458+
initialFooCount, _ := strconv.Atoi(data.Labels().Get("initialFooCount"))
459+
assert.Assert(t, finalLogsCount > initialFooCount, "Expected 'foo' count to increase after restart", info)
460+
},
461+
}
462+
},
463+
},
464+
}
465+
testCase.Run(t)
466+
}

cmd/nerdctl/container/container_start.go

+10-5
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func StartCommand() *cobra.Command {
4343
cmd.Flags().SetInterspersed(false)
4444
cmd.Flags().BoolP("attach", "a", false, "Attach STDOUT/STDERR and forward signals")
4545
cmd.Flags().String("detach-keys", consoleutil.DefaultDetachKeys, "Override the default detach keys")
46-
46+
cmd.Flags().BoolP("interactive", "i", false, "Attach container's STDIN")
4747
return cmd
4848
}
4949

@@ -60,11 +60,16 @@ func startOptions(cmd *cobra.Command) (types.ContainerStartOptions, error) {
6060
if err != nil {
6161
return types.ContainerStartOptions{}, err
6262
}
63+
interactive, err := cmd.Flags().GetBool("interactive")
64+
if err != nil {
65+
return types.ContainerStartOptions{}, err
66+
}
6367
return types.ContainerStartOptions{
64-
Stdout: cmd.OutOrStdout(),
65-
GOptions: globalOptions,
66-
Attach: attach,
67-
DetachKeys: detachKeys,
68+
Stdout: cmd.OutOrStdout(),
69+
GOptions: globalOptions,
70+
Attach: attach,
71+
DetachKeys: detachKeys,
72+
Interactive: interactive,
6873
}, nil
6974
}
7075

cmd/nerdctl/container/container_start_linux_test.go

+1-7
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,7 @@ func TestStartDetachKeys(t *testing.T) {
5252
}
5353

5454
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
55-
flags := "-a"
56-
// Started container must be interactive - which is apparently the default for nerdctl, which does not support
57-
// the -i flag, while docker requires it explicitly
58-
if nerdtest.IsDocker() {
59-
flags += "i"
60-
}
61-
cmd := helpers.Command("start", flags, "--detach-keys=ctrl-a,ctrl-b", data.Identifier())
55+
cmd := helpers.Command("start", "-ai", "--detach-keys=ctrl-a,ctrl-b", data.Identifier())
6256
cmd.WithPseudoTTY()
6357
cmd.WithFeeder(func() io.Reader {
6458
// ctrl+a and ctrl+b (see https://en.wikipedia.org/wiki/C0_and_C1_control_codes)

pkg/api/types/container_types.go

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ type ContainerStartOptions struct {
3030
Attach bool
3131
// The key sequence for detaching a container.
3232
DetachKeys string
33+
// Attach stdin
34+
Interactive bool
3335
}
3436

3537
// ContainerKillOptions specifies options for `nerdctl (container) kill`.

pkg/cmd/container/restart.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func Restart(ctx context.Context, client *containerd.Client, containers []string
3838
if err := containerutil.Stop(ctx, found.Container, options.Timeout, options.Signal); err != nil {
3939
return err
4040
}
41-
if err := containerutil.Start(ctx, found.Container, false, client, ""); err != nil {
41+
if err := containerutil.Start(ctx, found.Container, false, false, client, ""); err != nil {
4242
return err
4343
}
4444
_, err := fmt.Fprintln(options.Stdout, found.Req)

pkg/cmd/container/start.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func Start(ctx context.Context, client *containerd.Client, reqs []string, option
4040
if found.MatchCount > 1 {
4141
return fmt.Errorf("multiple IDs found with provided prefix: %s", found.Req)
4242
}
43-
if err := containerutil.Start(ctx, found.Container, options.Attach, client, options.DetachKeys); err != nil {
43+
if err := containerutil.Start(ctx, found.Container, options.Attach, options.Interactive, client, options.DetachKeys); err != nil {
4444
return err
4545
}
4646
if !options.Attach {

pkg/containerutil/containerutil.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ func GenerateSharingPIDOpts(ctx context.Context, targetCon containerd.Container)
213213
}
214214

215215
// Start starts `container` with `attach` flag. If `attach` is true, it will attach to the container's stdio.
216-
func Start(ctx context.Context, container containerd.Container, flagA bool, client *containerd.Client, detachKeys string) (err error) {
216+
func Start(ctx context.Context, container containerd.Container, flagA bool, flagI bool, client *containerd.Client, detachKeys string) (err error) {
217217
// defer the storage of start error in the dedicated label
218218
defer func() {
219219
if err != nil {
@@ -243,7 +243,7 @@ func Start(ctx context.Context, container containerd.Container, flagA bool, clie
243243
}
244244
flagT := process.Process.Terminal
245245
var con console.Console
246-
if flagA && flagT {
246+
if (flagI || flagA) && flagT {
247247
con, err = consoleutil.Current()
248248
if err != nil {
249249
return err
@@ -284,7 +284,7 @@ func Start(ctx context.Context, container containerd.Container, flagA bool, clie
284284
// source: https://github.com/containerd/nerdctl/blob/main/docs/command-reference.md#whale-nerdctl-start
285285
attachStreamOpt = []string{"STDOUT", "STDERR"}
286286
}
287-
task, err := taskutil.NewTask(ctx, client, container, attachStreamOpt, false, flagT, true, con, logURI, detachKeys, namespace, detachC)
287+
task, err := taskutil.NewTask(ctx, client, container, attachStreamOpt, flagI, flagT, true, con, logURI, detachKeys, namespace, detachC)
288288
if err != nil {
289289
return err
290290
}

pkg/errutil/exit_coder.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
package errutil
1818

19-
import "os"
19+
import (
20+
"os"
21+
)
2022

2123
type ExitCoder interface {
2224
error
@@ -46,7 +48,6 @@ func HandleExitCoder(err error) {
4648
if err == nil {
4749
return
4850
}
49-
5051
if exitErr, ok := err.(ExitCoder); ok {
5152
os.Exit(exitErr.ExitCode())
5253
}

pkg/taskutil/taskutil.go

+14-5
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,24 @@ func NewTask(ctx context.Context, client *containerd.Client, container container
6666
var ioCreator cio.Creator
6767
if len(attachStreamOpt) != 0 {
6868
log.G(ctx).Debug("attaching output instead of using the log-uri")
69+
// when attaching a TTY we use writee for stdio and binary for log persistence
6970
if flagT {
70-
in, err := consoleutil.NewDetachableStdin(con, detachKeys, closer)
71-
if err != nil {
72-
return nil, err
71+
var in io.Reader
72+
if flagI {
73+
// FIXME: check IsTerminal on Windows too
74+
if runtime.GOOS != "windows" && !term.IsTerminal(0) {
75+
return nil, errors.New("the input device is not a TTY")
76+
}
77+
var err error
78+
in, err = consoleutil.NewDetachableStdin(con, detachKeys, closer)
79+
if err != nil {
80+
return nil, err
81+
}
7382
}
74-
ioCreator = cio.NewCreator(cio.WithStreams(in, con, nil), cio.WithTerminal)
83+
ioCreator = cioutil.NewContainerIO(namespace, logURI, true, in, con, nil)
7584
} else {
7685
streams := processAttachStreamsOpt(attachStreamOpt)
77-
ioCreator = cio.NewCreator(cio.WithStreams(streams.stdIn, streams.stdOut, streams.stdErr))
86+
ioCreator = cioutil.NewContainerIO(namespace, logURI, false, streams.stdIn, streams.stdOut, streams.stdErr)
7887
}
7988

8089
} else if flagT && flagD {

0 commit comments

Comments
 (0)