carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package host
2
3import (
4 "context"
5 "os"
6 "sync"
7 "time"
8
9 "github.com/carlosframework/platform/internal/lease"
10 "github.com/carlosframework/platform/internal/moves"
11)
12
13// guardedMirror is the one path every staging write takes (moves spec §7.3,
14// model 14 and 18): each mutation of the target bucket calls Guarded.Before
15// and is signed at the instant the guard checked the staging lease, through
16// a lease.StagingWriter whose At methods sign once and never retry. A
17// caller that wants another attempt calls again and gets a new check and a
18// new signature.
19//
20// It is a lease.MirrorStore, a VersionedCopier and a VersionedPutter, so
21// lease.MirrorPrefixKeep runs over it in its versioned mode. Writes to any
22// other bucket are the source's own receipts and ledger in its own bucket:
23// they are not staging, need no guard, and go through own, the ordinary
24// mirror client. Reads go through the staging writer's ordinary reads.
25// Nothing here ever calls a lease.VersionedStore against the target bucket.
26type guardedMirror struct {
27 w lease.StagingWriter
28 g moves.Guarded
29 dst string // the target bucket: the only bucket the guard covers
30 own lease.MirrorStore // the source's own bucket: receipts and the ledger
31
32 // lastSign is the latest signing time the guard handed out through
33 // this mirror: the cut's seal bound (ruling G7) is computed from it.
34 mu sync.Mutex
35 lastSign time.Time
36}
37
38// before is Guarded.Before, recording the signing time it hands out.
39func (m *guardedMirror) before(fn func(signAt time.Time) error) error {
40 return m.g.Before(func(signAt time.Time) error {
41 m.mu.Lock()
42 if signAt.After(m.lastSign) {
43 m.lastSign = signAt
44 }
45 m.mu.Unlock()
46 return fn(signAt)
47 })
48}
49
50// lastSigned is the latest signing time handed out, or zero when none was:
51// a request signed then can land until lastSigned + moves.SignatureLife.
52func (m *guardedMirror) lastSigned() time.Time {
53 m.mu.Lock()
54 defer m.mu.Unlock()
55 return m.lastSign
56}
57
58var (
59 _ lease.MirrorStore = (*guardedMirror)(nil)
60 _ lease.VersionedCopier = (*guardedMirror)(nil)
61 _ lease.VersionedPutter = (*guardedMirror)(nil)
62)
63
64func (m *guardedMirror) List(ctx context.Context, bucket, prefix string) (map[string]lease.MirrorObject, error) {
65 return m.w.List(ctx, bucket, prefix)
66}
67
68func (m *guardedMirror) Get(ctx context.Context, bucket, key string) ([]byte, error) {
69 if bucket != m.dst {
70 return m.own.Get(ctx, bucket, key)
71 }
72 return m.w.Get(ctx, bucket, key)
73}
74
75func (m *guardedMirror) CopyVersioned(ctx context.Context, srcBucket, srcKey, srcETag, dstBucket, dstKey string) (etag, versionID string, err error) {
76 err = m.before(func(signAt time.Time) error {
77 var cerr error
78 etag, versionID, cerr = m.w.CopyVersionedAt(ctx, signAt, srcBucket, srcKey, srcETag, dstBucket, dstKey)
79 return cerr
80 })
81 return etag, versionID, err
82}
83
84// Copy is CopyVersioned without the version. MirrorPrefixKeep never calls
85// it on a VersionedCopier; it exists so the type is a MirrorStore.
86func (m *guardedMirror) Copy(ctx context.Context, srcBucket, srcKey, srcETag, dstBucket, dstKey string) (string, error) {
87 etag, _, err := m.CopyVersioned(ctx, srcBucket, srcKey, srcETag, dstBucket, dstKey)
88 return etag, err
89}
90
91func (m *guardedMirror) PutVersioned(ctx context.Context, bucket, key string, body []byte) (versionID string, err error) {
92 err = m.before(func(signAt time.Time) error {
93 var perr error
94 versionID, perr = m.w.PutVersionedAt(ctx, signAt, bucket, key, body)
95 return perr
96 })
97 return versionID, err
98}
99
100// PutFileVersioned stages the file at path (size bytes, sha256Hex) as key,
101// read from disk inside the guarded call: a retry opens it again under a
102// new check.
103func (m *guardedMirror) PutFileVersioned(ctx context.Context, bucket, key, path string, size int64, sha256Hex string) (versionID string, err error) {
104 err = m.before(func(signAt time.Time) error {
105 f, oerr := os.Open(path)
106 if oerr != nil {
107 return oerr
108 }
109 defer f.Close()
110 var perr error
111 versionID, perr = m.w.PutReaderVersionedAt(ctx, signAt, bucket, key, f, size, sha256Hex)
112 return perr
113 })
114 return versionID, err
115}
116
117// PutIfAbsentVersioned is move.json's create-only write; an existing object
118// is lease.ErrExists.
119func (m *guardedMirror) PutIfAbsentVersioned(ctx context.Context, bucket, key string, body []byte) (versionID string, err error) {
120 err = m.before(func(signAt time.Time) error {
121 var perr error
122 versionID, perr = m.w.PutIfAbsentVersionedAt(ctx, signAt, bucket, key, body)
123 return perr
124 })
125 return versionID, err
126}
127
128func (m *guardedMirror) Put(ctx context.Context, bucket, key string, body []byte) error {
129 if bucket != m.dst {
130 return m.own.Put(ctx, bucket, key, body)
131 }
132 _, err := m.PutVersioned(ctx, bucket, key, body)
133 return err
134}
135
136func (m *guardedMirror) Delete(ctx context.Context, bucket, key string) error {
137 if bucket != m.dst {
138 return m.own.Delete(ctx, bucket, key)
139 }
140 return m.before(func(signAt time.Time) error {
141 return m.w.DeleteAt(ctx, signAt, bucket, key)
142 })
143}
144