-
Notifications
You must be signed in to change notification settings - Fork 0
Operational logs over API: hypeman.log, vmm.log #34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
ac6bd97
713620e
f2525b7
5c32875
3dd1f9d
e826b23
6af82cc
c92b545
db6c89f
390d6c6
92b2854
b6c7c37
dada46d
dfddd92
0382d44
228f6b1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -146,10 +146,18 @@ func run() error { | |
| "kernel", kernelVer) | ||
|
|
||
| // Initialize network manager (creates default network if needed) | ||
| // Get running instance IDs for TAP cleanup | ||
| runningIDs := getRunningInstanceIDs(app) | ||
| // Get instance IDs that might have a running VMM for TAP cleanup safety. | ||
| // Include Unknown state: we couldn't confirm their state, but they might still | ||
| // have a running VMM. Better to leave a stale TAP than crash a running VM. | ||
| allInstances, _ := app.InstanceManager.ListInstances(app.Ctx) | ||
| var preserveTAPs []string | ||
| for _, inst := range allInstances { | ||
| if inst.State == instances.StateRunning || inst.State == instances.StateUnknown { | ||
| preserveTAPs = append(preserveTAPs, inst.Id) | ||
| } | ||
| } | ||
|
Comment on lines
149
to
167
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change is the fix for stopping a possibly running instance, the rest of the change is VMM and Hypeman logs feature, which was inspired by how this problem was debugged (find issue in CH logs)
sjmiller609 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| logger.Info("Initializing network manager...") | ||
| if err := app.NetworkManager.Initialize(app.Ctx, runningIDs); err != nil { | ||
| if err := app.NetworkManager.Initialize(app.Ctx, preserveTAPs); err != nil { | ||
| logger.Error("failed to initialize network manager", "error", err) | ||
| return fmt.Errorf("initialize network manager: %w", err) | ||
| } | ||
|
|
@@ -199,6 +207,7 @@ func run() error { | |
| middleware.RequestID, | ||
| middleware.RealIP, | ||
| middleware.Recoverer, | ||
| mw.InjectLogger(logger), | ||
| mw.AccessLogger(accessLogger), | ||
| mw.JwtAuth(app.Config.JwtSecret), | ||
| ).Get("/instances/{id}/exec", app.ApiService.ExecHandler) | ||
|
|
@@ -226,7 +235,8 @@ func run() error { | |
| } | ||
|
|
||
| // Inject logger into request context for handlers to use | ||
| r.Use(mw.InjectLogger(accessLogger)) | ||
| // Use app logger (not accessLogger) so the instance log handler is included | ||
sjmiller609 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| r.Use(mw.InjectLogger(logger)) | ||
|
|
||
| // Access logger AFTER otelchi so trace context is available | ||
| r.Use(mw.AccessLogger(accessLogger)) | ||
|
|
@@ -354,21 +364,6 @@ func run() error { | |
| return err | ||
| } | ||
|
|
||
| // getRunningInstanceIDs returns IDs of instances currently in Running state | ||
| func getRunningInstanceIDs(app *application) []string { | ||
| allInstances, err := app.InstanceManager.ListInstances(app.Ctx) | ||
| if err != nil { | ||
| return nil | ||
| } | ||
| var running []string | ||
| for _, inst := range allInstances { | ||
| if inst.State == instances.StateRunning { | ||
| running = append(running, inst.Id) | ||
| } | ||
| } | ||
| return running | ||
| } | ||
|
|
||
| // checkKVMAccess verifies KVM is available and the user has permission to use it | ||
| func checkKVMAccess() error { | ||
| f, err := os.OpenFile("/dev/kvm", os.O_RDWR, 0) | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,14 +12,29 @@ import ( | |
| "github.com/onkernel/hypeman/lib/logger" | ||
| ) | ||
|
|
||
| // LogSource represents a log source type | ||
| type LogSource string | ||
|
|
||
| const ( | ||
| // LogSourceApp is the guest application log (serial console) | ||
| LogSourceApp LogSource = "app" | ||
| // LogSourceVMM is the Cloud Hypervisor VMM log | ||
| LogSourceVMM LogSource = "vmm" | ||
| // LogSourceHypeman is the hypeman operations log | ||
| LogSourceHypeman LogSource = "hypeman" | ||
| ) | ||
|
|
||
| // ErrTailNotFound is returned when the tail command is not available | ||
| var ErrTailNotFound = fmt.Errorf("tail command not found: required for log streaming") | ||
|
|
||
| // StreamInstanceLogs streams instance console logs | ||
| // ErrLogNotFound is returned when the requested log file doesn't exist | ||
| var ErrLogNotFound = fmt.Errorf("log file not found") | ||
sjmiller609 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // streamInstanceLogs streams instance logs from the specified source | ||
| // Returns last N lines, then continues following if follow=true | ||
| func (m *manager) streamInstanceLogs(ctx context.Context, id string, tail int, follow bool) (<-chan string, error) { | ||
| func (m *manager) streamInstanceLogs(ctx context.Context, id string, tail int, follow bool, source LogSource) (<-chan string, error) { | ||
| log := logger.FromContext(ctx) | ||
| log.DebugContext(ctx, "starting log stream", "id", id, "tail", tail, "follow", follow) | ||
| log.DebugContext(ctx, "starting log stream", "id", id, "tail", tail, "follow", follow, "source", source) | ||
|
|
||
| // Verify tail command is available | ||
| if _, err := exec.LookPath("tail"); err != nil { | ||
|
|
@@ -30,7 +45,24 @@ func (m *manager) streamInstanceLogs(ctx context.Context, id string, tail int, f | |
| return nil, err | ||
| } | ||
|
|
||
| logPath := m.paths.InstanceConsoleLog(id) | ||
| // Determine log path based on source | ||
| var logPath string | ||
| switch source { | ||
| case LogSourceApp: | ||
| logPath = m.paths.InstanceAppLog(id) | ||
| case LogSourceVMM: | ||
| logPath = m.paths.InstanceVMMLog(id) | ||
| case LogSourceHypeman: | ||
| logPath = m.paths.InstanceHypemanLog(id) | ||
| default: | ||
| // Default to app log for backwards compatibility | ||
| logPath = m.paths.InstanceAppLog(id) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. enjoy the freedom of not having to care about backwards compatability right now! :) |
||
| } | ||
|
|
||
| // Check if log file exists before starting tail | ||
| if _, err := os.Stat(logPath); os.IsNotExist(err) { | ||
| return nil, ErrLogNotFound | ||
| } | ||
|
|
||
| // Build tail command | ||
| args := []string{"-n", strconv.Itoa(tail)} | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.