mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-21 02:30:14 +00:00
test(agentcompat): add dashboard runtime harness
Co-authored-by: naiba/CloudCode <hi+cloudcode@nai.ba>
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
//go:build linux
|
||||
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/nezhahq/nezha/integration/agentcompat/internal/client"
|
||||
processharness "github.com/nezhahq/nezha/integration/agentcompat/internal/process"
|
||||
)
|
||||
|
||||
func (dashboard *Dashboard) StopProcess(ctx context.Context) (RuntimeIdentity, error) {
|
||||
dashboard.lifecycleMu.Lock()
|
||||
defer dashboard.lifecycleMu.Unlock()
|
||||
dashboard.stateMu.Lock()
|
||||
process := dashboard.currentProcess
|
||||
dashboard.currentProcess = nil
|
||||
dashboard.supervisor = nil
|
||||
dashboard.stateMu.Unlock()
|
||||
if process == nil {
|
||||
return RuntimeIdentity{}, errors.New("dashboard process is not running")
|
||||
}
|
||||
if process.receiptConn != nil {
|
||||
_ = process.receiptConn.Close()
|
||||
}
|
||||
if process.httpTransport != nil {
|
||||
process.httpTransport.CloseIdleConnections()
|
||||
}
|
||||
if process.tlsTransport != nil {
|
||||
process.tlsTransport.CloseIdleConnections()
|
||||
}
|
||||
if err := process.supervisor.Stop(ctx); err != nil {
|
||||
return process.identity, fmt.Errorf("stop dashboard process: %w", err)
|
||||
}
|
||||
process.record = process.supervisor.CleanupRecord()
|
||||
return process.identity, nil
|
||||
}
|
||||
|
||||
func (dashboard *Dashboard) StartProcess(ctx context.Context) (RuntimeIdentity, error) {
|
||||
dashboard.lifecycleMu.Lock()
|
||||
defer dashboard.lifecycleMu.Unlock()
|
||||
dashboard.stateMu.Lock()
|
||||
if dashboard.currentProcess != nil {
|
||||
dashboard.stateMu.Unlock()
|
||||
return RuntimeIdentity{}, errors.New("dashboard process is already running")
|
||||
}
|
||||
dashboard.generation++
|
||||
generation := dashboard.generation
|
||||
dashboard.stateMu.Unlock()
|
||||
dashboard.receiptMu.Lock()
|
||||
dashboard.receiptAccepted = false
|
||||
dashboard.receiptAcceptedCount = 0
|
||||
dashboard.receiptGeneration = 0
|
||||
dashboard.receiptMu.Unlock()
|
||||
process, err := dashboard.startGeneration(ctx, generation)
|
||||
if err != nil {
|
||||
return RuntimeIdentity{}, err
|
||||
}
|
||||
dashboard.stateMu.Lock()
|
||||
dashboard.currentProcess = process
|
||||
dashboard.supervisor = process.supervisor
|
||||
dashboard.processes = append(dashboard.processes, process)
|
||||
dashboard.stateMu.Unlock()
|
||||
return process.identity, nil
|
||||
}
|
||||
|
||||
func (dashboard *Dashboard) FixtureIdentity() FixtureIdentity {
|
||||
identity := FixtureIdentity{WorkspaceRoot: dashboard.workspace.Root(), ConfigPath: dashboard.configPath, DatabasePath: dashboard.databasePath, BinaryPath: dashboard.binaryPath}
|
||||
if dashboard.httpListener != nil {
|
||||
identity.HTTP = dashboard.httpListener.Identity()
|
||||
}
|
||||
if dashboard.receiptListener != nil {
|
||||
identity.Receipt = dashboard.receiptListener.Identity()
|
||||
}
|
||||
if dashboard.httpsListener != nil {
|
||||
identity.HTTPS = dashboard.httpsListener.Identity()
|
||||
}
|
||||
return identity
|
||||
}
|
||||
|
||||
func (dashboard *Dashboard) RuntimeIdentity() RuntimeIdentity {
|
||||
dashboard.stateMu.Lock()
|
||||
defer dashboard.stateMu.Unlock()
|
||||
if dashboard.currentProcess == nil {
|
||||
return RuntimeIdentity{}
|
||||
}
|
||||
return dashboard.currentProcess.identity
|
||||
}
|
||||
|
||||
func (dashboard *Dashboard) Restart(ctx context.Context) error {
|
||||
if _, err := dashboard.StopProcess(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := dashboard.StartProcess(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
func (dashboard *Dashboard) cleanupOnCancellation(ctx context.Context) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
dashboard.cleanupOnce.Do(func() { go dashboard.cleanup(context.WithoutCancel(ctx)) })
|
||||
case <-dashboard.cleanupDone:
|
||||
}
|
||||
}
|
||||
|
||||
func (dashboard *Dashboard) cleanup(ctx context.Context) {
|
||||
defer close(dashboard.cleanupDone)
|
||||
var stopError error
|
||||
cleanupReceipt := dashboard.cleanupProcesses(ctx, &stopError)
|
||||
if err := dashboard.workspace.Close(); err != nil {
|
||||
stopError = errors.Join(stopError, fmt.Errorf("close dashboard workspace: %w", err))
|
||||
cleanupReceipt = processharness.NewCleanupReceipt(append(cleanupReceipt.Processes, processharness.CleanupRecord{Name: "dashboard-workspace", Error: client.Redact(err.Error())}))
|
||||
}
|
||||
dashboard.cleanupMu.Lock()
|
||||
dashboard.cleanupError = stopError
|
||||
dashboard.cleanupReceipt = cleanupReceipt
|
||||
dashboard.cleanupMu.Unlock()
|
||||
}
|
||||
|
||||
func (dashboard *Dashboard) cleanupProcesses(ctx context.Context, stopError *error) processharness.CleanupReceipt {
|
||||
cleanupReceipt := processharness.CleanupReceipt{}
|
||||
dashboard.stateMu.Lock()
|
||||
processes := append([]*dashboardGeneration(nil), dashboard.processes...)
|
||||
legacySupervisor := dashboard.supervisor
|
||||
dashboard.stateMu.Unlock()
|
||||
if len(processes) == 0 && legacySupervisor != nil {
|
||||
if err := legacySupervisor.Stop(ctx); err != nil {
|
||||
*stopError = errors.Join(*stopError, fmt.Errorf("stop dashboard process: %w", err))
|
||||
}
|
||||
return processharness.NewCleanupReceipt([]processharness.CleanupRecord{legacySupervisor.CleanupRecord()})
|
||||
}
|
||||
for _, process := range processes {
|
||||
if process.receiptConn != nil {
|
||||
_ = process.receiptConn.Close()
|
||||
}
|
||||
if process.httpTransport != nil {
|
||||
process.httpTransport.CloseIdleConnections()
|
||||
}
|
||||
if process.tlsTransport != nil {
|
||||
process.tlsTransport.CloseIdleConnections()
|
||||
}
|
||||
stopContext, cancel := context.WithTimeout(ctx, failedStartCleanupTimeout)
|
||||
if err := process.supervisor.Stop(stopContext); err != nil {
|
||||
*stopError = errors.Join(*stopError, fmt.Errorf("stop dashboard process: %w", err))
|
||||
}
|
||||
cancel()
|
||||
process.record = process.supervisor.CleanupRecord()
|
||||
if process.record.Forced {
|
||||
*stopError = errors.Join(*stopError, errors.New("dashboard required forced SIGKILL cleanup"))
|
||||
}
|
||||
cleanupReceipt.Processes = append(cleanupReceipt.Processes, process.record)
|
||||
}
|
||||
return processharness.NewCleanupReceipt(cleanupReceipt.Processes)
|
||||
}
|
||||
Reference in New Issue
Block a user