chore: establish local platform baseline
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
package recoveryconformance
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
CaptureSchema = "production-recovery-capture-v1"
|
||||
ReportSchema = "production-recovery-conformance-report-v1"
|
||||
)
|
||||
|
||||
var digestPattern = regexp.MustCompile(`^sha256:[0-9a-f]{64}$`)
|
||||
|
||||
var RequiredDrillTypes = []string{"POSTGRES_RESTORE", "REDIS_REBUILD", "SERVICE_ROLLING_RESTART", "PROVIDER_OUTAGE_RECOVERY"}
|
||||
|
||||
type Capture struct {
|
||||
SchemaVersion string `json:"schema_version"`
|
||||
EvidenceSource string `json:"evidence_source"`
|
||||
CandidateVersion string `json:"candidate_version"`
|
||||
BuildDigest string `json:"build_digest"`
|
||||
Environment string `json:"environment"`
|
||||
InfrastructureRef string `json:"infrastructure_ref"`
|
||||
FailureDomainRef string `json:"failure_domain_ref"`
|
||||
ChangeTicketRef string `json:"change_ticket_ref"`
|
||||
AttestedByRef string `json:"attested_by_ref"`
|
||||
AttestedAt time.Time `json:"attested_at"`
|
||||
CollectedAt time.Time `json:"collected_at"`
|
||||
Drills []DrillEvidence `json:"drills"`
|
||||
}
|
||||
|
||||
type DrillEvidence struct {
|
||||
DrillType string `json:"drill_type"`
|
||||
ScenarioRef string `json:"scenario_ref"`
|
||||
StartedAt time.Time `json:"started_at"`
|
||||
CompletedAt time.Time `json:"completed_at"`
|
||||
ObservedRecoverySeconds int `json:"observed_recovery_seconds"`
|
||||
RecoveryVerified bool `json:"recovery_verified"`
|
||||
DataIntegrityVerified bool `json:"data_integrity_verified"`
|
||||
BusinessContinuityVerified bool `json:"business_continuity_verified"`
|
||||
NoDuplicateEffects bool `json:"no_duplicate_effects"`
|
||||
CleanupVerified bool `json:"cleanup_verified"`
|
||||
BeforeStateDigest string `json:"before_state_digest"`
|
||||
AfterStateDigest string `json:"after_state_digest"`
|
||||
ExecutionRef string `json:"execution_ref"`
|
||||
ValidationRef string `json:"validation_ref"`
|
||||
AuditRef string `json:"audit_ref"`
|
||||
}
|
||||
|
||||
type Check struct {
|
||||
Code string `json:"code"`
|
||||
Status string `json:"status"`
|
||||
Detail string `json:"detail"`
|
||||
}
|
||||
|
||||
type Report struct {
|
||||
SchemaVersion string `json:"schema_version"`
|
||||
Result string `json:"result"`
|
||||
GeneratedAt time.Time `json:"generated_at"`
|
||||
CandidateVersion string `json:"candidate_version"`
|
||||
BuildDigest string `json:"build_digest"`
|
||||
EvidenceScope string `json:"evidence_scope"`
|
||||
ExternalValidationRequired bool `json:"external_validation_required"`
|
||||
Environment string `json:"environment"`
|
||||
InfrastructureRef string `json:"infrastructure_ref"`
|
||||
DrillCount int `json:"drill_count"`
|
||||
CompletedDrillTypes []string `json:"completed_drill_types"`
|
||||
ObservedRecoverySeconds map[string]int `json:"observed_recovery_seconds"`
|
||||
InputSHA256 map[string]string `json:"input_sha256"`
|
||||
Checks []Check `json:"checks"`
|
||||
Disclaimer string `json:"disclaimer"`
|
||||
}
|
||||
|
||||
func ParseCapture(content []byte) (Capture, error) {
|
||||
var capture Capture
|
||||
if err := decodeStrict(content, &capture); err != nil {
|
||||
return Capture{}, fmt.Errorf("decode production recovery capture: %w", err)
|
||||
}
|
||||
return capture, nil
|
||||
}
|
||||
|
||||
func ParseReport(content []byte) (Report, error) {
|
||||
var report Report
|
||||
if err := decodeStrict(content, &report); err != nil {
|
||||
return Report{}, fmt.Errorf("decode production recovery report: %w", err)
|
||||
}
|
||||
return report, nil
|
||||
}
|
||||
|
||||
func Evaluate(capture Capture, candidateVersion, buildDigest string, generatedAt time.Time) Report {
|
||||
candidateVersion = strings.TrimSpace(candidateVersion)
|
||||
buildDigest = strings.ToLower(strings.TrimSpace(buildDigest))
|
||||
report := Report{
|
||||
SchemaVersion: ReportSchema, Result: "PASS", GeneratedAt: generatedAt.UTC(), CandidateVersion: candidateVersion, BuildDigest: buildDigest,
|
||||
EvidenceScope: "LOCAL_RECOVERY", ExternalValidationRequired: true, Environment: capture.Environment, InfrastructureRef: capture.InfrastructureRef,
|
||||
DrillCount: len(capture.Drills), CompletedDrillTypes: []string{}, ObservedRecoverySeconds: map[string]int{}, InputSHA256: map[string]string{}, Checks: []Check{},
|
||||
Disclaimer: "恢复一致性报告只证明所提供候选、故障注入、恢复结果、数据完整性、连续性和审计引用相互一致;本地演练不替代生产等价基础设施和发布审批。",
|
||||
}
|
||||
if capture.EvidenceSource == "PRODUCTION_EQUIVALENT" {
|
||||
report.EvidenceScope = "PRODUCTION_EQUIVALENT"
|
||||
}
|
||||
add := func(code string, ok bool, detail string) {
|
||||
status := "PASS"
|
||||
if !ok {
|
||||
status, report.Result = "FAIL", "FAIL"
|
||||
}
|
||||
report.Checks = append(report.Checks, Check{Code: code, Status: status, Detail: detail})
|
||||
}
|
||||
add("CAPTURE_SCHEMA", capture.SchemaVersion == CaptureSchema, "恢复证据必须使用"+CaptureSchema)
|
||||
add("EVIDENCE_SOURCE", capture.EvidenceSource == "LOCAL" || capture.EvidenceSource == "PRODUCTION_EQUIVALENT", "证据来源必须明确为LOCAL或PRODUCTION_EQUIVALENT")
|
||||
add("CANDIDATE_IDENTITY", candidateVersion != "" && digestPattern.MatchString(buildDigest), "候选版本和sha256构建摘要必须完整")
|
||||
add("CAPTURE_CANDIDATE_BINDING", capture.CandidateVersion == candidateVersion && strings.ToLower(capture.BuildDigest) == buildDigest, "恢复采集必须绑定同一候选版本和构建摘要")
|
||||
real := capture.EvidenceSource == "PRODUCTION_EQUIVALENT"
|
||||
environmentOK := capture.InfrastructureRef != "" && capture.AttestedByRef != "" && !capture.AttestedAt.IsZero() && !capture.CollectedAt.IsZero() && !capture.AttestedAt.After(capture.CollectedAt)
|
||||
if real {
|
||||
environmentOK = environmentOK && (capture.Environment == "STAGING" || capture.Environment == "PRODUCTION") && capture.FailureDomainRef != "" && capture.ChangeTicketRef != ""
|
||||
} else {
|
||||
environmentOK = environmentOK && capture.Environment == "LOCAL"
|
||||
}
|
||||
add("ENVIRONMENT_IDENTITY", environmentOK, "生产等价证据必须绑定环境、故障域、变更工单和复核人")
|
||||
|
||||
drillsOK, timeOK, recoveryOK, integrityOK, continuityOK, refsOK := len(capture.Drills) > 0, true, true, true, true, true
|
||||
seen := map[string]bool{}
|
||||
for _, drill := range capture.Drills {
|
||||
if !slices.Contains(RequiredDrillTypes, drill.DrillType) || seen[drill.DrillType] || drill.ScenarioRef == "" {
|
||||
drillsOK = false
|
||||
}
|
||||
seen[drill.DrillType] = true
|
||||
timeOK = timeOK && !drill.StartedAt.IsZero() && !drill.CompletedAt.IsZero() && !drill.CompletedAt.Before(drill.StartedAt) && !drill.CompletedAt.After(capture.CollectedAt) && drill.ObservedRecoverySeconds >= 0
|
||||
recoveryOK = recoveryOK && drill.RecoveryVerified && drill.CleanupVerified
|
||||
integrityOK = integrityOK && drill.DataIntegrityVerified && drill.BeforeStateDigest != "" && drill.BeforeStateDigest == drill.AfterStateDigest
|
||||
continuityOK = continuityOK && drill.BusinessContinuityVerified && drill.NoDuplicateEffects
|
||||
refsOK = refsOK && drill.ExecutionRef != "" && drill.ValidationRef != "" && drill.AuditRef != ""
|
||||
report.ObservedRecoverySeconds[drill.DrillType] = drill.ObservedRecoverySeconds
|
||||
}
|
||||
for drillType := range seen {
|
||||
report.CompletedDrillTypes = append(report.CompletedDrillTypes, drillType)
|
||||
}
|
||||
slices.Sort(report.CompletedDrillTypes)
|
||||
if real {
|
||||
for _, drillType := range RequiredDrillTypes {
|
||||
drillsOK = drillsOK && seen[drillType]
|
||||
}
|
||||
}
|
||||
add("DRILL_COVERAGE", drillsOK, "生产等价恢复必须覆盖PostgreSQL、Redis、三个服务滚动重启和供应商停机恢复")
|
||||
add("TIMELINE_INTEGRITY", timeOK, "演练时间线和观测恢复用时必须有效")
|
||||
add("RECOVERY_OUTCOME", recoveryOK, "每项故障注入必须恢复成功并完成夹具清理")
|
||||
add("DATA_INTEGRITY", integrityOK, "恢复前后权威状态摘要必须一致")
|
||||
add("BUSINESS_CONTINUITY", continuityOK, "降级/恢复期间业务连续性必须通过且不得产生重复效果")
|
||||
add("AUDIT_REFERENCES", refsOK, "每项演练必须保存执行、验证和审计引用")
|
||||
if real && environmentOK && drillsOK && timeOK && recoveryOK && integrityOK && continuityOK && refsOK && report.Result == "PASS" {
|
||||
report.ExternalValidationRequired = false
|
||||
}
|
||||
return report
|
||||
}
|
||||
|
||||
func SHA256(content []byte) string {
|
||||
digest := sha256.Sum256(content)
|
||||
return hex.EncodeToString(digest[:])
|
||||
}
|
||||
|
||||
func decodeStrict(content []byte, target any) error {
|
||||
decoder := json.NewDecoder(bytes.NewReader(content))
|
||||
decoder.DisallowUnknownFields()
|
||||
if err := decoder.Decode(target); err != nil {
|
||||
return err
|
||||
}
|
||||
var trailing any
|
||||
if err := decoder.Decode(&trailing); !errors.Is(err, io.EOF) {
|
||||
return errors.New("trailing JSON data")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user