diff --git a/README.md b/README.md index 4a32dd1cb475..e56d4fa26e9b 100644 --- a/README.md +++ b/README.md @@ -503,6 +503,7 @@ The directory layout conforms to OCI Image Spec v1.0. * `src=`: source directory for cache importer * `tag=`: specify custom tag of image to read from local index (default: `latest`) * `digest=sha256:`: specify explicit digest of the manifest list to import +* `lazy=`: when `true`, imported cache layers are not eagerly extracted but loaded on demand (default: `false`). This is useful for ephemeral/daemonless `buildkitd` setups where each build uses its own daemon instance, avoiding unnecessary extraction overhead for cached builds. #### GitHub Actions cache (experimental) diff --git a/cache/remotecache/local/local.go b/cache/remotecache/local/local.go index a71f5ce914ec..8f5decf99fdc 100644 --- a/cache/remotecache/local/local.go +++ b/cache/remotecache/local/local.go @@ -19,6 +19,7 @@ const ( attrDigest = "digest" attrSrc = "src" attrDest = "dest" + attrLazy = "lazy" attrImageManifest = "image-manifest" attrOCIMediatypes = "oci-mediatypes" contentStoreIDPrefix = "local:" @@ -63,7 +64,7 @@ func ResolveCacheExporterFunc(sm *session.Manager) remotecache.ResolveCacheExpor } csID := contentStoreIDPrefix + store - cs, err := getContentStore(ctx, sm, g, csID) + cs, err := getContentStore(ctx, sm, g, csID, false) if err != nil { return nil, err } @@ -83,8 +84,16 @@ func ResolveCacheImporterFunc(sm *session.Manager) remotecache.ResolveCacheImpor if store == "" { return nil, ocispecs.Descriptor{}, errors.New("local cache importer requires src") } + lazy := false + if v, ok := attrs[attrLazy]; ok { + b, err := strconv.ParseBool(v) + if err != nil { + return nil, ocispecs.Descriptor{}, errors.Wrapf(err, "failed to parse %s", attrLazy) + } + lazy = b + } csID := contentStoreIDPrefix + store - cs, err := getContentStore(ctx, sm, g, csID) + cs, err := getContentStore(ctx, sm, g, csID, lazy) if err != nil { return nil, ocispecs.Descriptor{}, err } @@ -102,7 +111,7 @@ func ResolveCacheImporterFunc(sm *session.Manager) remotecache.ResolveCacheImpor } } -func getContentStore(ctx context.Context, sm *session.Manager, g session.Group, storeID string) (content.Store, error) { +func getContentStore(ctx context.Context, sm *session.Manager, g session.Group, storeID string, lazy bool) (content.Store, error) { // TODO: to ensure correct session is detected, new api for finding if storeID is supported is needed sessionID := g.SessionIterator().NextSession() if sessionID == "" { @@ -116,7 +125,11 @@ func getContentStore(ctx context.Context, sm *session.Manager, g session.Group, if err != nil { return nil, err } - return &unlazyProvider{sessioncontent.NewCallerStore(caller, storeID), g}, nil + cs := sessioncontent.NewCallerStore(caller, storeID) + if lazy { + return cs, nil + } + return &unlazyProvider{cs, g}, nil } type unlazyProvider struct { diff --git a/docs/reference/buildctl.md b/docs/reference/buildctl.md index ede9679ab255..4ba24ed9e8d7 100644 --- a/docs/reference/buildctl.md +++ b/docs/reference/buildctl.md @@ -253,3 +253,4 @@ For example: * `--import-cache type=registry,ref=example.com/foo/bar` - import into the cache from an OCI image. * `--import-cache type=local,src=path/to/dir` - import into the cache from a directory local to where `buildctl` is running. +* `--import-cache type=local,src=path/to/dir,lazy=true` - import with lazy layer loading (layers are not eagerly extracted; useful for ephemeral/daemonless buildkitd).