Files
nezha_domains/integration/agentcompat/internal/scenario/transfer_evidence.go
T
2026-07-20 04:51:20 +00:00

70 lines
4.5 KiB
Go

//go:build linux
package scenario
import (
"errors"
"fmt"
"strings"
"time"
"github.com/nezhahq/nezha/integration/agentcompat/internal/contract"
)
var (
ErrTransferEvidenceSizeMismatch = errors.New("transfer evidence size mismatch")
ErrTransferEvidenceHashMismatch = errors.New("transfer evidence hash mismatch")
ErrTransferEvidenceHeapBudget = errors.New("transfer evidence retained heap budget exceeded")
ErrTransferEvidenceMeasurement = errors.New("transfer evidence measurement missing")
ErrTransferEvidenceContract = errors.New("transfer evidence contract assertion failed")
)
type TransferEvidence struct {
WarmupUploadBytes uint64 `json:"warmup_upload_bytes"`
WarmupDownloadBytes uint64 `json:"warmup_download_bytes"`
WarmupSHA256 string `json:"warmup_sha256"`
WarmupDuration time.Duration `json:"warmup_duration"`
WarmupDeadlineRemaining time.Duration `json:"warmup_deadline_remaining"`
WarmupQuiescent bool `json:"warmup_quiescent"`
UploadBytes uint64 `json:"upload_bytes"`
DownloadBytes uint64 `json:"download_bytes"`
UploadSHA256 string `json:"upload_sha256"`
DownloadSHA256 string `json:"download_sha256"`
UploadChunks uint64 `json:"upload_chunks"`
DownloadChunks uint64 `json:"download_chunks"`
UploadDuration time.Duration `json:"upload_duration"`
DownloadDuration time.Duration `json:"download_duration"`
RetainedHeapBytes uint64 `json:"retained_heap_bytes"`
Mode string `json:"mode"`
CreateDirs bool `json:"create_dirs"`
UploadReplayRejected bool `json:"upload_replay_rejected"`
DownloadReplayRejected bool `json:"download_replay_rejected"`
OversizeRejected bool `json:"oversize_rejected"`
AgentTempResidue int `json:"agent_temp_residue"`
DashboardSpoolResidue int `json:"dashboard_spool_residue"`
OutsideRootSentinelsUnchanged bool `json:"outside_root_sentinels_unchanged"`
}
func (e TransferEvidence) Validate() error {
var validationErr error
if e.WarmupUploadBytes != transferWarmupBytes || e.WarmupDownloadBytes != transferWarmupBytes || e.WarmupSHA256 == "" || e.WarmupDuration <= 0 || e.WarmupDeadlineRemaining <= 0 || !e.WarmupQuiescent {
validationErr = errors.Join(validationErr, fmt.Errorf("%w: warmup_upload=%d warmup_download=%d warmup_sha=%q warmup_duration=%s warmup_deadline=%s warmup_quiescent=%t", ErrTransferEvidenceMeasurement, e.WarmupUploadBytes, e.WarmupDownloadBytes, e.WarmupSHA256, e.WarmupDuration, e.WarmupDeadlineRemaining, e.WarmupQuiescent))
}
if e.UploadBytes != contract.TransferBytes || e.DownloadBytes != contract.TransferBytes {
validationErr = errors.Join(validationErr, fmt.Errorf("%w: upload=%d download=%d want=%d", ErrTransferEvidenceSizeMismatch, e.UploadBytes, e.DownloadBytes, contract.TransferBytes))
}
if e.UploadSHA256 == "" || e.DownloadSHA256 == "" || !strings.EqualFold(e.UploadSHA256, e.DownloadSHA256) {
validationErr = errors.Join(validationErr, fmt.Errorf("%w: upload=%q download=%q", ErrTransferEvidenceHashMismatch, e.UploadSHA256, e.DownloadSHA256))
}
if e.UploadChunks == 0 || e.DownloadChunks == 0 || e.UploadDuration <= 0 || e.DownloadDuration <= 0 {
validationErr = errors.Join(validationErr, fmt.Errorf("%w: upload_chunks=%d download_chunks=%d upload_duration=%s download_duration=%s", ErrTransferEvidenceMeasurement, e.UploadChunks, e.DownloadChunks, e.UploadDuration, e.DownloadDuration))
}
if e.RetainedHeapBytes > contract.TransferHeapBytes {
validationErr = errors.Join(validationErr, fmt.Errorf("%w: retained=%d limit=%d", ErrTransferEvidenceHeapBudget, e.RetainedHeapBytes, contract.TransferHeapBytes))
}
if e.Mode != "0640" || !e.CreateDirs || !e.UploadReplayRejected || !e.DownloadReplayRejected || !e.OversizeRejected || e.AgentTempResidue != 0 || e.DashboardSpoolResidue != 0 || !e.OutsideRootSentinelsUnchanged {
validationErr = errors.Join(validationErr, fmt.Errorf("%w: mode=%q create_dirs=%t upload_replay=%t download_replay=%t oversize=%t agent_temp=%d dashboard_spool=%d sentinels=%t", ErrTransferEvidenceContract, e.Mode, e.CreateDirs, e.UploadReplayRejected, e.DownloadReplayRejected, e.OversizeRejected, e.AgentTempResidue, e.DashboardSpoolResidue, e.OutsideRootSentinelsUnchanged))
}
return validationErr
}