Skip to content

Commit 73bfafa

Browse files
committed
feat(cmd): Add '--follow' flag for start/restart
This allows for following logs on re/starting instances. This is not implemented for creation with autostart. Signed-off-by: Cezar Craciunoiu <[email protected]>
1 parent b26be2f commit 73bfafa

6 files changed

Lines changed: 120 additions & 4 deletions

File tree

cmd/unikraft/instances_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package main
88
import (
99
"regexp"
1010
"testing"
11+
"time"
1112
)
1213

1314
func instancesTests(t *testing.T, r *testRunner) {
@@ -169,6 +170,65 @@ func instancesTests(t *testing.T, r *testRunner) {
169170
})
170171
})
171172

173+
t.Run("start-follow", func(t *testing.T) {
174+
r.
175+
online().
176+
withCleaners(instanceCleaners).
177+
run(t, []command{
178+
// Create a stopped instance
179+
{args: []string{
180+
unikraftCmd, "instance", "create",
181+
"--output", "quiet",
182+
"--set", "name=test-$UNIQ_INST",
183+
"--set", "metro=" + metroName,
184+
"--set", "image=nginx:latest",
185+
"--set", "autostart=false",
186+
"--set", "resources.memory=128",
187+
"--set", "resources.vcpus=1",
188+
}},
189+
// Start it and follow logs for up to 5 seconds
190+
{
191+
args: []string{
192+
unikraftCmd, "instance", "start",
193+
"--follow",
194+
"test-$UNIQ_INST",
195+
},
196+
timeout: 5 * time.Second,
197+
},
198+
{args: []string{unikraftCmd, "instance", "delete", "test-$UNIQ_INST"}},
199+
})
200+
})
201+
202+
t.Run("restart-follow", func(t *testing.T) {
203+
r.
204+
online().
205+
withCleaners(instanceCleaners).
206+
run(t, []command{
207+
// Create and start an instance
208+
{args: []string{
209+
unikraftCmd, "instance", "create",
210+
"--output", "quiet",
211+
"--set", "name=test-$UNIQ_INST",
212+
"--set", "metro=" + metroName,
213+
"--set", "image=nginx:latest",
214+
"--set", "autostart=true",
215+
"--set", "resources.memory=128",
216+
"--set", "resources.vcpus=1",
217+
}},
218+
{args: []string{unikraftCmd, "instance", "wait", "--until", "state==running", "--timeout", "30s", "test-$UNIQ_INST"}},
219+
// Restart it and follow logs for up to 5 seconds
220+
{
221+
args: []string{
222+
unikraftCmd, "instance", "restart",
223+
"--follow",
224+
"test-$UNIQ_INST",
225+
},
226+
timeout: 5 * time.Second,
227+
},
228+
{args: []string{unikraftCmd, "instance", "delete", "test-$UNIQ_INST"}},
229+
})
230+
})
231+
172232
t.Run("edit", func(t *testing.T) {
173233
r.
174234
online().

cmd/unikraft/main_test.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ type command struct {
5858
args []string
5959
err commandErr
6060
captureEnv string
61+
timeout time.Duration
6162
}
6263

6364
type commandErr int
@@ -201,12 +202,18 @@ func (b *testBuilder) run(t *testing.T, commands []command) {
201202
Strs("args", args).
202203
Msg("executing command")
203204

205+
cmdCtx := ctx
206+
var cmdCancel context.CancelFunc
207+
if command.timeout > 0 {
208+
cmdCtx, cmdCancel = context.WithTimeout(ctx, command.timeout)
209+
}
210+
204211
var cmd *exec.Cmd
205212
if args[0] == unikraftCmd {
206-
cmd = exec.CommandContext(ctx, r.unikraftPath, args[1:]...)
213+
cmd = exec.CommandContext(cmdCtx, r.unikraftPath, args[1:]...)
207214
cmd.Args[0] = r.unikraftPath
208215
} else {
209-
cmd = exec.CommandContext(ctx, args[0], args[1:]...)
216+
cmd = exec.CommandContext(cmdCtx, args[0], args[1:]...)
210217
}
211218

212219
var stdout, stderr bytes.Buffer
@@ -229,6 +236,15 @@ func (b *testBuilder) run(t *testing.T, commands []command) {
229236
}
230237

231238
err := cmd.Run()
239+
if cmdCancel != nil {
240+
cmdCancel()
241+
}
242+
var killedErr *exec.ExitError
243+
if command.timeout > 0 && errors.Is(cmdCtx.Err(), context.DeadlineExceeded) &&
244+
errors.As(err, &killedErr) && killedErr.ExitCode() == -1 {
245+
// The process was killed by the timeout: signal-terminated, exit code -1
246+
err = nil
247+
}
232248
if command.captureEnv != "" {
233249
value := strings.TrimSpace(stdout.String())
234250
if value == "" {

cmd/unikraft/testdata/TestGolden/instances/help

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
2.25 KB
Binary file not shown.
1.26 KB
Binary file not shown.

internal/cmd/instances.go

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,6 +1244,8 @@ func (cmd *InstancesLogsCmd) Run(ctx context.Context, stdio config.Stdio) error
12441244
type InstancesStartCmd struct {
12451245
Targets []string `arg:"" name:"target" completion-predictor:"resource-key-instance" help:"Target instances to start."`
12461246

1247+
Follow bool `help:"Follow log output after starting."`
1248+
12471249
cmd.FormatOpts
12481250
}
12491251

@@ -1255,6 +1257,12 @@ func (cmd InstancesStartCmd) Examples() []kingkong.Example {
12551257
"unikraft instance start demo-instance",
12561258
},
12571259
},
1260+
{
1261+
Description: "Start an instance and follow its logs",
1262+
Commands: []string{
1263+
"unikraft instance start demo-instance --follow",
1264+
},
1265+
},
12581266
}
12591267
}
12601268

@@ -1295,7 +1303,14 @@ func (c *InstancesStartCmd) Run(ctx context.Context, stdio config.Stdio) error {
12951303
})
12961304

12971305
diffErr := cmd.Diff(ctx, stdio.Stdout, c.FormatOpts, Instance{}, before, updated)
1298-
return errors.Join(opErr, diffErr)
1306+
if err := errors.Join(opErr, diffErr); err != nil {
1307+
return err
1308+
}
1309+
if !c.Follow || len(started) == 0 {
1310+
return nil
1311+
}
1312+
fmt.Fprintln(stdio.Stdout)
1313+
return streamInstanceLogs(ctx, stdio, started, 0, true)
12991314
}
13001315

13011316
type InstancesStopCmd struct {
@@ -1371,6 +1386,8 @@ type InstancesRestartCmd struct {
13711386
Targets []string `arg:"" name:"target" completion-predictor:"resource-key-instance" help:"Target instances to restart."`
13721387
StopOpts
13731388

1389+
Follow bool `help:"Follow log output after restarting."`
1390+
13741391
cmd.FormatOpts
13751392
}
13761393

@@ -1388,6 +1405,12 @@ func (cmd InstancesRestartCmd) Examples() []kingkong.Example {
13881405
"unikraft instance restart demo-instance --force",
13891406
},
13901407
},
1408+
{
1409+
Description: "Restart an instance and follow its logs",
1410+
Commands: []string{
1411+
"unikraft instance restart demo-instance --follow",
1412+
},
1413+
},
13911414
}
13921415
}
13931416

@@ -1434,7 +1457,14 @@ func (c *InstancesRestartCmd) Run(ctx context.Context, stdio config.Stdio) error
14341457
})
14351458

14361459
diffErr := cmd.Diff(ctx, stdio.Stdout, c.FormatOpts, Instance{}, before, updated)
1437-
return errors.Join(opErr, diffErr)
1460+
if err := errors.Join(opErr, diffErr); err != nil {
1461+
return err
1462+
}
1463+
if !c.Follow || len(started) == 0 {
1464+
return nil
1465+
}
1466+
fmt.Fprintln(stdio.Stdout)
1467+
return streamInstanceLogs(ctx, stdio, started, 0, true)
14381468
}
14391469

14401470
type StopOpts struct {

0 commit comments

Comments
 (0)