Skip to content

Commit f450f5a

Browse files
authored
Merge pull request #27 from stacklok/oci-skills-replace-store-with-oras
Replace custom Store with ORAS oci.Store
2 parents 0855b19 + 8fac0c9 commit f450f5a

4 files changed

Lines changed: 105 additions & 360 deletions

File tree

oci/skills/registry.go

Lines changed: 4 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"encoding/json"
1010
"fmt"
1111
"io"
12-
"strings"
1312

1413
"github.com/opencontainers/go-digest"
1514
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
@@ -35,7 +34,6 @@ const maxManifestLayers = 64
3534
// Compile-time interface checks.
3635
var (
3736
_ RegistryClient = (*Registry)(nil)
38-
_ oras.Target = (*storeAdapter)(nil)
3937
_ oras.Target = (*validatingTarget)(nil)
4038
)
4139

@@ -100,10 +98,8 @@ func (r *Registry) Push(ctx context.Context, store *Store, artifactDigest digest
10098
return err
10199
}
102100

103-
adapter := newStoreAdapter(store)
104-
105-
// Resolve the artifact to get its descriptor for oras.CopyGraph
106-
desc, err := adapter.descriptorFromDigest(ctx, artifactDigest)
101+
// Resolve the artifact to get its full descriptor from the OCI store.
102+
desc, err := store.Target().Resolve(ctx, artifactDigest.String())
107103
if err != nil {
108104
return fmt.Errorf("resolving artifact descriptor: %w", err)
109105
}
@@ -114,7 +110,7 @@ func (r *Registry) Push(ctx context.Context, store *Store, artifactDigest digest
114110
}
115111

116112
// Copy the content graph (blobs → manifests → index) to the remote
117-
if err := oras.CopyGraph(ctx, adapter, target, desc, oras.DefaultCopyGraphOptions); err != nil {
113+
if err := oras.CopyGraph(ctx, store.Target(), target, desc, oras.DefaultCopyGraphOptions); err != nil {
118114
return fmt.Errorf("pushing to registry: %w", err)
119115
}
120116

@@ -139,8 +135,7 @@ func (r *Registry) Pull(ctx context.Context, store *Store, ref string) (digest.D
139135
return "", fmt.Errorf("getting repository: %w", err)
140136
}
141137

142-
adapter := newStoreAdapter(store)
143-
validated := newValidatingTarget(adapter)
138+
validated := newValidatingTarget(store.Target())
144139

145140
// Copy from remote to the validated local store
146141
desc, err := oras.Copy(
@@ -159,114 +154,6 @@ func (r *Registry) Pull(ctx context.Context, store *Store, ref string) (digest.D
159154
return desc.Digest, nil
160155
}
161156

162-
// storeAdapter wraps a Store to satisfy ORAS content interfaces,
163-
// routing content by media type to the appropriate Store methods.
164-
type storeAdapter struct {
165-
store *Store
166-
}
167-
168-
func newStoreAdapter(store *Store) *storeAdapter {
169-
return &storeAdapter{store: store}
170-
}
171-
172-
// Push routes content to PutManifest or PutBlob based on media type.
173-
// Verifies the content digest matches the expected descriptor.
174-
func (a *storeAdapter) Push(ctx context.Context, expected ocispec.Descriptor, content io.Reader) error {
175-
data, err := io.ReadAll(io.LimitReader(content, MaxBlobSize+1))
176-
if err != nil {
177-
return fmt.Errorf("reading content: %w", err)
178-
}
179-
if int64(len(data)) > MaxBlobSize {
180-
return fmt.Errorf("content exceeds maximum size of %d bytes", MaxBlobSize)
181-
}
182-
183-
// Verify digest integrity
184-
actual := digest.FromBytes(data)
185-
if actual != expected.Digest {
186-
return fmt.Errorf("digest mismatch: expected %s, got %s", expected.Digest, actual)
187-
}
188-
189-
if isManifestMediaType(expected.MediaType) {
190-
_, err = a.store.PutManifest(ctx, data)
191-
} else {
192-
_, err = a.store.PutBlob(ctx, data)
193-
}
194-
return err
195-
}
196-
197-
// Fetch retrieves content from the store by descriptor.
198-
func (a *storeAdapter) Fetch(ctx context.Context, target ocispec.Descriptor) (io.ReadCloser, error) {
199-
var data []byte
200-
var err error
201-
202-
if isManifestMediaType(target.MediaType) {
203-
data, err = a.store.GetManifest(ctx, target.Digest)
204-
} else {
205-
data, err = a.store.GetBlob(ctx, target.Digest)
206-
}
207-
if err != nil {
208-
return nil, err
209-
}
210-
return io.NopCloser(bytes.NewReader(data)), nil
211-
}
212-
213-
// Exists checks whether content exists in the store.
214-
func (a *storeAdapter) Exists(ctx context.Context, target ocispec.Descriptor) (bool, error) {
215-
if isManifestMediaType(target.MediaType) {
216-
_, err := a.store.GetManifest(ctx, target.Digest)
217-
if err != nil {
218-
if strings.Contains(err.Error(), "not found") {
219-
return false, nil
220-
}
221-
return false, err
222-
}
223-
return true, nil
224-
}
225-
_, err := a.store.GetBlob(ctx, target.Digest)
226-
if err != nil {
227-
if strings.Contains(err.Error(), "not found") {
228-
return false, nil
229-
}
230-
return false, err
231-
}
232-
return true, nil
233-
}
234-
235-
// Resolve resolves a reference (tag or digest) to a full descriptor.
236-
func (a *storeAdapter) Resolve(ctx context.Context, reference string) (ocispec.Descriptor, error) {
237-
d, err := a.store.Resolve(ctx, reference)
238-
if err != nil {
239-
return ocispec.Descriptor{}, err
240-
}
241-
return a.descriptorFromDigest(ctx, d)
242-
}
243-
244-
// Tag associates a reference with a descriptor in the store.
245-
func (a *storeAdapter) Tag(ctx context.Context, desc ocispec.Descriptor, reference string) error {
246-
return a.store.Tag(ctx, desc.Digest, reference)
247-
}
248-
249-
// descriptorFromDigest reads content to build a full OCI descriptor.
250-
func (a *storeAdapter) descriptorFromDigest(ctx context.Context, d digest.Digest) (ocispec.Descriptor, error) {
251-
data, err := a.store.GetManifest(ctx, d)
252-
if err != nil {
253-
return ocispec.Descriptor{}, fmt.Errorf("reading content for descriptor: %w", err)
254-
}
255-
256-
var header struct {
257-
MediaType string `json:"mediaType"`
258-
}
259-
if err := json.Unmarshal(data, &header); err != nil {
260-
return ocispec.Descriptor{}, fmt.Errorf("parsing media type: %w", err)
261-
}
262-
263-
return ocispec.Descriptor{
264-
MediaType: header.MediaType,
265-
Digest: d,
266-
Size: int64(len(data)),
267-
}, nil
268-
}
269-
270157
// validatingTarget wraps an oras.Target to enforce size and count limits
271158
// on pushed content. This prevents OOM and resource exhaustion from
272159
// malicious registries during pull operations.

oci/skills/registry_test.go

Lines changed: 0 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -212,83 +212,6 @@ func TestValidateManifestCounts(t *testing.T) {
212212
})
213213
}
214214

215-
// --- storeAdapter tests ---
216-
217-
func TestStoreAdapter_PushFetchRoundTrip(t *testing.T) {
218-
t.Parallel()
219-
220-
ctx := t.Context()
221-
store, err := NewStore(t.TempDir())
222-
require.NoError(t, err)
223-
adapter := newStoreAdapter(store)
224-
225-
// Push a blob
226-
blobContent := []byte("test blob data")
227-
blobDesc := ocispec.Descriptor{
228-
MediaType: "application/octet-stream",
229-
Digest: digest.FromBytes(blobContent),
230-
Size: int64(len(blobContent)),
231-
}
232-
err = adapter.Push(ctx, blobDesc, bytes.NewReader(blobContent))
233-
require.NoError(t, err)
234-
235-
// Fetch it back
236-
rc, err := adapter.Fetch(ctx, blobDesc)
237-
require.NoError(t, err)
238-
defer rc.Close()
239-
var buf bytes.Buffer
240-
_, err = buf.ReadFrom(rc)
241-
require.NoError(t, err)
242-
assert.Equal(t, blobContent, buf.Bytes())
243-
244-
// Exists
245-
exists, err := adapter.Exists(ctx, blobDesc)
246-
require.NoError(t, err)
247-
assert.True(t, exists)
248-
249-
// Non-existent
250-
fakeDesc := ocispec.Descriptor{
251-
MediaType: "application/octet-stream",
252-
Digest: digest.FromString("fake"),
253-
Size: 4,
254-
}
255-
exists, err = adapter.Exists(ctx, fakeDesc)
256-
require.NoError(t, err)
257-
assert.False(t, exists)
258-
}
259-
260-
func TestStoreAdapter_ResolveAndTag(t *testing.T) {
261-
t.Parallel()
262-
263-
ctx := t.Context()
264-
store, err := NewStore(t.TempDir())
265-
require.NoError(t, err)
266-
adapter := newStoreAdapter(store)
267-
268-
// Build and store a manifest
269-
manifest := ocispec.Manifest{MediaType: ocispec.MediaTypeImageManifest}
270-
manifestBytes, err := json.Marshal(manifest)
271-
require.NoError(t, err)
272-
273-
manifestDigest, err := store.PutManifest(ctx, manifestBytes)
274-
require.NoError(t, err)
275-
276-
// Tag via adapter
277-
desc := ocispec.Descriptor{
278-
MediaType: ocispec.MediaTypeImageManifest,
279-
Digest: manifestDigest,
280-
Size: int64(len(manifestBytes)),
281-
}
282-
err = adapter.Tag(ctx, desc, "my-tag")
283-
require.NoError(t, err)
284-
285-
// Resolve via adapter
286-
resolved, err := adapter.Resolve(ctx, "my-tag")
287-
require.NoError(t, err)
288-
assert.Equal(t, manifestDigest, resolved.Digest)
289-
assert.Equal(t, ocispec.MediaTypeImageManifest, resolved.MediaType)
290-
}
291-
292215
// --- Integration tests using in-memory target ---
293216

294217
func newTestRegistry(t *testing.T, remoteStore *memory.Store) *Registry {

0 commit comments

Comments
 (0)