mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 09:40:12 +00:00
157 lines
5.2 KiB
Go
157 lines
5.2 KiB
Go
//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)
|
|
}
|