2019-01-24 20:15:10 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
2018-10-16 18:40:34 +01:00
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package audit
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
|
2019-05-23 21:07:19 +01:00
|
|
|
"github.com/zeebo/errs"
|
2019-06-07 13:38:41 +01:00
|
|
|
"go.uber.org/zap"
|
2019-05-23 21:07:19 +01:00
|
|
|
|
2019-12-27 11:48:47 +00:00
|
|
|
"storj.io/common/storj"
|
2021-11-08 20:51:04 +00:00
|
|
|
"storj.io/storj/satellite/overlay"
|
2021-06-23 00:09:39 +01:00
|
|
|
"storj.io/storj/satellite/reputation"
|
2018-10-16 18:40:34 +01:00
|
|
|
)
|
|
|
|
|
2022-04-11 17:47:14 +01:00
|
|
|
// reporter records audit reports in overlay and implements the Reporter interface.
|
2019-09-10 14:24:16 +01:00
|
|
|
//
|
|
|
|
// architecture: Service
|
2022-04-11 17:47:14 +01:00
|
|
|
type reporter struct {
|
2019-06-07 13:38:41 +01:00
|
|
|
log *zap.Logger
|
2021-06-23 00:09:39 +01:00
|
|
|
reputations *reputation.Service
|
2019-05-31 16:23:00 +01:00
|
|
|
containment Containment
|
|
|
|
maxRetries int
|
|
|
|
maxReverifyCount int32
|
2018-10-16 18:40:34 +01:00
|
|
|
}
|
|
|
|
|
2022-04-11 17:47:14 +01:00
|
|
|
// Reporter records audit reports in the overlay and database.
|
|
|
|
type Reporter interface {
|
|
|
|
RecordAudits(ctx context.Context, req Report) (_ Report, err error)
|
|
|
|
}
|
|
|
|
|
2021-08-11 21:02:54 +01:00
|
|
|
// Report contains audit result.
|
|
|
|
// It records whether an audit is able to be completed, the total number of
|
|
|
|
// pieces a given audit has conducted for, lists for nodes that
|
|
|
|
// succeeded, failed, were offline, have pending audits, or failed for unknown
|
2021-11-08 20:51:04 +00:00
|
|
|
// reasons and their current reputation status.
|
2019-05-23 23:32:19 +01:00
|
|
|
type Report struct {
|
2021-11-08 20:51:04 +00:00
|
|
|
Successes storj.NodeIDList
|
|
|
|
Fails storj.NodeIDList
|
|
|
|
Offlines storj.NodeIDList
|
|
|
|
PendingAudits []*PendingAudit
|
|
|
|
Unknown storj.NodeIDList
|
|
|
|
NodesReputation map[storj.NodeID]overlay.ReputationStatus
|
2018-12-19 18:44:03 +00:00
|
|
|
}
|
|
|
|
|
2020-07-16 15:18:02 +01:00
|
|
|
// NewReporter instantiates a reporter.
|
2022-04-11 17:47:14 +01:00
|
|
|
func NewReporter(log *zap.Logger, reputations *reputation.Service, containment Containment, maxRetries int, maxReverifyCount int32) Reporter {
|
|
|
|
return &reporter{
|
2019-06-07 13:38:41 +01:00
|
|
|
log: log,
|
2021-06-23 00:09:39 +01:00
|
|
|
reputations: reputations,
|
2019-06-07 13:38:41 +01:00
|
|
|
containment: containment,
|
|
|
|
maxRetries: maxRetries,
|
2021-09-15 21:31:33 +01:00
|
|
|
maxReverifyCount: maxReverifyCount,
|
|
|
|
}
|
2018-10-16 18:40:34 +01:00
|
|
|
}
|
|
|
|
|
2019-08-06 17:35:59 +01:00
|
|
|
// RecordAudits saves audit results to overlay. When no error, it returns
|
2019-06-25 10:23:41 +01:00
|
|
|
// nil for both return values, otherwise it returns the report with the fields
|
|
|
|
// set to the values which have been saved and the error.
|
2022-04-11 17:47:14 +01:00
|
|
|
func (reporter *reporter) RecordAudits(ctx context.Context, req Report) (_ Report, err error) {
|
2019-06-04 12:36:27 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-05-27 12:13:47 +01:00
|
|
|
|
2019-05-23 23:32:19 +01:00
|
|
|
successes := req.Successes
|
|
|
|
fails := req.Fails
|
2020-03-09 15:35:54 +00:00
|
|
|
unknowns := req.Unknown
|
2019-05-23 23:32:19 +01:00
|
|
|
offlines := req.Offlines
|
2019-05-23 21:07:19 +01:00
|
|
|
pendingAudits := req.PendingAudits
|
2018-12-19 18:44:03 +00:00
|
|
|
|
2019-06-07 13:38:41 +01:00
|
|
|
reporter.log.Debug("Reporting audits",
|
|
|
|
zap.Int("successes", len(successes)),
|
|
|
|
zap.Int("failures", len(fails)),
|
2020-03-09 15:35:54 +00:00
|
|
|
zap.Int("unknowns", len(unknowns)),
|
2019-06-07 13:38:41 +01:00
|
|
|
zap.Int("offlines", len(offlines)),
|
|
|
|
zap.Int("pending", len(pendingAudits)),
|
|
|
|
)
|
|
|
|
|
2019-05-23 21:07:19 +01:00
|
|
|
var errlist errs.Group
|
2021-11-08 20:51:04 +00:00
|
|
|
nodesReputation := req.NodesReputation
|
2018-12-19 18:44:03 +00:00
|
|
|
|
2019-06-21 16:10:03 +01:00
|
|
|
tries := 0
|
|
|
|
for tries <= reporter.maxRetries {
|
2020-03-09 15:35:54 +00:00
|
|
|
if len(successes) == 0 && len(fails) == 0 && len(unknowns) == 0 && len(offlines) == 0 && len(pendingAudits) == 0 {
|
2019-10-09 15:06:58 +01:00
|
|
|
return Report{}, nil
|
2018-10-16 18:40:34 +01:00
|
|
|
}
|
2018-12-19 18:44:03 +00:00
|
|
|
|
2019-05-23 21:07:19 +01:00
|
|
|
errlist = errs.Group{}
|
2018-12-19 18:44:03 +00:00
|
|
|
|
2022-04-11 17:47:14 +01:00
|
|
|
successes, err = reporter.recordAuditStatus(ctx, successes, nodesReputation, reputation.AuditSuccess)
|
|
|
|
errlist.Add(err)
|
|
|
|
fails, err = reporter.recordAuditStatus(ctx, fails, nodesReputation, reputation.AuditFailure)
|
|
|
|
errlist.Add(err)
|
|
|
|
unknowns, err = reporter.recordAuditStatus(ctx, unknowns, nodesReputation, reputation.AuditUnknown)
|
|
|
|
errlist.Add(err)
|
|
|
|
offlines, err = reporter.recordAuditStatus(ctx, offlines, nodesReputation, reputation.AuditOffline)
|
|
|
|
errlist.Add(err)
|
|
|
|
pendingAudits, err = reporter.recordPendingAudits(ctx, pendingAudits, nodesReputation)
|
|
|
|
errlist.Add(err)
|
2018-12-19 18:44:03 +00:00
|
|
|
|
2019-06-21 16:10:03 +01:00
|
|
|
tries++
|
2018-10-16 18:40:34 +01:00
|
|
|
}
|
2019-05-23 21:07:19 +01:00
|
|
|
|
|
|
|
err = errlist.Err()
|
2019-06-21 16:10:03 +01:00
|
|
|
if tries >= reporter.maxRetries && err != nil {
|
2019-10-09 15:06:58 +01:00
|
|
|
return Report{
|
2019-05-23 23:32:19 +01:00
|
|
|
Successes: successes,
|
|
|
|
Fails: fails,
|
|
|
|
Offlines: offlines,
|
2020-03-09 15:35:54 +00:00
|
|
|
Unknown: unknowns,
|
2019-05-23 23:32:19 +01:00
|
|
|
PendingAudits: pendingAudits,
|
2019-05-23 21:07:19 +01:00
|
|
|
}, errs.Combine(Error.New("some nodes failed to be updated in overlay"), err)
|
2018-10-16 18:40:34 +01:00
|
|
|
}
|
2019-10-09 15:06:58 +01:00
|
|
|
return Report{}, nil
|
2018-10-16 18:40:34 +01:00
|
|
|
}
|
|
|
|
|
2022-04-11 17:47:14 +01:00
|
|
|
func (reporter *reporter) recordAuditStatus(ctx context.Context, nodeIDs storj.NodeIDList, nodesReputation map[storj.NodeID]overlay.ReputationStatus, auditOutcome reputation.AuditType) (failed storj.NodeIDList, err error) {
|
2019-06-04 12:36:27 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
2020-10-22 22:02:48 +01:00
|
|
|
|
2022-04-11 17:47:14 +01:00
|
|
|
if len(nodeIDs) == 0 {
|
|
|
|
return nil, nil
|
2018-10-16 18:40:34 +01:00
|
|
|
}
|
2021-11-08 20:51:04 +00:00
|
|
|
var errors errs.Group
|
2022-04-11 17:47:14 +01:00
|
|
|
for _, nodeID := range nodeIDs {
|
|
|
|
err = reporter.reputations.ApplyAudit(ctx, nodeID, nodesReputation[nodeID], auditOutcome)
|
2021-06-23 00:09:39 +01:00
|
|
|
if err != nil {
|
|
|
|
failed = append(failed, nodeID)
|
2022-04-11 17:47:14 +01:00
|
|
|
errors.Add(Error.New("failed to record audit status %s in overlay for node %s: %w", auditOutcome.String(), nodeID.String(), err))
|
2019-05-23 21:07:19 +01:00
|
|
|
}
|
|
|
|
}
|
2021-11-08 20:51:04 +00:00
|
|
|
return failed, errors.Err()
|
2019-05-23 21:07:19 +01:00
|
|
|
}
|
|
|
|
|
2020-07-16 15:18:02 +01:00
|
|
|
// recordPendingAudits updates the containment status of nodes with pending audits.
|
2022-04-11 17:47:14 +01:00
|
|
|
func (reporter *reporter) recordPendingAudits(ctx context.Context, pendingAudits []*PendingAudit, nodesReputation map[storj.NodeID]overlay.ReputationStatus) (failed []*PendingAudit, err error) {
|
2019-06-04 12:36:27 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-05-23 21:07:19 +01:00
|
|
|
var errlist errs.Group
|
2019-07-31 18:21:06 +01:00
|
|
|
|
2019-05-23 21:07:19 +01:00
|
|
|
for _, pendingAudit := range pendingAudits {
|
2019-05-31 16:23:00 +01:00
|
|
|
if pendingAudit.ReverifyCount < reporter.maxReverifyCount {
|
2019-05-27 12:13:47 +01:00
|
|
|
err := reporter.containment.IncrementPending(ctx, pendingAudit)
|
|
|
|
if err != nil {
|
|
|
|
failed = append(failed, pendingAudit)
|
|
|
|
errlist.Add(err)
|
|
|
|
}
|
2021-03-09 22:05:37 +00:00
|
|
|
reporter.log.Info("Audit pending",
|
|
|
|
zap.Stringer("Piece ID", pendingAudit.PieceID),
|
|
|
|
zap.Stringer("Node ID", pendingAudit.NodeID))
|
2019-05-27 12:13:47 +01:00
|
|
|
} else {
|
|
|
|
// record failure -- max reverify count reached
|
2021-03-09 22:05:37 +00:00
|
|
|
reporter.log.Info("max reverify count reached (audit failed)", zap.Stringer("Node ID", pendingAudit.NodeID))
|
2021-11-08 20:51:04 +00:00
|
|
|
err = reporter.reputations.ApplyAudit(ctx, pendingAudit.NodeID, nodesReputation[pendingAudit.NodeID], reputation.AuditFailure)
|
2021-06-23 00:09:39 +01:00
|
|
|
if err != nil {
|
|
|
|
errlist.Add(err)
|
|
|
|
failed = append(failed, pendingAudit)
|
2021-08-18 18:21:52 +01:00
|
|
|
} else {
|
|
|
|
_, err = reporter.containment.Delete(ctx, pendingAudit.NodeID)
|
|
|
|
if err != nil && !ErrContainedNotFound.Has(err) {
|
|
|
|
errlist.Add(err)
|
|
|
|
}
|
2019-07-31 18:21:06 +01:00
|
|
|
}
|
|
|
|
}
|
2020-03-09 15:35:54 +00:00
|
|
|
}
|
2019-07-31 18:21:06 +01:00
|
|
|
|
2020-03-09 15:35:54 +00:00
|
|
|
if len(failed) > 0 {
|
|
|
|
for _, v := range failed {
|
2021-06-11 15:34:46 +01:00
|
|
|
reporter.log.Debug("failed to record Pending Nodes ",
|
|
|
|
zap.Stringer("NodeID", v.NodeID),
|
|
|
|
zap.String("Segment StreamID", v.StreamID.String()),
|
|
|
|
zap.Uint64("Segment Position", v.Position.Encode()))
|
2019-07-30 17:03:25 +01:00
|
|
|
}
|
2020-03-09 15:35:54 +00:00
|
|
|
return failed, errs.Combine(Error.New("failed to record some pending audits"), errlist.Err())
|
2018-12-19 18:44:03 +00:00
|
|
|
}
|
|
|
|
return nil, nil
|
2018-10-16 18:40:34 +01:00
|
|
|
}
|