Skip to content

Commit ffa3a9c

Browse files
yroblataskbotCopilot
authored
feat: add runtime check command (#1165)
Co-authored-by: taskbot <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent 88c04f9 commit ffa3a9c

File tree

4 files changed

+175
-0
lines changed

4 files changed

+175
-0
lines changed

cmd/thv/app/runtime.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package app
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"time"
7+
8+
"github.com/spf13/cobra"
9+
10+
"github.com/stacklok/toolhive/pkg/container"
11+
"github.com/stacklok/toolhive/pkg/container/runtime"
12+
)
13+
14+
// Define the `runtime` parent command
15+
var runtimeCmd = &cobra.Command{
16+
Use: "runtime",
17+
Short: "Commands related to the container runtime",
18+
}
19+
20+
// Define the `runtime check` subcommand
21+
var runtimeCheckCmd = &cobra.Command{
22+
Use: "check",
23+
Short: "Ping the container runtime",
24+
Long: "Ensure the container runtime is responsive.",
25+
Args: cobra.NoArgs, // no args allowed
26+
RunE: runtimeCheckCmdFunc,
27+
}
28+
29+
var runtimeCheckTimeout int
30+
31+
func init() {
32+
rootCmd.AddCommand(runtimeCmd)
33+
runtimeCmd.AddCommand(runtimeCheckCmd)
34+
runtimeCheckCmd.Flags().IntVar(&runtimeCheckTimeout, "timeout", 30,
35+
"Timeout in seconds for runtime checks (default: 30 seconds)")
36+
}
37+
38+
func init() {
39+
rootCmd.AddCommand(runtimeCmd)
40+
runtimeCmd.AddCommand(runtimeCheckCmd)
41+
}
42+
43+
func runtimeCheckCmdFunc(cmd *cobra.Command, _ []string) error {
44+
ctx := cmd.Context()
45+
46+
// Create runtime with timeout
47+
createCtx, cancelCreate := context.WithTimeout(ctx, time.Duration(runtimeCheckTimeout)*time.Second)
48+
defer cancelCreate()
49+
rt, err := createWithTimeout(createCtx)
50+
if err != nil {
51+
if createCtx.Err() == context.DeadlineExceeded {
52+
return fmt.Errorf("creating container runtime timed out after %d seconds", runtimeCheckTimeout)
53+
}
54+
return fmt.Errorf("failed to create container runtime: %w", err)
55+
}
56+
57+
// Ping with separate timeout
58+
pingCtx, cancelPing := context.WithTimeout(ctx, time.Duration(runtimeCheckTimeout)*time.Second)
59+
defer cancelPing()
60+
if err := pingRuntime(pingCtx, rt); err != nil {
61+
if pingCtx.Err() == context.DeadlineExceeded {
62+
return fmt.Errorf("runtime ping timed out after %d seconds", runtimeCheckTimeout)
63+
}
64+
return fmt.Errorf("runtime ping failed: %w", err)
65+
}
66+
67+
fmt.Println("Container runtime is responsive")
68+
return nil
69+
}
70+
71+
func createWithTimeout(ctx context.Context) (runtime.Runtime, error) {
72+
done := make(chan struct {
73+
rt runtime.Runtime
74+
err error
75+
}, 1)
76+
go func() {
77+
rt, err := container.NewFactory().Create(ctx)
78+
done <- struct {
79+
rt runtime.Runtime
80+
err error
81+
}{rt, err}
82+
}()
83+
84+
select {
85+
case <-ctx.Done():
86+
return nil, ctx.Err()
87+
case res := <-done:
88+
return res.rt, res.err
89+
}
90+
}
91+
92+
func pingRuntime(ctx context.Context, rt runtime.Runtime) error {
93+
done := make(chan error, 1)
94+
go func() {
95+
done <- rt.IsRunning(ctx)
96+
}()
97+
98+
select {
99+
case <-ctx.Done():
100+
return ctx.Err()
101+
case err := <-done:
102+
return err
103+
}
104+
}

docs/cli/thv.md

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cli/thv_runtime.md

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cli/thv_runtime_check.md

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)