carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package hostbox
2
3import (
4 "context"
5 "crypto/sha256"
6 "encoding/hex"
7 "encoding/json"
8 "github.com/carlosframework/platform/internal/hostguard"
9 "math"
10 "slices"
11)
12
13type ProducerCompletion struct {
14 Count uint64
15 Digest string
16}
17
18func (c ProducerCompletion) Valid() bool {
19 if c.Count == 0 {
20 return c.Digest == ""
21 }
22 b, err := hex.DecodeString(c.Digest)
23 return err == nil && len(b) == sha256.Size
24}
25
26// CompleteProducer atomically removes a joined producer and extends its durable
27// completion chain. The previous value permits replay after a lost commit reply.
28func (j *LaunchJournal) CompleteProducer(ctx context.Context, t hostguard.FenceTarget, id string, previous ProducerCompletion) (ProducerCompletion, error) {
29 if j == nil {
30 return previous, ErrUnsupported
31 }
32 if j.remote != nil {
33 out, err := journalCall(ctx, j.remote, JournalRequest{Op: "complete", Target: t, Producer: id, Previous: previous})
34 return out.Completion, err
35 }
36
37 if !component(id) || !previous.Valid() || previous.Count == math.MaxUint64 {
38 return previous, ErrTypedInventory
39 }
40 raw, _ := json.Marshal(struct {
41 Previous ProducerCompletion
42 ID string
43 }{previous, id})
44 digest := sha256.Sum256(raw)
45 next := ProducerCompletion{Count: previous.Count + 1, Digest: hex.EncodeToString(digest[:])}
46 err := j.mutate(ctx, t, func(in *LaunchInventory) error {
47 i := slices.Index(in.ProducerIDs, id)
48 if i < 0 && in.Completed == next {
49 return nil
50 }
51 if i < 0 || in.Completed != previous {
52 return ErrChanged
53 }
54 in.ProducerIDs = slices.Delete(in.ProducerIDs, i, i+1)
55 in.Completed = next
56 return nil
57 })
58 if err != nil {
59 return previous, err
60 }
61 return next, nil
62}
63
64func (j *LaunchJournal) RecordProducer(ctx context.Context, t hostguard.FenceTarget, id string) error {
65 if j == nil {
66 return ErrUnsupported
67 }
68 if j.remote != nil {
69 out, err := journalCall(ctx, j.remote, JournalRequest{Op: "producer", Target: t, Producer: id})
70 _ = out
71 return err
72 }
73
74 if !component(id) {
75 return ErrTypedInventory
76 }
77 return j.mutate(ctx, t, func(in *LaunchInventory) error {
78 for _, old := range in.ProducerIDs {
79 if old == id {
80 return ErrTypedInventory
81 }
82 }
83 in.ProducerIDs = append(in.ProducerIDs, id)
84 return nil
85 })
86}
87