-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow pulling partial layer chains from an image #2795
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -116,6 +116,11 @@ func Image(ref string, opts ...ImageOption) State { | |
attrs[pb.AttrImageRecordType] = info.RecordType | ||
} | ||
|
||
if ll := info.layerLimit; ll != nil { | ||
attrs[pb.AttrImageLayerLimit] = strconv.FormatInt(int64(*ll), 10) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably worth setting this as a uint or just enforcing that it's not negative. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In |
||
addCap(&info.Constraints, pb.CapSourceImageLayerLimit) | ||
} | ||
|
||
src := NewSource("docker-image://"+ref, attrs, info.Constraints) // controversial | ||
if err != nil { | ||
src.err = err | ||
|
@@ -204,6 +209,7 @@ type ImageInfo struct { | |
metaResolver ImageMetaResolver | ||
resolveDigest bool | ||
resolveMode ResolveMode | ||
layerLimit *int | ||
RecordType string | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -152,17 +152,19 @@ type puller struct { | |
*pull.Puller | ||
} | ||
|
||
func mainManifestKey(ctx context.Context, desc ocispecs.Descriptor, platform ocispecs.Platform) (digest.Digest, error) { | ||
func mainManifestKey(ctx context.Context, desc ocispecs.Descriptor, platform ocispecs.Platform, layerLimit *int) (digest.Digest, error) { | ||
dt, err := json.Marshal(struct { | ||
Digest digest.Digest | ||
OS string | ||
Arch string | ||
Variant string `json:",omitempty"` | ||
Limit *int `json:",omitempty"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a nil Limit will be encoded as a json null value, so does that mean that anyone upgrading to a Buildkit with this feature will get cache misses on already pulled images due to the fact that the cache key changed here? Not exactly the end of the world if so, just making sure that's alright. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "omitempty" in here takes care of that. Key only changes if the limit is set. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh cool, didn't know that |
||
}{ | ||
Digest: desc.Digest, | ||
OS: platform.OS, | ||
Arch: platform.Architecture, | ||
Variant: platform.Variant, | ||
Limit: layerLimit, | ||
}) | ||
if err != nil { | ||
return "", err | ||
|
@@ -178,7 +180,7 @@ func (p *puller) CacheKey(ctx context.Context, g session.Group, index int) (cach | |
progressFactory := progress.FromContext(ctx) | ||
|
||
_, err = p.g.Do(ctx, "", func(ctx context.Context) (_ interface{}, err error) { | ||
if p.cacheKeyErr != nil || p.cacheKeyDone == true { | ||
if p.cacheKeyErr != nil || p.cacheKeyDone { | ||
return nil, p.cacheKeyErr | ||
} | ||
defer func() { | ||
|
@@ -203,6 +205,13 @@ func (p *puller) CacheKey(ctx context.Context, g session.Group, index int) (cach | |
return nil, err | ||
} | ||
|
||
if ll := p.id.LayerLimit; ll != nil { | ||
if *ll > len(p.manifest.Descriptors) { | ||
return nil, errors.Errorf("layer limit %d is greater than the number of layers in the image %d", *ll, len(p.manifest.Descriptors)) | ||
} | ||
p.manifest.Descriptors = p.manifest.Descriptors[:*ll] | ||
} | ||
|
||
if len(p.manifest.Descriptors) > 0 { | ||
progressController := &controller.Controller{ | ||
WriterFactory: progressFactory, | ||
|
@@ -233,7 +242,7 @@ func (p *puller) CacheKey(ctx context.Context, g session.Group, index int) (cach | |
} | ||
|
||
desc := p.manifest.MainManifestDesc | ||
k, err := mainManifestKey(ctx, desc, p.Platform) | ||
k, err := mainManifestKey(ctx, desc, p.Platform, p.id.LayerLimit) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
@@ -243,7 +252,11 @@ func (p *puller) CacheKey(ctx context.Context, g session.Group, index int) (cach | |
if err != nil { | ||
return nil, err | ||
} | ||
p.configKey = cacheKeyFromConfig(dt).String() | ||
ck, err := cacheKeyFromConfig(dt, p.id.LayerLimit) | ||
if err != nil { | ||
return nil, err | ||
} | ||
p.configKey = ck.String() | ||
p.cacheKeyDone = true | ||
return nil, nil | ||
}) | ||
|
@@ -336,16 +349,27 @@ func (p *puller) Snapshot(ctx context.Context, g session.Group) (ir cache.Immuta | |
|
||
// cacheKeyFromConfig returns a stable digest from image config. If image config | ||
// is a known oci image we will use chainID of layers. | ||
func cacheKeyFromConfig(dt []byte) digest.Digest { | ||
func cacheKeyFromConfig(dt []byte, layerLimit *int) (digest.Digest, error) { | ||
var img ocispecs.Image | ||
err := json.Unmarshal(dt, &img) | ||
if err != nil { | ||
return digest.FromBytes(dt) | ||
if layerLimit != nil { | ||
return "", errors.Wrap(err, "failed to parse image config") | ||
} | ||
return digest.FromBytes(dt), nil // digest of config | ||
} | ||
if layerLimit != nil { | ||
l := *layerLimit | ||
if len(img.RootFS.DiffIDs) < l { | ||
return "", errors.Errorf("image has %d layers, limit is %d", len(img.RootFS.DiffIDs), l) | ||
} | ||
img.RootFS.DiffIDs = img.RootFS.DiffIDs[:l] | ||
} | ||
if img.RootFS.Type != "layers" || len(img.RootFS.DiffIDs) == 0 { | ||
return "" | ||
return "", nil | ||
} | ||
return identity.ChainID(img.RootFS.DiffIDs) | ||
|
||
return identity.ChainID(img.RootFS.DiffIDs), nil | ||
} | ||
|
||
func oneOffProgress(ctx context.Context, id string) func(err error) error { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth adding a quick test in here that setting the limit to 0 doesn't break anything, also some of the other corner cases like that an error is returned when limit is greater than layers in the image.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added testcase