Skip to content

Commit 65e78dc

Browse files
authored
Add partial.Manifests for lazy index access (#1631)
This allows us to manipulate an index that contains an image that contains a streaming layer.
1 parent 0577676 commit 65e78dc

File tree

4 files changed

+103
-44
lines changed

4 files changed

+103
-44
lines changed

cmd/crane/cmd/flatten.go

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -123,22 +123,13 @@ func push(flat partial.Describable, ref name.Reference, o crane.Options) error {
123123
return fmt.Errorf("can't push %T", flat)
124124
}
125125

126-
type remoteIndex interface {
127-
Manifests() ([]partial.Describable, error)
128-
}
129-
130126
func flattenIndex(old v1.ImageIndex, repo name.Repository, use string, o crane.Options) (partial.Describable, error) {
131-
ri, ok := old.(remoteIndex)
132-
if !ok {
133-
return nil, fmt.Errorf("unexpected index")
134-
}
135-
136127
m, err := old.IndexManifest()
137128
if err != nil {
138129
return nil, err
139130
}
140131

141-
manifests, err := ri.Manifests()
132+
manifests, err := partial.Manifests(old)
142133
if err != nil {
143134
return nil, err
144135
}

pkg/v1/mutate/index.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ package mutate
1616

1717
import (
1818
"encoding/json"
19+
"errors"
1920
"fmt"
2021
"sync"
2122

2223
"github.com/google/go-containerregistry/pkg/logs"
2324
v1 "github.com/google/go-containerregistry/pkg/v1"
2425
"github.com/google/go-containerregistry/pkg/v1/match"
2526
"github.com/google/go-containerregistry/pkg/v1/partial"
27+
"github.com/google/go-containerregistry/pkg/v1/stream"
2628
"github.com/google/go-containerregistry/pkg/v1/types"
2729
)
2830

@@ -208,3 +210,23 @@ func (i *index) RawManifest() ([]byte, error) {
208210
}
209211
return json.Marshal(i.manifest)
210212
}
213+
214+
func (i *index) Manifests() ([]partial.Describable, error) {
215+
if err := i.compute(); errors.Is(err, stream.ErrNotComputed) {
216+
// Index contains a streamable layer which has not yet been
217+
// consumed. Just return the manifests we have in case the caller
218+
// is going to consume the streamable layers.
219+
manifests, err := partial.Manifests(i.base)
220+
if err != nil {
221+
return nil, err
222+
}
223+
for _, add := range i.adds {
224+
manifests = append(manifests, add.Add)
225+
}
226+
return manifests, nil
227+
} else if err != nil {
228+
return nil, err
229+
}
230+
231+
return partial.ComputeManifests(i)
232+
}

pkg/v1/partial/index.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919

2020
v1 "github.com/google/go-containerregistry/pkg/v1"
2121
"github.com/google/go-containerregistry/pkg/v1/match"
22+
"github.com/google/go-containerregistry/pkg/v1/types"
2223
)
2324

2425
// FindManifests given a v1.ImageIndex, find the manifests that fit the matcher.
@@ -83,3 +84,82 @@ func FindIndexes(index v1.ImageIndex, matcher match.Matcher) ([]v1.ImageIndex, e
8384
}
8485
return matches, nil
8586
}
87+
88+
type withManifests interface {
89+
Manifests() ([]Describable, error)
90+
}
91+
92+
type withLayer interface {
93+
Layer(v1.Hash) (v1.Layer, error)
94+
}
95+
96+
type describable struct {
97+
desc v1.Descriptor
98+
}
99+
100+
func (d describable) Digest() (v1.Hash, error) {
101+
return d.desc.Digest, nil
102+
}
103+
104+
func (d describable) Size() (int64, error) {
105+
return d.desc.Size, nil
106+
}
107+
108+
func (d describable) MediaType() (types.MediaType, error) {
109+
return d.desc.MediaType, nil
110+
}
111+
112+
func (d describable) Descriptor() (*v1.Descriptor, error) {
113+
return &d.desc, nil
114+
}
115+
116+
// Manifests is analogous to v1.Image.Layers in that it allows values in the
117+
// returned list to be lazily evaluated, which enables an index to contain
118+
// an image that contains a streaming layer.
119+
//
120+
// This should have been part of the v1.ImageIndex interface, but wasn't.
121+
// It is instead usable through this extension interface.
122+
func Manifests(idx v1.ImageIndex) ([]Describable, error) {
123+
if wm, ok := idx.(withManifests); ok {
124+
return wm.Manifests()
125+
}
126+
127+
return ComputeManifests(idx)
128+
}
129+
130+
// ComputeManifests provides a fallback implementation for Manifests.
131+
func ComputeManifests(idx v1.ImageIndex) ([]Describable, error) {
132+
m, err := idx.IndexManifest()
133+
if err != nil {
134+
return nil, err
135+
}
136+
manifests := []Describable{}
137+
for _, desc := range m.Manifests {
138+
switch {
139+
case desc.MediaType.IsImage():
140+
img, err := idx.Image(desc.Digest)
141+
if err != nil {
142+
return nil, err
143+
}
144+
manifests = append(manifests, img)
145+
case desc.MediaType.IsIndex():
146+
idx, err := idx.ImageIndex(desc.Digest)
147+
if err != nil {
148+
return nil, err
149+
}
150+
manifests = append(manifests, idx)
151+
default:
152+
if wl, ok := idx.(withLayer); ok {
153+
layer, err := wl.Layer(desc.Digest)
154+
if err != nil {
155+
return nil, err
156+
}
157+
manifests = append(manifests, layer)
158+
} else {
159+
manifests = append(manifests, describable{desc})
160+
}
161+
}
162+
}
163+
164+
return manifests, nil
165+
}

pkg/v1/remote/index.go

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -148,40 +148,6 @@ func (r *remoteIndex) Layer(h v1.Hash) (v1.Layer, error) {
148148
return nil, fmt.Errorf("layer not found: %s", h)
149149
}
150150

151-
// Experiment with a better API for v1.ImageIndex. We might want to move this
152-
// to partial?
153-
func (r *remoteIndex) Manifests() ([]partial.Describable, error) {
154-
m, err := r.IndexManifest()
155-
if err != nil {
156-
return nil, err
157-
}
158-
manifests := []partial.Describable{}
159-
for _, desc := range m.Manifests {
160-
switch {
161-
case desc.MediaType.IsImage():
162-
img, err := r.Image(desc.Digest)
163-
if err != nil {
164-
return nil, err
165-
}
166-
manifests = append(manifests, img)
167-
case desc.MediaType.IsIndex():
168-
idx, err := r.ImageIndex(desc.Digest)
169-
if err != nil {
170-
return nil, err
171-
}
172-
manifests = append(manifests, idx)
173-
default:
174-
layer, err := r.Layer(desc.Digest)
175-
if err != nil {
176-
return nil, err
177-
}
178-
manifests = append(manifests, layer)
179-
}
180-
}
181-
182-
return manifests, nil
183-
}
184-
185151
func (r *remoteIndex) imageByPlatform(platform v1.Platform) (v1.Image, error) {
186152
desc, err := r.childByPlatform(platform)
187153
if err != nil {

0 commit comments

Comments
 (0)