package hostops import ( "context" "errors" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/release" "reflect" "time" ) // ReportAutomaticLosses materializes retained consequences even after operation // or holder replacement. The signed record is durable before reporting is marked. func (w *ControlWriter) ReportAutomaticLosses(ctx context.Context, signer release.Signer, host string) error { if recoveryWriter(ctx) != w { return ErrContract } if err := w.lossSigner(signer); err != nil { return err } o, err := hostclaim.Read(ctx, w.raw, host) if err != nil { return err } if o.Absent { return nil } for _, l := range o.Claim.EvidenceRefs { if l.Reported { continue } record, err := hostclaim.ReadLossRecord(ctx, w.raw, w.pub, l.ID) if errors.Is(err, hostclaim.ErrLossRecordAbsent) { at, e := time.Parse(time.RFC3339Nano, l.ObservedAt) if e != nil { return e } r := hostclaim.LossRecord{AlertsPending: true, V: 1, ID: l.ID, State: "prepared", Actor: "automatic", AcceptedAt: at, Host: host, HolderIncarnation: l.Incarnation, HolderStamp: l.Stamp, HolderSession: l.Session, ExpectedClaimGen: o.Claim.Gen, ExpectedClaimETag: o.ETag, OpID: l.Op, OpKind: l.Kind, OpPhase: l.Phase, Box: l.Box, Enrollment: l.Enrollment, FencedAt: at, Timers: hostclaim.TimerLoss{Knowledge: l.Timers}, Automatic: &l} record, err = hostclaim.CreateLossRecord(ctx, w.raw, signer, r) } if err != nil { return err } if !reflect.DeepEqual(record.Record.Automatic, &l) || record.Record.Host != host { return hostclaim.ErrLossRecord } if record.Record.State == "prepared" { next := record.Record next.State = "committed" next.ResultClaimGen = next.ExpectedClaimGen record, err = hostclaim.ReplaceLossRecord(ctx, w.raw, signer, record, next) if err != nil { return err } } if record.Record.State != "committed" { return hostclaim.ErrLossRecord } o, err = hostclaim.Update(ctx, w.raw, o, func(c *hostclaim.Claim) error { for i := range c.EvidenceRefs { if c.EvidenceRefs[i].ID == l.ID { if !reflect.DeepEqual(c.EvidenceRefs[i], l) { return hostclaim.ErrLossRecord } c.EvidenceRefs[i].Reported = true return nil } } return hostclaim.ErrLossRecord }) if err != nil { return err } } return nil }