init
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"browser.local/run/config"
|
||||
"browser.local/run/protocol"
|
||||
)
|
||||
|
||||
const metricReportTimeout = 5 * time.Second
|
||||
|
||||
// MetricCollector provides only generic local utilization data. Game-specific
|
||||
// observations belong to a plugin-declared bridge, never the Run runtime.
|
||||
type MetricCollector interface {
|
||||
Collect(context.Context, string) (MetricUtilization, error)
|
||||
}
|
||||
|
||||
type MetricUtilization struct {
|
||||
CPUPercent *float64
|
||||
MemoryPercent *float64
|
||||
DiskPercent *float64
|
||||
}
|
||||
|
||||
type defaultMetricCollector struct{}
|
||||
|
||||
func (defaultMetricCollector) Collect(ctx context.Context, workspaceRoot string) (MetricUtilization, error) {
|
||||
var utilization MetricUtilization
|
||||
var collectionErrors []error
|
||||
if cpuPercent, err := hostCPUPercent(ctx); err != nil {
|
||||
collectionErrors = append(collectionErrors, fmt.Errorf("collect CPU utilization: %w", err))
|
||||
} else {
|
||||
utilization.CPUPercent = &cpuPercent
|
||||
}
|
||||
if memoryPercent, err := hostMemoryPercent(); err != nil {
|
||||
collectionErrors = append(collectionErrors, fmt.Errorf("collect memory utilization: %w", err))
|
||||
} else {
|
||||
utilization.MemoryPercent = &memoryPercent
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
collectionErrors = append(collectionErrors, err)
|
||||
} else if diskPercent, err := workspaceDiskPercent(workspaceRoot); err != nil {
|
||||
collectionErrors = append(collectionErrors, fmt.Errorf("collect disk utilization: %w", err))
|
||||
} else {
|
||||
utilization.DiskPercent = &diskPercent
|
||||
}
|
||||
return utilization, errors.Join(collectionErrors...)
|
||||
}
|
||||
|
||||
// WithMetricCollector overrides the generic host collector for tests and
|
||||
// platform-specific collectors. It has no access to plugin inputs or secrets.
|
||||
func WithMetricCollector(collector MetricCollector) LifecycleExecutorOption {
|
||||
return func(executor *LifecycleExecutor) {
|
||||
executor.metricCollector = collector
|
||||
}
|
||||
}
|
||||
|
||||
func (worker *Worker) reportMetricsDegraded(ctx context.Context, trigger string) {
|
||||
if err := worker.ReportMetricsOnce(ctx); err != nil {
|
||||
log.Printf("RUN phase=metrics status=degraded trigger=%s error=%s", safeOptional(trigger), RedactText(err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
func (worker *Worker) ReportMetricsOnce(ctx context.Context) error {
|
||||
if worker.cfg.ComponentKind != config.PackageComponentRun || worker.cfg.ServerInstanceID == "" {
|
||||
return nil
|
||||
}
|
||||
state, err := worker.registeredState()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sample := protocol.MetricSample{ServerInstanceID: worker.cfg.ServerInstanceID, Online: worker.managedServerProcessOnline(state), Source: "run", CollectedAt: time.Now().UTC()}
|
||||
if worker.metricCollector != nil {
|
||||
utilization, collectErr := worker.metricCollector.Collect(ctx, worker.cfg.WorkspaceRoot)
|
||||
sample.CPUPercent = utilization.CPUPercent
|
||||
sample.MemoryPercent = utilization.MemoryPercent
|
||||
sample.DiskPercent = utilization.DiskPercent
|
||||
if collectErr != nil {
|
||||
log.Printf("RUN phase=metrics status=utilization_unavailable error=%s", RedactText(collectErr.Error()))
|
||||
}
|
||||
}
|
||||
reportCtx, cancel := context.WithTimeout(ctx, metricReportTimeout)
|
||||
defer cancel()
|
||||
response, err := worker.client.IngestMetricBatch(reportCtx, protocol.MetricBatchIngestRequest{RunEndpointID: state.RunEndpointID, SessionToken: state.SessionToken, Samples: []protocol.MetricSample{sample}})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Accepted || response.AcceptedCount != 1 {
|
||||
return fmt.Errorf("metric batch was not accepted")
|
||||
}
|
||||
log.Printf("RUN phase=metrics status=accepted server=%s online=%t", sample.ServerInstanceID, sample.Online)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (worker *Worker) managedServerProcessOnline(state WorkerState) bool {
|
||||
source, ok := worker.executor.managed.(ManagedProcessObservationSource)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
for _, identity := range source.ManagedProcessObservations() {
|
||||
if identity.ServerInstanceID != worker.cfg.ServerInstanceID || identity.RunEndpointID != state.RunEndpointID {
|
||||
continue
|
||||
}
|
||||
if worker.executor.managed.Status(identity).State == "running" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user