Skip to content

Commit 621760f

Browse files
committed
Add ReaderAt support to content store
Signed-off-by: Derek McGowan <derek@mcgstyle.net>
1 parent 9c0897d commit 621760f

File tree

5 files changed

+76
-7
lines changed

5 files changed

+76
-7
lines changed

content/content.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ var (
3030

3131
type Provider interface {
3232
Reader(ctx context.Context, dgst digest.Digest) (io.ReadCloser, error)
33+
ReaderAt(ctx context.Context, dgst digest.Digest) (io.ReaderAt, error)
3334
}
3435

3536
type Ingester interface {

content/readerat.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package content
2+
3+
import (
4+
"io"
5+
"os"
6+
)
7+
8+
// readerat implements io.ReaderAt in a completely stateless manner by opening
9+
// the referenced file for each call to ReadAt.
10+
type readerAt struct {
11+
f string
12+
}
13+
14+
func (ra readerAt) ReadAt(p []byte, offset int64) (int, error) {
15+
fp, err := os.Open(ra.f)
16+
if err != nil {
17+
return 0, err
18+
}
19+
defer fp.Close()
20+
21+
if _, err := fp.Seek(offset, io.SeekStart); err != nil {
22+
return 0, err
23+
}
24+
25+
return fp.Read(p)
26+
}

content/store.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,7 @@ func (s *store) info(dgst digest.Digest, fi os.FileInfo) Info {
5858
}
5959
}
6060

61-
// Open returns an io.ReadCloser for the blob.
62-
//
63-
// TODO(stevvooe): This would work much better as an io.ReaderAt in practice.
64-
// Right now, we are doing type assertion to tease that out, but it won't scale
65-
// well.
61+
// Reader returns an io.ReadCloser for the blob.
6662
func (s *store) Reader(ctx context.Context, dgst digest.Digest) (io.ReadCloser, error) {
6763
fp, err := os.Open(s.blobPath(dgst))
6864
if err != nil {
@@ -75,6 +71,11 @@ func (s *store) Reader(ctx context.Context, dgst digest.Digest) (io.ReadCloser,
7571
return fp, nil
7672
}
7773

74+
// ReaderAt returns an io.ReaderAt for the blob.
75+
func (s *store) ReaderAt(ctx context.Context, dgst digest.Digest) (io.ReaderAt, error) {
76+
return readerAt{f: s.blobPath(dgst)}, nil
77+
}
78+
7879
// Delete removes a blob by its digest.
7980
//
8081
// While this is safe to do concurrently, safe exist-removal logic must hold

services/content/reader.go

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package content
22

33
import (
4+
"context"
5+
46
contentapi "github.com/containerd/containerd/api/services/content"
7+
digest "github.com/opencontainers/go-digest"
58
)
69

710
type remoteReader struct {
@@ -42,8 +45,38 @@ func (rr *remoteReader) Read(p []byte) (n int, err error) {
4245
return
4346
}
4447

45-
// TODO(stevvooe): Implemente io.ReaderAt.
46-
4748
func (rr *remoteReader) Close() error {
4849
return rr.client.CloseSend()
4950
}
51+
52+
type remoteReaderAt struct {
53+
ctx context.Context
54+
digest digest.Digest
55+
client contentapi.ContentClient
56+
}
57+
58+
func (ra *remoteReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
59+
rr := &contentapi.ReadRequest{
60+
Digest: ra.digest,
61+
Offset: off,
62+
Size_: int64(len(p)),
63+
}
64+
rc, err := ra.client.Read(ra.ctx, rr)
65+
if err != nil {
66+
return 0, err
67+
}
68+
69+
for len(p) > 0 {
70+
var resp *contentapi.ReadResponse
71+
// fill our buffer up until we can fill p.
72+
resp, err = rc.Recv()
73+
if err != nil {
74+
return n, err
75+
}
76+
77+
copied := copy(p, resp.Data)
78+
n += copied
79+
p = p[copied:]
80+
}
81+
return n, nil
82+
}

services/content/store.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ func (rs *remoteStore) Reader(ctx context.Context, dgst digest.Digest) (io.ReadC
8585
}, nil
8686
}
8787

88+
func (rs *remoteStore) ReaderAt(ctx context.Context, dgst digest.Digest) (io.ReaderAt, error) {
89+
return &remoteReaderAt{
90+
ctx: ctx,
91+
digest: dgst,
92+
client: rs.client,
93+
}, nil
94+
}
95+
8896
func (rs *remoteStore) Status(ctx context.Context, re string) ([]content.Status, error) {
8997
resp, err := rs.client.Status(ctx, &contentapi.StatusRequest{
9098
Regexp: re,

0 commit comments

Comments
 (0)