Skip to content

Commit a038f16

Browse files
authored
Merge pull request #30 from coderbirju/add-namespace-option-test
Add namespace option test
2 parents 930cd84 + 2a21ae8 commit a038f16

17 files changed

+165
-38
lines changed

cmd/nerdctl/compose/compose_start.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ func startAction(cmd *cobra.Command, args []string) error {
5454
return err
5555
}
5656

57+
nerdctlCmd, nerdctlArgs := helpers.GlobalFlags(cmd)
58+
5759
client, ctx, cancel, err := clientutil.NewClient(cmd.Context(), globalOptions.Namespace, globalOptions.Address)
5860
if err != nil {
5961
return err
@@ -88,15 +90,15 @@ func startAction(cmd *cobra.Command, args []string) error {
8890
return fmt.Errorf("service %q has no container to start", svcName)
8991
}
9092

91-
if err := startContainers(ctx, client, containers, &globalOptions); err != nil {
93+
if err := startContainers(ctx, client, containers, &globalOptions, nerdctlCmd, nerdctlArgs); err != nil {
9294
return err
9395
}
9496
}
9597

9698
return nil
9799
}
98100

99-
func startContainers(ctx context.Context, client *containerd.Client, containers []containerd.Container, globalOptions *types.GlobalCommandOptions) error {
101+
func startContainers(ctx context.Context, client *containerd.Client, containers []containerd.Container, globalOptions *types.GlobalCommandOptions, nerdctlCmd string, nerdctlArgs []string) error {
100102
eg, ctx := errgroup.WithContext(ctx)
101103
for _, c := range containers {
102104
c := c
@@ -114,7 +116,7 @@ func startContainers(ctx context.Context, client *containerd.Client, containers
114116
}
115117

116118
// in compose, always disable attach
117-
if err := containerutil.Start(ctx, c, false, false, client, "", "", (*config.Config)(globalOptions)); err != nil {
119+
if err := containerutil.Start(ctx, c, false, false, client, "", "", (*config.Config)(globalOptions), nerdctlCmd, nerdctlArgs); err != nil {
118120
return err
119121
}
120122
info, err := c.Info(ctx, containerd.WithoutRefreshedMetadata)

cmd/nerdctl/container/container_health_check_linux_test.go

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"github.com/containerd/nerdctl/mod/tigron/tig"
3333

3434
"github.com/containerd/nerdctl/v2/pkg/healthcheck"
35+
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
3536
"github.com/containerd/nerdctl/v2/pkg/rootlessutil"
3637
"github.com/containerd/nerdctl/v2/pkg/testutil"
3738
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
@@ -309,7 +310,7 @@ func TestContainerHealthCheckAdvance(t *testing.T) {
309310
debug, _ := json.MarshalIndent(h, "", " ")
310311
t.Log(string(debug))
311312
assert.Assert(t, h != nil, "expected health state")
312-
assert.Equal(t, h.FailingStreak, 1)
313+
assert.Assert(t, h.FailingStreak >= 1, "expected at least one failing streak")
313314
assert.Assert(t, len(inspect.State.Health.Log) > 0, "expected health log to have entries")
314315
last := inspect.State.Health.Log[0]
315316
assert.Equal(t, -1, last.ExitCode)
@@ -348,7 +349,7 @@ func TestContainerHealthCheckAdvance(t *testing.T) {
348349
t.Log(string(debug))
349350
assert.Assert(t, h != nil, "expected health state")
350351
assert.Equal(t, h.Status, healthcheck.Unhealthy)
351-
assert.Equal(t, h.FailingStreak, 2)
352+
assert.Assert(t, h.FailingStreak >= 1, "expected atleast one FailingStreak")
352353
}),
353354
}
354355
},
@@ -411,7 +412,7 @@ func TestContainerHealthCheckAdvance(t *testing.T) {
411412
t.Log(string(debug))
412413
assert.Assert(t, h != nil, "expected health state")
413414
assert.Equal(t, h.Status, healthcheck.Unhealthy)
414-
assert.Equal(t, h.FailingStreak, 1)
415+
assert.Assert(t, h.FailingStreak >= 1, "expected at least one failing streak")
415416
}),
416417
}
417418
},
@@ -633,7 +634,7 @@ func TestContainerHealthCheckAdvance(t *testing.T) {
633634
assert.Assert(t, h != nil, "expected health state")
634635
assert.Equal(t, h.Status, healthcheck.Healthy)
635636
assert.Equal(t, h.FailingStreak, 0)
636-
assert.Assert(t, len(h.Log) == 1, "expected one log entry")
637+
assert.Assert(t, len(h.Log) >= 1, "expected at least one log entry")
637638
output := h.Log[0].Output
638639
assert.Assert(t, strings.HasSuffix(output, "[truncated]"), "expected output to be truncated with '[truncated]'")
639640
}),
@@ -931,6 +932,107 @@ func TestHealthCheck_SystemdIntegration_Basic(t *testing.T) {
931932
testCase.Run(t)
932933
}
933934

935+
func TestHealthCheck_GlobalFlags(t *testing.T) {
936+
testCase := nerdtest.Setup()
937+
testCase.Require = require.Not(nerdtest.Docker)
938+
// Skip systemd tests in rootless environment to bypass dbus permission issues
939+
if rootlessutil.IsRootless() {
940+
t.Skip("systemd healthcheck tests are skipped in rootless environment")
941+
}
942+
943+
testCase.SubTests = []*test.Case{
944+
{
945+
Description: "Healthcheck works with custom namespace flag",
946+
Setup: func(data test.Data, helpers test.Helpers) {
947+
// Create container in custom namespace with healthcheck
948+
helpers.Ensure("--namespace=healthcheck-test", "run", "-d", "--name", data.Identifier(),
949+
"--health-cmd", "echo healthy",
950+
"--health-interval", "2s",
951+
testutil.CommonImage, "sleep", "30")
952+
// Wait a bit to ensure container is running (can't use EnsureContainerStarted with custom namespace)
953+
time.Sleep(1 * time.Second)
954+
},
955+
Cleanup: func(data test.Data, helpers test.Helpers) {
956+
helpers.Anyhow("--namespace=healthcheck-test", "rm", "-f", data.Identifier())
957+
},
958+
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
959+
// Wait a bit for healthcheck to run
960+
time.Sleep(3 * time.Second)
961+
// Verify container is accessible in the custom namespace
962+
return helpers.Command("--namespace=healthcheck-test", "inspect", data.Identifier())
963+
},
964+
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
965+
return &test.Expected{
966+
ExitCode: 0,
967+
Output: func(stdout string, t tig.T) {
968+
var inspectResults []dockercompat.Container
969+
err := json.Unmarshal([]byte(stdout), &inspectResults)
970+
assert.NilError(t, err, "failed to parse inspect output")
971+
assert.Assert(t, len(inspectResults) > 0, "expected at least one container in inspect results")
972+
973+
inspect := inspectResults[0]
974+
h := inspect.State.Health
975+
assert.Assert(t, h != nil, "expected health state to be present")
976+
assert.Assert(t, h.Status == healthcheck.Healthy || h.Status == healthcheck.Starting,
977+
"expected health status to be healthy or starting, got: %s", h.Status)
978+
assert.Assert(t, len(h.Log) > 0, "expected at least one health check log entry")
979+
},
980+
}
981+
},
982+
},
983+
{
984+
Description: "Healthcheck works correctly with namespace after container restart",
985+
Setup: func(data test.Data, helpers test.Helpers) {
986+
// Create container in custom namespace
987+
helpers.Ensure("--namespace=restart-test", "run", "-d", "--name", data.Identifier(),
988+
"--health-cmd", "echo healthy",
989+
"--health-interval", "2s",
990+
testutil.CommonImage, "sleep", "60")
991+
// Wait a bit to ensure container is running (can't use EnsureContainerStarted with custom namespace)
992+
time.Sleep(1 * time.Second)
993+
},
994+
Cleanup: func(data test.Data, helpers test.Helpers) {
995+
helpers.Anyhow("--namespace=restart-test", "rm", "-f", data.Identifier())
996+
},
997+
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
998+
// Wait for initial healthcheck
999+
time.Sleep(3 * time.Second)
1000+
1001+
// Stop and restart the container
1002+
helpers.Ensure("--namespace=restart-test", "stop", data.Identifier())
1003+
helpers.Ensure("--namespace=restart-test", "start", data.Identifier())
1004+
// Wait a bit to ensure container is running after restart
1005+
time.Sleep(1 * time.Second)
1006+
1007+
// Wait for healthcheck to run after restart
1008+
time.Sleep(3 * time.Second)
1009+
1010+
return helpers.Command("--namespace=restart-test", "inspect", data.Identifier())
1011+
},
1012+
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
1013+
return &test.Expected{
1014+
ExitCode: 0,
1015+
Output: func(stdout string, t tig.T) {
1016+
// Parse the inspect JSON output directly since we're in a custom namespace
1017+
var inspectResults []dockercompat.Container
1018+
err := json.Unmarshal([]byte(stdout), &inspectResults)
1019+
assert.NilError(t, err, "failed to parse inspect output")
1020+
assert.Assert(t, len(inspectResults) > 0, "expected at least one container in inspect results")
1021+
1022+
inspect := inspectResults[0]
1023+
h := inspect.State.Health
1024+
assert.Assert(t, h != nil, "expected health state after restart")
1025+
assert.Assert(t, h.Status == healthcheck.Healthy || h.Status == healthcheck.Starting,
1026+
"expected health status to be healthy or starting after restart, got: %s", h.Status)
1027+
assert.Assert(t, len(h.Log) > 0, "expected health check logs after restart")
1028+
},
1029+
}
1030+
},
1031+
},
1032+
}
1033+
testCase.Run(t)
1034+
}
1035+
9341036
func TestHealthCheck_SystemdIntegration_Advanced(t *testing.T) {
9351037
testCase := nerdtest.Setup()
9361038
testCase.Require = require.Not(nerdtest.Docker)

cmd/nerdctl/container/container_restart.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ func restartOptions(cmd *cobra.Command) (types.ContainerRestartOptions, error) {
4848
return types.ContainerRestartOptions{}, err
4949
}
5050

51+
// Call GlobalFlags function here
52+
nerdctlCmd, nerdctlArgs := helpers.GlobalFlags(cmd)
53+
5154
var timeout *time.Duration
5255
if cmd.Flags().Changed("time") {
5356
// Seconds to wait for stop before killing it
@@ -70,10 +73,12 @@ func restartOptions(cmd *cobra.Command) (types.ContainerRestartOptions, error) {
7073
}
7174

7275
return types.ContainerRestartOptions{
73-
Stdout: cmd.OutOrStdout(),
74-
GOption: globalOptions,
75-
Timeout: timeout,
76-
Signal: signal,
76+
Stdout: cmd.OutOrStdout(),
77+
GOption: globalOptions,
78+
Timeout: timeout,
79+
Signal: signal,
80+
NerdctlCmd: nerdctlCmd,
81+
NerdctlArgs: nerdctlArgs,
7782
}, err
7883
}
7984

cmd/nerdctl/container/container_run.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ func runAction(cmd *cobra.Command, args []string) error {
457457
}
458458

459459
// Setup container healthchecks.
460-
if err := healthcheck.CreateTimer(ctx, c, (*config.Config)(&createOpt.GOptions)); err != nil {
460+
if err := healthcheck.CreateTimer(ctx, c, (*config.Config)(&createOpt.GOptions), createOpt.NerdctlCmd, createOpt.NerdctlArgs); err != nil {
461461
return fmt.Errorf("failed to create healthcheck timer: %w", err)
462462
}
463463
if err := healthcheck.StartTimer(ctx, c, (*config.Config)(&createOpt.GOptions)); err != nil {

cmd/nerdctl/container/container_start.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ func startAction(cmd *cobra.Command, args []string) error {
9191
return err
9292
}
9393

94+
options.NerdctlCmd, options.NerdctlArgs = helpers.GlobalFlags(cmd)
95+
9496
client, ctx, cancel, err := clientutil.NewClient(cmd.Context(), options.GOptions.Namespace, options.GOptions.Address)
9597
if err != nil {
9698
return err

cmd/nerdctl/container/container_unpause.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,12 @@ func unpauseOptions(cmd *cobra.Command) (types.ContainerUnpauseOptions, error) {
4646
if err != nil {
4747
return types.ContainerUnpauseOptions{}, err
4848
}
49+
nerdctlCmd, nerdctlArgs := helpers.GlobalFlags(cmd)
4950
return types.ContainerUnpauseOptions{
50-
GOptions: globalOptions,
51-
Stdout: cmd.OutOrStdout(),
51+
GOptions: globalOptions,
52+
Stdout: cmd.OutOrStdout(),
53+
NerdctlCmd: nerdctlCmd,
54+
NerdctlArgs: nerdctlArgs,
5255
}, nil
5356
}
5457

cmd/nerdctl/helpers/cobra.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,11 @@ func GlobalFlags(cmd *cobra.Command) (string, []string) {
156156
flagSet.VisitAll(func(f *pflag.Flag) {
157157
key := f.Name
158158
val := f.Value.String()
159-
if f.Changed {
159+
// Include flag if:
160+
// 1. It was explicitly changed via CLI (highest priority), OR
161+
// 2. It has a non-default value (from TOML config)
162+
// This ensures both CLI flags and TOML config values are propagated
163+
if f.Changed || (val != f.DefValue && val != "") {
160164
args = append(args, "--"+key+"="+val)
161165
}
162166
})

pkg/api/types/container_types.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ type ContainerStartOptions struct {
3636
Checkpoint string
3737
// CheckpointDir is the directory to store checkpoints
3838
CheckpointDir string
39+
// NerdctlCmd is the command name of nerdctl
40+
NerdctlCmd string
41+
// NerdctlArgs is the arguments of nerdctl
42+
NerdctlArgs []string
3943
}
4044

4145
// ContainerKillOptions specifies options for `nerdctl (container) kill`.
@@ -329,6 +333,10 @@ type ContainerRestartOptions struct {
329333
Timeout *time.Duration
330334
// Signal to send to stop the container, before sending SIGKILL
331335
Signal string
336+
// NerdctlCmd is the command name of nerdctl
337+
NerdctlCmd string
338+
// NerdctlArgs is the arguments of nerdctl
339+
NerdctlArgs []string
332340
}
333341

334342
// ContainerPauseOptions specifies options for `nerdctl (container) pause`.
@@ -346,7 +354,14 @@ type ContainerPruneOptions struct {
346354
}
347355

348356
// ContainerUnpauseOptions specifies options for `nerdctl (container) unpause`.
349-
type ContainerUnpauseOptions ContainerPauseOptions
357+
type ContainerUnpauseOptions struct {
358+
Stdout io.Writer
359+
GOptions GlobalCommandOptions
360+
// NerdctlCmd is the command name of nerdctl
361+
NerdctlCmd string
362+
// NerdctlArgs is the arguments of nerdctl
363+
NerdctlArgs []string
364+
}
350365

351366
// ContainerRemoveOptions specifies options for `nerdctl (container) rm`.
352367
type ContainerRemoveOptions struct {

pkg/cmd/container/restart.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ func Restart(ctx context.Context, client *containerd.Client, containers []string
4848
if err := containerutil.Stop(ctx, found.Container, options.Timeout, options.Signal); err != nil {
4949
return err
5050
}
51-
if err := containerutil.Start(ctx, found.Container, false, false, client, "", "", (*config.Config)(&options.GOption)); err != nil {
51+
52+
if err := containerutil.Start(ctx, found.Container, false, false, client, "", "", (*config.Config)(&options.GOption), options.NerdctlCmd, options.NerdctlArgs); err != nil {
5253
return err
5354
}
5455
_, err = fmt.Fprintln(options.Stdout, found.Req)

pkg/cmd/container/start.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func Start(ctx context.Context, client *containerd.Client, reqs []string, option
5656
return err
5757
}
5858
}
59-
if err := containerutil.Start(ctx, found.Container, options.Attach, options.Interactive, client, options.DetachKeys, checkpointDir, (*config.Config)(&options.GOptions)); err != nil {
59+
if err := containerutil.Start(ctx, found.Container, options.Attach, options.Interactive, client, options.DetachKeys, checkpointDir, (*config.Config)(&options.GOptions), options.NerdctlCmd, options.NerdctlArgs); err != nil {
6060
return err
6161
}
6262
if !options.Attach {

0 commit comments

Comments
 (0)