1package replica
2
3import (
4 "context"
5 "errors"
6 "time"
7
8 "github.com/go-git/go-git/v5"
9 "github.com/go-git/go-git/v5/plumbing"
10 "github.com/go-git/go-git/v5/plumbing/object"
11)
12
13const dailyInterval = 24 * time.Hour
14
15var ErrNoDigest = errors.New("replica: start-up needs a digest function for committed exports")
16
17type StartInput struct {
18 Repo *git.Repository
19 Dir string
20 Checkpoints Checkpoints
21 Primary Remote
22 Digest func([]byte) ([32]byte, error)
23 Current [32]byte
24}
25
26type StartPlan struct {
27 StepNow, Pending bool
28 CheckpointUnreadable bool
29 Committed [32]byte
30 DailyDue time.Time
31}
32
33func PlanStart(ctx context.Context, in StartInput) (StartPlan, error) {
34 if in.Digest == nil {
35 return StartPlan{}, ErrNoDigest
36 }
37 k, err := in.Checkpoints.ReadCheckpoint(ctx)
38 if err != nil {
39 return StartPlan{}, err
40 }
41 if err := ensureCheckpoint(ctx, in.Repo, in.Dir, in.Primary, k); err != nil {
42 return StartPlan{}, err
43 }
44 local, err := localHead(in.Repo)
45 if err != nil {
46 return StartPlan{}, err
47 }
48 if !k.Set {
49 if local.IsZero() {
50 return StartPlan{Pending: true}, nil
51 }
52 return StartPlan{StepNow: true}, nil
53 }
54 if local.String() != k.Value {
55 return StartPlan{StepNow: true}, nil
56 }
57 id := plumbing.NewHash(k.Value)
58 c, err := object.GetCommit(in.Repo.Storer, id)
59 if err != nil {
60 return StartPlan{}, err
61 }
62 blob, err := ReadFile(in.Repo.Storer, id)
63 if err != nil {
64 return StartPlan{}, err
65 }
66 dailyDue := c.Committer.When.UTC().Add(dailyInterval)
67 committed, err := in.Digest(blob)
68 if err != nil {
69 return StartPlan{StepNow: true, CheckpointUnreadable: true, DailyDue: dailyDue}, nil
70 }
71 return StartPlan{
72 Pending: committed != in.Current,
73 Committed: committed,
74 DailyDue: dailyDue,
75 }, nil
76}