Skip to content

Commit 4e32e4c

Browse files
authored
Merge pull request #8 from runloopai/gautam-tar-to-registry-export
Add tar to registry export
2 parents a20b305 + 561bad0 commit 4e32e4c

File tree

6 files changed

+187
-78
lines changed

6 files changed

+187
-78
lines changed

cmd/convertor/builder/builder.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ func (b *graphBuilder) process(ctx context.Context, src v1.Descriptor, tag bool)
164164
var wg sync.WaitGroup
165165
var mu sync.Mutex
166166
var filteredManifests []v1.Descriptor
167-
167+
168168
for _, m := range index.Manifests {
169169
manifest := m
170170
wg.Add(1)
@@ -187,7 +187,7 @@ func (b *graphBuilder) process(ctx context.Context, src v1.Descriptor, tag bool)
187187
if ctx.Err() != nil {
188188
return v1.Descriptor{}, ctx.Err()
189189
}
190-
190+
191191
// Update index with only the non-provenance manifests
192192
index.Manifests = filteredManifests
193193

@@ -328,7 +328,7 @@ func (b *graphBuilder) buildOne(ctx context.Context, src v1.Descriptor, tag bool
328328

329329
func Build(ctx context.Context, opt BuilderOptions) error {
330330
var resolver remotes.Resolver
331-
331+
332332
// Use custom resolver if provided, otherwise create default docker resolver
333333
if opt.CustomResolver != nil {
334334
log.G(ctx).Info("using custom resolver (tar import mode)")

cmd/convertor/builder/builder_utils_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ func Test_fetchManifest(t *testing.T) {
149149
// 2. Manifest lists (which require platform-specific manifest selection)
150150
if tt.args.desc.MediaType != images.MediaTypeDockerSchema2ManifestList &&
151151
tt.args.desc.MediaType != v1.MediaTypeImageIndex {
152-
152+
153153
// For regular manifests, we can directly compare the digest
154154
// because we expect to get back exactly what we asked for
155155
if tt.args.desc.Digest != contentDigest {

cmd/convertor/builder/content_store_resolver.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ func NewContentStoreResolverFromTar(ctx context.Context, tarPath string) (*Conte
9292
}
9393
defer tarFile.Close()
9494

95-
// Import tar into content store
95+
// Import tar into content store
9696
log.G(ctx).Infof("importing tar file: %s", tarPath)
9797
indexDesc, err := archive.ImportIndex(ctx, store, tarFile,
9898
archive.WithImportCompression(),
@@ -119,18 +119,18 @@ func NewContentStoreResolverFromTar(ctx context.Context, tarPath string) (*Conte
119119
log.G(ctx).Infof(" MediaType: %s", manifest.MediaType)
120120
log.G(ctx).Infof(" Digest: %s", manifest.Digest)
121121
log.G(ctx).Infof(" Size: %d bytes", manifest.Size)
122-
122+
123123
if manifest.Platform != nil {
124124
log.G(ctx).Infof(" Platform: %s/%s", manifest.Platform.OS, manifest.Platform.Architecture)
125125
if manifest.Platform.Variant != "" {
126126
log.G(ctx).Infof(" Platform Variant: %s", manifest.Platform.Variant)
127127
}
128128
}
129-
129+
130130
if manifest.ArtifactType != "" {
131131
log.G(ctx).Infof(" ArtifactType: %s", manifest.ArtifactType)
132132
}
133-
133+
134134
if manifest.Annotations != nil && len(manifest.Annotations) > 0 {
135135
log.G(ctx).Infof(" Annotations:")
136136
for key, value := range manifest.Annotations {
@@ -144,10 +144,10 @@ func NewContentStoreResolverFromTar(ctx context.Context, tarPath string) (*Conte
144144
continue
145145
}
146146

147-
// Check if this is an image index (multi-arch)
147+
// Check if this is an image index (multi-arch)
148148
if manifest.MediaType == v1.MediaTypeImageIndex || manifest.MediaType == "application/vnd.docker.distribution.manifest.list.v2+json" {
149149
log.G(ctx).Infof(" 📁 FOUND IMAGE INDEX: Importing as-is for multi-arch conversion")
150-
150+
151151
// Import the index itself - let the builder handle platform traversal
152152
ref := fmt.Sprintf("imported:%s", manifest.Digest.Encoded()[:12])
153153
if manifest.Annotations != nil {
@@ -194,7 +194,7 @@ func (r *ContentStoreResolver) Resolve(ctx context.Context, ref string) (string,
194194

195195
// Parse reference
196196
name, tag := parseRef(ref)
197-
197+
198198
// Look up in image store
199199
image, err := r.imageStore.Get(ctx, name)
200200
if err != nil {
@@ -455,7 +455,7 @@ func isProvenanceManifest(desc v1.Descriptor) bool {
455455
// by examining both the descriptor metadata and the actual manifest content
456456
func isProvenanceManifestWithContent(ctx context.Context, store content.Store, desc v1.Descriptor) bool {
457457
log.G(ctx).Debugf(" 🔍 Checking if manifest is provenance: %s", desc.Digest)
458-
458+
459459
// First check descriptor metadata
460460
if isProvenanceManifest(desc) {
461461
log.G(ctx).Debugf(" 🔍 Detected provenance via descriptor metadata")
@@ -465,9 +465,9 @@ func isProvenanceManifestWithContent(ctx context.Context, store content.Store, d
465465
// For generic manifest media types, we need to check the content
466466
if desc.MediaType == "application/vnd.docker.distribution.manifest.v2+json" ||
467467
desc.MediaType == "application/vnd.oci.image.manifest.v1+json" {
468-
468+
469469
log.G(ctx).Debugf(" 🔍 Generic manifest media type, reading content to inspect...")
470-
470+
471471
// Read the manifest content
472472
manifestContent, err := content.ReadBlob(ctx, store, desc)
473473
if err != nil {
@@ -492,11 +492,11 @@ func isProvenanceManifestWithContent(ctx context.Context, store content.Store, d
492492
log.G(ctx).Debugf(" 🔍 FOUND provenance markers in content!")
493493
return true
494494
}
495-
495+
496496
log.G(ctx).Debugf(" 🔍 No provenance markers found in content")
497497
} else {
498498
log.G(ctx).Debugf(" 🔍 Non-generic media type, skipping content inspection")
499499
}
500500

501501
return false
502-
}
502+
}

cmd/convertor/builder/file_pusher.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ import (
3636
// FileBasedResolver implements remotes.Resolver that captures pushed content locally
3737
// for later export to tar files
3838
type FileBasedResolver struct {
39-
store content.Store
40-
imageStore images.Store
41-
outputStore content.Store // Where converted layers are stored
39+
store content.Store
40+
imageStore images.Store
41+
outputStore content.Store // Where converted layers are stored
4242
outputImageStore images.Store
43-
tempDir string // Path to temporary directory for cleanup
43+
tempDir string // Path to temporary directory for cleanup
4444
}
4545

4646
// NewFileBasedResolver creates a resolver that captures converted content locally
@@ -94,7 +94,7 @@ func (r *FileBasedResolver) OutputImageStore() images.Store {
9494
// Resolve resolves a reference from the import store
9595
func (r *FileBasedResolver) Resolve(ctx context.Context, ref string) (string, v1.Descriptor, error) {
9696
log.G(ctx).Debugf("file-based resolver: resolving reference: %s", ref)
97-
97+
9898
// Look up in import image store
9999
image, err := r.imageStore.Get(ctx, ref)
100100
if err != nil {
@@ -114,7 +114,7 @@ func (r *FileBasedResolver) Fetcher(ctx context.Context, ref string) (remotes.Fe
114114
// Pusher returns a file-based pusher that captures converted content locally
115115
func (r *FileBasedResolver) Pusher(ctx context.Context, ref string) (remotes.Pusher, error) {
116116
log.G(ctx).Debugf("file-based resolver: creating pusher for ref: %s", ref)
117-
117+
118118
return &FilePusher{
119119
ref: ref,
120120
store: r.outputStore,
@@ -167,7 +167,7 @@ type fileWriter struct {
167167

168168
func (w *fileWriter) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...content.Opt) error {
169169
log.G(ctx).Debugf("file pusher: committing blob: %s", w.desc.Digest)
170-
170+
171171
err := w.Writer.Commit(ctx, size, expected, opts...)
172172
if err != nil {
173173
return err
@@ -176,7 +176,7 @@ func (w *fileWriter) Commit(ctx context.Context, size int64, expected digest.Dig
176176
// If this is a manifest, store it in the image store as well
177177
if isManifestMediaType(w.desc.MediaType) {
178178
log.G(ctx).Debugf("file pusher: storing manifest in image store: %s", w.desc.Digest)
179-
179+
180180
// Generate image name based on the pusher's reference
181181
imageName := fmt.Sprintf("converted:%s", w.desc.Digest.Encoded()[:12])
182182
if w.pusher.ref != "" {
@@ -195,7 +195,7 @@ func (w *fileWriter) Commit(ctx context.Context, size int64, expected digest.Dig
195195
CreatedAt: time.Now(),
196196
UpdatedAt: time.Now(),
197197
}
198-
198+
199199
_, err = w.pusher.imageStore.Create(ctx, image)
200200
if err != nil && !errdefs.IsAlreadyExists(err) {
201201
log.G(ctx).Warnf("failed to store image in image store: %v", err)
@@ -215,7 +215,7 @@ func isManifestMediaType(mediaType string) bool {
215215
v1.MediaTypeImageIndex,
216216
"application/vnd.docker.distribution.manifest.list.v2+json",
217217
}
218-
218+
219219
for _, mt := range manifestTypes {
220220
if mediaType == mt {
221221
return true
@@ -233,7 +233,7 @@ func (r *FileBasedResolver) CleanupTempDir() error {
233233
return nil
234234
}
235235

236-
// GetTempDir returns the temporary directory path for debugging
236+
// GetTempDir returns the temporary directory path for debugging
237237
func (r *FileBasedResolver) GetTempDir() string {
238238
return r.tempDir
239-
}
239+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
Copyright The Accelerated Container Image Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package builder
18+
19+
import (
20+
"context"
21+
22+
"github.com/containerd/containerd/v2/core/content"
23+
"github.com/containerd/containerd/v2/core/images"
24+
"github.com/containerd/containerd/v2/core/remotes"
25+
"github.com/containerd/log"
26+
v1 "github.com/opencontainers/image-spec/specs-go/v1"
27+
)
28+
29+
// RegistryExportResolver implements remotes.Resolver that:
30+
// - Fetches from import content store (tar)
31+
// - Pushes to registry using a registry resolver
32+
type RegistryExportResolver struct {
33+
store content.Store
34+
imageStore images.Store
35+
registryResolver remotes.Resolver // For creating registry pushers
36+
}
37+
38+
// NewRegistryExportResolver creates a resolver for tar import -> registry export
39+
func NewRegistryExportResolver(importStore content.Store, importImageStore images.Store, registryResolver remotes.Resolver) *RegistryExportResolver {
40+
return &RegistryExportResolver{
41+
store: importStore,
42+
imageStore: importImageStore,
43+
registryResolver: registryResolver,
44+
}
45+
}
46+
47+
// Resolve resolves a reference from the import store
48+
func (r *RegistryExportResolver) Resolve(ctx context.Context, ref string) (string, v1.Descriptor, error) {
49+
log.G(ctx).Debugf("registry export resolver: resolving reference: %s", ref)
50+
51+
// Look up in import image store
52+
image, err := r.imageStore.Get(ctx, ref)
53+
if err != nil {
54+
return "", v1.Descriptor{}, err
55+
}
56+
57+
return ref, image.Target, nil
58+
}
59+
60+
// Fetcher returns a fetcher for the import content store
61+
func (r *RegistryExportResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetcher, error) {
62+
return &ContentStoreFetcher{
63+
store: r.store,
64+
}, nil
65+
}
66+
67+
// Pusher returns a registry pusher from the registry resolver
68+
func (r *RegistryExportResolver) Pusher(ctx context.Context, ref string) (remotes.Pusher, error) {
69+
log.G(ctx).Debugf("registry export resolver: creating registry pusher for ref: %s", ref)
70+
return r.registryResolver.Pusher(ctx, ref)
71+
}

0 commit comments

Comments
 (0)