Skip to content
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

Import/export support for OCI compatible image manifest version of cache manifest (opt-in on export, inferred on import) #3724

Merged
merged 2 commits into from
May 13, 2023

Conversation

kattmang
Copy link
Contributor

Import/export support for OCI compatible image manifest version of cache manifest (opt-in on export, inferred on import)

This feature addition is an extension to the remote and local cache feature. This generally follows @joadrp's Proposal in #2251 while also taking in Tonis' comment on thread regarding an opt-in key for exporting with this new cache manifest format. #2251 (comment)

To summarize, this commit adds:

  • README change detailing the new export-cache key for both local and registry types
  • added logic to plumb through image-manifest bool key with default False (opt-in) to the contentCacheExporter.
  • added export contentCacheImporter logic to support both the new Image Manifest approach as well as the existing Image Index approach based on above key
  • added import cache manifest inference logic to support and detect both the old and new cache manifest format (also drops and errors on when it can't be inferred).

Tested to both local and registry, and tested for regression to the old manifest type, using ECR as the registry destination (At ECR we do NOT support the Image Index format as it isn't strict to the OCI spec for valid mediaTypes in the manifest list).

Open to additional testing suggestions.

@kattmang
Copy link
Contributor Author

Also, looking for maintainer feedback on how we can get this change into the next upcoming release branch for Buildkit/Docker CLI support.

@kattmang kattmang marked this pull request as draft March 17, 2023 06:38
@kattmang
Copy link
Contributor Author

kattmang commented Mar 17, 2023

Converted to draft as I see some integ tests failures on tests I wasn't able to test locally due to work network limitations.

EDIT: error appears to be for all/most to be related to cache manifest push tests (on the old image index format):

x.go:281: time="2023-03-17T05:41:55Z" level=debug msg="fetch response received" response.header.content-length=82 response.header.content-type="application/json; charset=utf-8" response.header.date="Fri, 17 Mar 2023 05:41:55 GMT" response.header.docker-distribution-api-version=registry/2.0 response.status="400 Bad Request" spanID=42abbcfb23d7bac7 traceID=193289b2c6357960aab7bacaa62a7bb2 url="http://localhost:46109/v2/buildkit/buildkit/testdiffself-cache/manifests/latest"
2023-03-17T05:43:20.8581033Z     sandbox.go:281: time="2023-03-17T05:41:55Z" level=debug msg="unexpected response" body="{\"errors\":[{\"code\":\"MANIFEST_INVALID\",\"message\":\"manifest invalid\",\"detail\":{}}]}\n" resp="&{400 Bad Request 400 HTTP/1.1 1 1 map[Content-Length:[82] Content-Type:[application/json; charset=utf-8] Date:[Fri, 17 Mar 2023 05:41:55 GMT] Docker-Distribution-Api-Version:[registry/2.0]] {0xc00021cdc0} 82 [] false false map[] 0xc00021f700 <nil>}" spanID=42abbcfb23d7bac7 traceID=193289b2c6357960aab7bacaa62a7bb2
2023-03-17T05:43:20.8582004Z     sandbox.go:281: time="2023-03-17T05:41:55Z" level=error msg="/moby.buildkit.v1.Control/Solve returned error: rpc error: code = Unknown desc = error writing manifest blob: failed commit on ref \"sha256:cb8f036590ad3c123d37457ab73199281279b099405d7ce2f33a93bf6a3600ed\": unexpected status from PUT request to http://localhost:46109/v2/buildkit/buildkit/testdiffself-cache/manifests/latest: 400 Bad Request"

Image Index cache manifest works fine on my box to my local cache with the PR commit

docker run \
    -it \
    --rm \
    --privileged \
    -v /home/kangmatt/buildctl-test:/tmp \
    -v $HOME/.docker:/root/.docker \
    -v /home/kangmatt/.aws:/root/.aws \
    --entrypoint buildctl-daemonless.sh \
    ecr-buildkit:local \
    --debug build \
    --frontend=dockerfile.v0 \
    --local context=/tmp \
    --local dockerfile=/tmp \
    --output type=image,name=010500926215.dkr.ecr.us-west-2.amazonaws.com/buildkit-cache:image,push=true \
    --import-cache type=local,src=/tmp/bkcache3 \
      --export-cache type=local,dest=/tmp/bkcache3
DEBU[2023-03-17T07:38:33Z] serving grpc connection                       spanID=cbf32771e6c1da9f traceID=e0acf1b700a4dab25577a75c9e97c368
[+] Building 2.7s (10/11)
 => => transferring dockerfile: 102B                                                                                                                 0.0s
 => [internal] load metadata for docker.io/library/alpine:3.4                                                                                        1.4s
 => [internal] load .dockerignore                                                                                                                    0.0s
 => => transferring context: 2B                                                                                                                      0.0s
 => importing cache manifest from local:16548731276217466773                                                                                         0.0s
 => => inferred cache manifest type: Manifest List                                                                                                   0.0s
 => [1/4] FROM docker.io/library/alpine:3.4@sha256:b733d4a32c4da6a00a84df2ca32791bb03df95400243648d8c539e7b4cce329c                                  0.0s
 => => resolve docker.io/library/alpine:3.4@sha256:b733d4a32c4da6a00a84df2ca32791bb03df95400243648d8c539e7b4cce329c                                  0.0s
 => CACHED [2/4] RUN apk update                                                                                                                      0.0s
 => CACHED [3/4] RUN apk add vim                                                                                                                     0.0s
 => CACHED [4/4] RUN apk add curl                                                                                                                    1.0s
 => => sha256:c1e54eec4b5786500c19795d1fc604aa7302aee307edfe0554a5c07108b77d48 2.39MB / 2.39MB                                                       0.0s
 => => extracting sha256:c1e54eec4b5786500c19795d1fc604aa7302aee307edfe0554a5c07108b77d48                                                            0.1s
 => => sha256:ce0647ccd38e3c1f8dcd3726a16b73d221486587ecb489433dd78991bd913662 769.08kB / 769.08kB                                                   0.0s
 => => extracting sha256:ce0647ccd38e3c1f8dcd3726a16b73d221486587ecb489433dd78991bd913662                                                            0.0s
 => => sha256:3c3bb6b86aebf4589683a24f265145b10dabc83f495a06e48befa488ba79d138 8.48MB / 8.48MB                                                       0.1s
 => => extracting sha256:3c3bb6b86aebf4589683a24f265145b10dabc83f495a06e48befa488ba79d138                                                            0.5s
 => => sha256:9493ae436659cad0a53e3cd7d047c9620770455786d80b3f00eb671d47fe213e 847.77kB / 847.77kB                                                   0.0s
 => => extracting sha256:9493ae436659cad0a53e3cd7d047c9620770455786d80b3f00eb671d47fe213e                                                            0.1s
 => exporting to image                                                                                                                               0.2s
 => => exporting layers                                                                                                                              0.0s
 => => exporting manifest sha256:2a0a27c9a62b5d3df27ff008886be597b816c987551c24a13010f2a516b39079                                                    0.0s
 => => exporting config sha256:c1c3409a7c2d8449a12ad3bfd30fc70cc03cc2c308428be865305817ce0b9f6a                                                      0.0s
 => => pushing layers                                                                                                                                0.1s
 => => pushing manifest for 010500926215.dkr.ecr.us-west-2.amazonaws.com/buildkit-cache:image@sha256:2a0a27c9a62b5d3df27ff008886be597b816c987551c24  0.1s
 => [auth] sharing credentials for 010500926215.dkr.ecr.us-west-2.amazonaws.com                                                                      0.0s
 => exporting cache to client directory                                                                                                              0.0s
 => => preparing build cache for export                                                                                                              0.0s
DEBU[2023-03-17T07:38:36Z] (*service).Write started                      expected="sha256:3c3bb6b86aebf4589683a24f265145b10dabc83f495a06e48befa488ba79d138[+] Building 2.7s (11/11) FINISHED                                                                                                                         => [internal] load build definition from Dockerfile                                                                                                 0.0s
 => => transferring dockerfile: 102B                                                                                                                 0.0se => [internal] load metadata for docker.io/library/alpine:3.4                                                                                        1.4s5 => [internal] load .dockerignore                                                                                                                    0.0s
 => => transferring context: 2B                                                                                                                      0.0s8 => importing cache manifest from local:16548731276217466773                                                                                         0.0s7 => => inferred cache manifest type: Manifest List                                                                                                   0.0s
 => [1/4] FROM docker.io/library/alpine:3.4@sha256:b733d4a32c4da6a00a84df2ca32791bb03df95400243648d8c539e7b4cce329c                                  0.0s2 => => resolve docker.io/library/alpine:3.4@sha256:b733d4a32c4da6a00a84df2ca32791bb03df95400243648d8c539e7b4cce329c                                  0.0s5 => CACHED [2/4] RUN apk update                                                                                                                      0.0s
 => CACHED [3/4] RUN apk add vim                                                                                                                     0.0s4 => CACHED [4/4] RUN apk add curl                                                                                                                    1.0s6 => => sha256:c1e54eec4b5786500c19795d1fc604aa7302aee307edfe0554a5c07108b77d48 2.39MB / 2.39MB                                                       0.0s
 => => extracting sha256:c1e54eec4b5786500c19795d1fc604aa7302aee307edfe0554a5c07108b77d48                                                            0.1s9 => => sha256:ce0647ccd38e3c1f8dcd3726a16b73d221486587ecb489433dd78991bd913662 769.08kB / 769.08kB                                                   0.0s6 => => extracting sha256:ce0647ccd38e3c1f8dcd3726a16b73d221486587ecb489433dd78991bd913662                                                            0.0s
 => => sha256:3c3bb6b86aebf4589683a24f265145b10dabc83f495a06e48befa488ba79d138 8.48MB / 8.48MB                                                       0.1s
 => => extracting sha256:3c3bb6b86aebf4589683a24f265145b10dabc83f495a06e48befa488ba79d138                                                            0.5sI => => sha256:9493ae436659cad0a53e3cd7d047c9620770455786d80b3f00eb671d47fe213e 847.77kB / 847.77kB                                                   0.0s
 => => extracting sha256:9493ae436659cad0a53e3cd7d047c9620770455786d80b3f00eb671d47fe213e                                                            0.1se => exporting to image                                                                                                                               0.2s
 => => exporting layers                                                                                                                              0.0si => => exporting manifest sha256:2a0a27c9a62b5d3df27ff008886be597b816c987551c24a13010f2a516b39079                                                    0.0sh => => exporting config sha256:c1c3409a7c2d8449a12ad3bfd30fc70cc03cc2c308428be865305817ce0b9f6a                                                      0.0s
 => => pushing layers                                                                                                                                0.1sD => => pushing manifest for 010500926215.dkr.ecr.us-west-2.amazonaws.com/buildkit-cache:image@sha256:2a0a27c9a62b5d3df27ff008886be597b816c987551c24  0.1s
 => [auth] sharing credentials for 010500926215.dkr.ecr.us-west-2.amazonaws.com                                                                      0.0s7 => exporting cache to client directory                                                                                                              0.0s
 => => preparing build cache for export                                                                                                              0.0s
 => => writing layer sha256:3c3bb6b86aebf4589683a24f265145b10dabc83f495a06e48befa488ba79d138                                                         0.0s
 => => writing layer sha256:9493ae436659cad0a53e3cd7d047c9620770455786d80b3f00eb671d47fe213e                                                         0.0s
 => => writing layer sha256:c1e54eec4b5786500c19795d1fc604aa7302aee307edfe0554a5c07108b77d48                                                         0.0s
 => => writing layer sha256:ce0647ccd38e3c1f8dcd3726a16b73d221486587ecb489433dd78991bd913662                                                         0.0s
 => => writing config sha256:2134fc6fc8b4ff0f425570b9d8b3f0eba3b6611482a7dd419a0b90cee98c96a4                                                        0.0s
 => => writing cache manifest sha256:758738f665e3b7108b4ce18b4e885b00714eef6256e6ba23381a0e30c10bcc19                                                0.0s

@kattmang
Copy link
Contributor Author

Fixed in amended commit, existing integ tests are passing now. Humbly looking for guidance on where to add integ tests for the new ImageManifest type :)

@kattmang kattmang marked this pull request as ready for review March 17, 2023 09:38
@kattmang
Copy link
Contributor Author

Linter errors fixed as well.

Copy link
Member

@tonistiigi tonistiigi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look at the current integration tests, eg. testBasicCacheImportExport, testCacheImportExport and add variants where there is opt-in to this version.

@@ -75,20 +76,23 @@ func (ce *contentCacheExporter) Finalize(ctx context.Context) (map[string]string
}

// own type because oci type can't be pushed and docker type doesn't have annotations
type manifestList struct {
type abstractManifest struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use different types for this and don't try to combine them together. I believe missing MediaType issue was fixed in latest oci spec so probably custom types are not needed anymore at all.

Copy link
Contributor Author

@kattmang kattmang Apr 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think ocispecs.Manifest and ocispecs.Index would work respectively. I think if that's the case though, for the rest of the export code I would either need to:

  1. create an wrapper struct around Manifest and Index (seems to still have the same concern you have here) and implement functions that map the generated cache descriptors to the appropriate spec/oci object aka WrapperObj.addCacheDescriptor()

OR

  1. Have two object references for Manifest and Index and interchange which is used when the same "flat" way the code is structured now (bifurcation/ifs where the content descriptors are actually being set)

Is the concern more around the possibility of generating a nonconforming OCI Index/Manifest that has a superset of fields? If so, going with Approach 2 would create a new problem of writing to the wrong Object altogether and at the end of Finalize encode an empty object to string. I kind of prefer Approach 1 as the wrapping struct could have a setCacheFormat(), which would remove all of the if ce.imageManifest references in Finalize. I only worry that with Approach 1 we may be abstracting in the wrong direction (should Finalize become be a switchboard to what generated descriptors go where or should it only concern itself about the generation of the descriptors?)

I'm going to go with Approach 1 as I can formalize it in my head better but open to alternatives.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we probably would want to do option 3: use the ocispecs.Index struct, and simply set the mediaType depending on whether oci-mediatypes has been appropriately configured: see

idx := ocispecs.Index{
MediaType: ocispecs.MediaTypeImageIndex,
Annotations: opts.Annotations.Platform(nil).Index,
Versioned: specs.Versioned{
SchemaVersion: 2,
},
}
if !opts.OCITypes {
idx.MediaType = images.MediaTypeDockerSchema2ManifestList
}
for how we do it for the containerimage exporter.

Copy link
Contributor Author

@kattmang kattmang Apr 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jedevc I'm not sure how Index can be adapted to support both Index and Manifest (Manifest is the format that being introduced into both import.go and export.go here). Maybe I'm missing something here.

EDIT: point taken on oci-mediatypes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See new commit: I went ahead with option one. I also added checks on oci-mediatype compatibility with image manifest: #3724 (comment)

@@ -122,7 +132,11 @@ func (ce *contentCacheExporter) Finalize(ctx context.Context) (map[string]string
}
configDone(nil)

mfst.Manifests = append(mfst.Manifests, desc)
if ce.imageManifest {
mfst.Config = &desc
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the mediatype correct in here for the config descriptor? @sudo-bmitch

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this still looks correct to me. We should definitely avoid changing the config media type here if at all possible since some registries have a hardcoded list of allowed config media types (as demonstrated in #3610) - if we keep it the same, we won't have the same problem, and this new way of storing cache should "just work".

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that looks right. We've added guidance in image-spec for packaging artifacts (like the build cache) in the image manifest. My advice is to stick with the first example for now, which I believe you're already doing: https://github.com/opencontainers/image-spec/blob/933f91770d22839aca9c0ae46abc01321ca4ee70/manifest.md#guidelines-for-artifact-usage

}

for _, m := range mfst.Layers {
if m.MediaType == v1.CacheConfigMediaTypeV0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not look correct. It is not layer mediatype that is CacheConfigMediaTypeV0 but the config's.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, yeah. This indeed popped when I added integ tests :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in new commit.

@@ -97,6 +150,37 @@ func (ci *contentCacheImporter) Resolve(ctx context.Context, desc ocispecs.Descr
return solver.NewCacheManager(ctx, id, keysStorage, resultStorage), nil
}

// extends support for "new"-style image-manifest style remote cache manifests and determining downstream
// handling based on inference of document structure (is this a new or old cache manifest type?)
func inferManifestType(ctx context.Context, dt []byte) (ManifestType, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have a function called DetectManifestMediaType

Copy link
Contributor Author

@kattmang kattmang Apr 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh, I feel very lucky it already exists :) Good learning for me to see a better quality implementation as well. I also like that it uses the containerd/oci mediatypes which seems more direct and extensible than my fake-enum approach.

@kattmang
Copy link
Contributor Author

kattmang commented Mar 20, 2023

Thanks for the feedback and pointers on new integ tests, @tonistiigi .Very insightful and will address yours and others' feedback items point by point after vacation in April!

mfst.SchemaVersion = 2
mfst.MediaType = images.MediaTypeDockerSchema2ManifestList
if ce.oci {
if ce.oci && !ce.imageManifest {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should still respect the value of oci-mediatypes here, even when image-manifest=true.

The default for oci-mediatypes is false, so it will still work out of the box with ECR, but users may use other registries and want to use OCI media types instead, so we should give them the option to enable this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably oci-mediatypes=false and image-manifest=true would be invalid configuration and should just error.

What would be a better name for image-manifest=true? Is it oci-artifact-manifest oci-artifact? I'm quite confused about what they mean, but image-manifest seems too generic and doesn't convey the meaning that this uses a config with custom mediatyoe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Funnily enough in my initial work for this I did use oci-artifact as the key name here, but after working with some colleagues more closely associated with the OCI image-spec 1.1 work ( tagging @michaelb990 and @jlbutler ), we found that the term artifact has become semantically loaded. More specifically there's two artifacts in people's mind in the space:

  • small "a" artifacts: image manifest + layers with a nonstandardized config.mediaType (this aligns with the cache manifest approach in this PR)
  • capital "A" Artifacts: artifact manifest + blobs (currently being pulled out / made optional in 1.1). This is an approach we could take for cache manifest if/when this manifest type becomes more prevalent.

As I work through this happy to consider other names for the key.

Will think through oci-mediatypes: I think I lean on erroring here though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about it more: ImageManifest mode could support docker's image manifest version (which I don't think makes sense given registry support is wide enough for OCI manifest), so I am going to error if oci-mediatypes=false and image-manifest=true as invalid and down the road that can be implemented as needed/warranted. I also will update the readme to instruct client users to need to toggle both for Image Manifest remote caching to work.

@kattmang
Copy link
Contributor Author

kattmang commented May 1, 2023

Added Client and Dockerfile Integ tests, and addressed feedback, primarily the largest thing being a bugfix to the import on imageManifest being broken on config mediatype being checked on layers instead of Config (thanks @tonistiigi ). Also tore out my manifest type inference code in favor of DetectManifestMediaType in utils.

@kattmang
Copy link
Contributor Author

kattmang commented May 1, 2023

one second, rebuilding branch.

EDIT: FIxed, ready for review

type ExportableCache struct {
exportedManifest ocispecs.Manifest
exportedIndex ocispecs.Index
cacheType int
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add actual type for this with iota const values.

cc := v1.NewCacheChains()
return &contentCacheExporter{CacheExporterTarget: cc, chains: cc, ingester: ingester, oci: oci, ref: ref, comp: compressionConfig}
type ExportableCache struct {
exportedManifest ocispecs.Manifest
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment in here that there are two styles of exports. Index of blobs and OCI artifact manifest.


// Manifests references platform specific manifests.
Manifests []ocispecs.Descriptor `json:"manifests"`
cache := ExportableCache{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iiuc the public functions SetMediaType and SetSchemaVersion are not needed. You can just make a constructor NewExportableCache(type) and everything is set up internally in that function.

For the oci check, this can be before NewExportableCache is called.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember when I was writing this I had it like that and I changed it to this (I have no idea why and on reflection this just makes more sense).

}

func (ec *ExportableCache) FinalizeCache(ctx context.Context, ce *contentCacheExporter) {
// Nothing needed here for Manifest-type cache manifests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure ConvertAllLayerMediaTypes is not needed? Is it guaranteed to be OCI already?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my tests I think it "accidentally" was OCI compatible, but I think you're right in that there's no guarantee that whatever the solver gives is going to be a consistent OCI Layer type. We should call this regardless of cache type to ensure the entire cache manifest is internally consistent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment in here was not removed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Literally had my IDE open to remove it as I got this message :)

}
}

func (ec *ExportableCache) GetCacheJSON() ([]byte, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call this MarshalJSON().

@@ -133,9 +193,14 @@ func (ce *contentCacheExporter) Finalize(ctx context.Context) (map[string]string
desc = ocispecs.Descriptor{
Digest: dgst,
Size: int64(len(dt)),
MediaType: mfst.MediaType,
MediaType: cache.GetMediaType(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After removing SetMediaType, Get prefix is not needed here.

if mfst.Config.MediaType == v1.CacheConfigMediaTypeV0 {
configDesc = mfst.Config
} else {
err = errors.Wrapf(err, "Image Manifest cache is missing expected cache Config mediatype")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lowercase errors

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

err is nil in here. This needs to be errors.Errorf. Include the mediatype that doesn't match in the error message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch

}
default:
err = errors.Wrapf(err, "Unsupported or uninferrable manifest type")
return nil, err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how is the inline case still hit in line 112?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would work fine if the ref'd manifest happens to be a Manifest List/Index and had an inline cache in its config (can that even happen?), but it would fail to hit the inline case if the ref is an Image Manifest because of line 88. I propose just ripping out lines 88 through 91 and letting any malform failures happen in importInlineCache.

Copy link
Contributor Author

@kattmang kattmang May 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, my apologies for not seeing the integ test failure indicating this issue: I did see some integ tests fail without any changes from my part so I thought that was attributable to other changes in master. Very good catch!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tested in my local, ripping out 88-91 does indeed fix the integ tests failing below.

@tonistiigi
Copy link
Member

Also rebase to remove the merge commit from the commit chain.

@kattmang kattmang force-pushed the master branch 2 times, most recently from badd8b2 to ba2c7dc Compare May 3, 2023 06:03
@kattmang
Copy link
Contributor Author

kattmang commented May 3, 2023

I've addressed comments and rebased.

@kattmang
Copy link
Contributor Author

kattmang commented May 3, 2023

@kattmang kattmang requested a review from tonistiigi May 3, 2023 18:35
@kattmang
Copy link
Contributor Author

kattmang commented May 5, 2023

Just to confirm it looks like the single test failure here is for a test unrelated to this change, but want to see what if you've seen this one: TestDiffDeleteDoesNotFollowSymlink .

Happy to pull from the latest master and resubmit.

EDIT: looks like reruns did the trick and is part of a subset of tests that are flakey. Corresponded over slack with @tonistiigi and appears related to #3401

Copy link
Member

@tonistiigi tonistiigi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested this and seemed to work correctly

CacheType: cacheType,
}
}
return &ExportableCache{ExportedManifest: ocispecs.Manifest{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add switch for the cacheType with error case for unknown value.

CacheType CacheType
}

func NewExportableCache(cacheType CacheType, mediaType string, schemaVersion specs.Versioned) *ExportableCache {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is schemaVersion a parameter? It is always 2 and can't be configured.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of mediatype this should take oci parameter so that the type switch only happens once. And caller can't make a mistake setting incompatible cacheType and mediaType.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take this to mean we should pull in scope from Finalize where we determine mediatype and cachetype more into the Cache object itself, which makes sense.

configDesc = m
continue
switch manifestType {
case images.MediaTypeDockerSchema2ManifestList, ocispecs.MediaTypeImageIndex:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be follow-up, but I guess manifest list of "cache image manifests" should also be a valid configuration.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm not sure I should tackle this because I don't know how such a cache would/could be exported.


dt2, err = os.ReadFile(filepath.Join(destDir, "unique"))
require.NoError(t, err)
require.Equal(t, string(dt), string(dt2))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, you should also test in here that the manifest in the registry is an image manifest and it has a config with correct mediatype. Otherwise, this test would pass without any of your changes as well because image-manifest=true would just be ignored.

… of cache manifest (opt-in on export, inferred on import) moby/buildkit moby#2251

Signed-off-by: Kang, Matthew <impulsecss@gmail.com>
@kattmang
Copy link
Contributor Author

Made the integ test change and the ExportableCache refactor @tonistiigi . LMK if we're good here.

@kattmang kattmang requested a review from tonistiigi May 11, 2023 23:00

func NewExportableCache(oci bool, imageManifest bool) (*ExportableCache, error) {
var mediaType string
if oci && !imageManifest {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't read very well.

var mediaType string
cacheType := ManifestList

if imageManifest {
  cacheType = ImageManifest
  if oci {} else {}
} else {
  if oci {} else {}
}

const (
// ExportResponseManifestDesc is a key for the map returned from Exporter.Finalize.
// The map value is a JSON string of an OCI desciptor of a manifest.
ExporterResponseManifestDesc = "cache.manifest"
CacheManifestSchemaVersion = 2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need to be public.

If you combine the schemaVersion to a single definition, then this doesn't need to be defined as constant at all.

func (ec *ExportableCache) FinalizeCache(ctx context.Context, ce *contentCacheExporter) {
// Nothing needed here for Manifest-type cache manifests
if ec.CacheType == ManifestList {
ec.ExportedIndex.Manifests = compression.ConvertAllLayerMediaTypes(ctx, ce.oci, ec.ExportedIndex.Manifests...)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just store the .oci in NewExportableCache so it can be read from ec.

@kattmang kattmang force-pushed the master branch 2 times, most recently from 5ebb4b3 to a7f450c Compare May 12, 2023 05:00
@kattmang kattmang requested a review from tonistiigi May 12, 2023 05:00
}

func (ec *ExportableCache) FinalizeCache(ctx context.Context, ce *contentCacheExporter) {
// Nothing needed here for Manifest-type cache manifests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment in here was not removed

ExportedManifest ocispecs.Manifest
ExportedIndex ocispecs.Index
CacheType CacheType
Oci bool
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: if it is public then OCI


func (data CacheType) String() string {
switch data {
case NotSet:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: case NotSet: is not needed here

Signed-off-by: Matt Kang <impulsecss@gmail.com>
@tonistiigi tonistiigi merged commit 995d908 into moby:master May 13, 2023
@kattmang
Copy link
Contributor Author

Thanks for your hard work in the review @tonistiigi ! Excited to see if we can fold this into 0.12.

@jxlwqq
Copy link

jxlwqq commented Jun 16, 2023

Can't wait for the new release.

If you want to test or use it early, you can use the master tag https://hub.docker.com/r/moby/buildkit/tags

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants