diff --git a/CHANGELOG.md b/CHANGELOG.md index 0362f11f..fe8c6b72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - (cli) Added a `stage show-next-index` command to print the index of the next staged pallet bundle (if it exists). +- (cli) Added a `--platform` flag (and `FORKLIFT_PLATFORM` env var) to override the auto-detected platform (e.g. `linux/amd64` or `linux/arm64`) used for downloading container images for file exports and for pre-downloading container images needed for the next `forklift stage apply`. ## 0.8.0-alpha.2 - 2024-09-22 diff --git a/cmd/forklift/dev/plt/downloads.go b/cmd/forklift/dev/plt/downloads.go index 4c01b933..525252fe 100644 --- a/cmd/forklift/dev/plt/downloads.go +++ b/cmd/forklift/dev/plt/downloads.go @@ -53,7 +53,7 @@ func cacheDlAction(versions Versions) cli.ActionFunc { } if err := fcli.DownloadExportFiles( - 0, plt, caches.r, caches.d, false, c.Bool("parallel"), + 0, plt, caches.r, caches.d, c.String("platform"), false, c.Bool("parallel"), ); err != nil { return err } diff --git a/cmd/forklift/dev/plt/images.go b/cmd/forklift/dev/plt/images.go index ed06961d..5bcd7314 100644 --- a/cmd/forklift/dev/plt/images.go +++ b/cmd/forklift/dev/plt/images.go @@ -51,7 +51,7 @@ func cacheImgAction(versions Versions) cli.ActionFunc { fmt.Println("Downloading Docker container images specified by the development pallet...") if err := fcli.DownloadImages( - 0, plt, caches.r, c.Bool("include-disabled"), c.Bool("parallel"), + 0, plt, caches.r, c.String("platform"), c.Bool("include-disabled"), c.Bool("parallel"), ); err != nil { return err } diff --git a/cmd/forklift/dev/plt/pallets.go b/cmd/forklift/dev/plt/pallets.go index 84e36118..73413396 100644 --- a/cmd/forklift/dev/plt/pallets.go +++ b/cmd/forklift/dev/plt/pallets.go @@ -261,7 +261,7 @@ func cacheAllAction(versions Versions) cli.ActionFunc { if err = fcli.CacheAllReqs( 0, plt, caches.m, caches.p, caches.r, caches.d, - c.Bool("include-disabled"), c.Bool("parallel"), + c.String("platform"), c.Bool("include-disabled"), c.Bool("parallel"), ); err != nil { return err } @@ -360,7 +360,8 @@ func stageAction(versions Versions) cli.ActionFunc { } if _, err = fcli.StagePallet( 0, plt, stageStore, caches.staging(), c.String("exports"), - versions.Staging, c.Bool("no-cache-img"), c.Bool("parallel"), c.Bool("ignore-tool-version"), + versions.Staging, c.Bool("no-cache-img"), c.String("platform"), c.Bool("parallel"), + c.Bool("ignore-tool-version"), ); err != nil { return err } @@ -400,7 +401,8 @@ func applyAction(versions Versions) cli.ActionFunc { } index, err := fcli.StagePallet( 0, plt, stageStore, caches.staging(), c.String("exports"), - versions.Staging, false, c.Bool("parallel"), c.Bool("ignore-tool-version"), + versions.Staging, false, c.String("platform"), c.Bool("parallel"), + c.Bool("ignore-tool-version"), ) if err != nil { return errors.Wrap(err, "couldn't stage pallet to be applied immediately") @@ -513,7 +515,8 @@ func addPltAction(versions Versions) cli.ActionFunc { return err } if _, _, err = fcli.CacheStagingReqs( - 0, plt, caches.m, caches.p, caches.r, caches.d, false, c.Bool("parallel"), + 0, plt, caches.m, caches.p, caches.r, caches.d, + c.String("platform"), false, c.Bool("parallel"), ); err != nil { return err } diff --git a/cmd/forklift/dev/plt/repositories.go b/cmd/forklift/dev/plt/repositories.go index 0bb8ddb0..fa4fa8ba 100644 --- a/cmd/forklift/dev/plt/repositories.go +++ b/cmd/forklift/dev/plt/repositories.go @@ -105,7 +105,8 @@ func addRepoAction(versions Versions) cli.ActionFunc { } if !c.Bool("no-cache-req") { if _, _, err = fcli.CacheStagingReqs( - 0, plt, caches.m, caches.p, caches.r, caches.d, false, c.Bool("parallel"), + 0, plt, caches.m, caches.p, caches.r, caches.d, + c.String("platform"), false, c.Bool("parallel"), ); err != nil { return err } diff --git a/cmd/forklift/main.go b/cmd/forklift/main.go index c0746af9..a8fcda79 100644 --- a/cmd/forklift/main.go +++ b/cmd/forklift/main.go @@ -14,6 +14,7 @@ import ( "github.com/PlanktoScope/forklift/cmd/forklift/plt" "github.com/PlanktoScope/forklift/cmd/forklift/stage" fcli "github.com/PlanktoScope/forklift/internal/app/forklift/cli" + "github.com/PlanktoScope/forklift/internal/clients/crane" ) func main() { @@ -22,7 +23,10 @@ func main() { } } -var defaultWorkspaceBase, _ = os.UserHomeDir() +var ( + defaultWorkspaceBase, _ = os.UserHomeDir() + defaultPlatform = crane.DetectPlatform().String() +) var fcliVersions fcli.StagingVersions = fcli.StagingVersions{ Core: fcli.Versions{ @@ -83,6 +87,12 @@ var app = &cli.App{ "or starting containers", EnvVars: []string{"FORKLIFT_PARALLEL"}, }, + &cli.StringFlag{ + Name: "platform", + Value: defaultPlatform, + Usage: "Select the os/arch or os/arch/variant platform for downloading container images", + EnvVars: []string{"FORKLIFT_PLATFORM"}, + }, }, Suggest: true, } diff --git a/cmd/forklift/plt/downloads.go b/cmd/forklift/plt/downloads.go index e4d93a4c..c3933d89 100644 --- a/cmd/forklift/plt/downloads.go +++ b/cmd/forklift/plt/downloads.go @@ -51,7 +51,7 @@ func cacheDlAction(versions Versions) cli.ActionFunc { } if err := fcli.DownloadExportFiles( - 0, plt, caches.r, caches.d, false, c.Bool("parallel"), + 0, plt, caches.r, caches.d, c.String("platform"), false, c.Bool("parallel"), ); err != nil { return err } diff --git a/cmd/forklift/plt/images.go b/cmd/forklift/plt/images.go index 04474e82..aa4c3382 100644 --- a/cmd/forklift/plt/images.go +++ b/cmd/forklift/plt/images.go @@ -49,7 +49,7 @@ func cacheImgAction(versions Versions) cli.ActionFunc { fmt.Println("Downloading Docker container images specified by the local pallet...") if err := fcli.DownloadImages( - 0, plt, caches.r, c.Bool("include-disabled"), c.Bool("parallel"), + 0, plt, caches.r, c.String("platform"), c.Bool("include-disabled"), c.Bool("parallel"), ); err != nil { return err } diff --git a/cmd/forklift/plt/pallets.go b/cmd/forklift/plt/pallets.go index 2ba13d96..402b095b 100644 --- a/cmd/forklift/plt/pallets.go +++ b/cmd/forklift/plt/pallets.go @@ -99,7 +99,7 @@ func cacheAllAction(versions Versions) cli.ActionFunc { if err = fcli.CacheAllReqs( 0, plt, caches.m, caches.p, caches.r, caches.d, - c.Bool("include-disabled"), c.Bool("parallel"), + c.String("platform"), c.Bool("include-disabled"), c.Bool("parallel"), ); err != nil { return err } @@ -136,7 +136,8 @@ func switchAction(versions Versions) cli.ActionFunc { if err = preparePallet( // Note: we don't cache staging requirements because that will be handled by the apply/stage // step anyways: - workspace, query, true, false, c.Bool("parallel"), c.Bool("ignore-tool-version"), versions, + workspace, query, true, false, c.String("platform"), c.Bool("parallel"), + c.Bool("ignore-tool-version"), versions, ); err != nil { return err } @@ -370,7 +371,8 @@ func isHeadInRemotes(indent int, gitRepo *git.Repo) (bool, error) { func preparePallet( workspace *forklift.FSWorkspace, gitRepoQuery forklift.GitRepoQuery, - updateLocalMirror, cacheStagingReqs, parallel, ignoreToolVersion bool, versions Versions, + updateLocalMirror, cacheStagingReqs bool, platform string, parallel, ignoreToolVersion bool, + versions Versions, ) error { // clone pallet if err := fcli.CloneQueriedGitRepoUsingLocalMirror( @@ -394,7 +396,7 @@ func preparePallet( if cacheStagingReqs { fmt.Println() if _, _, err = fcli.CacheStagingReqs( - 0, plt, caches.m, caches.p, caches.r, caches.d, false, parallel, + 0, plt, caches.m, caches.p, caches.r, caches.d, platform, false, parallel, ); err != nil { return err } @@ -431,7 +433,8 @@ func upgradeAction(versions Versions) cli.ActionFunc { if err = preparePallet( // Note: we don't cache staging requirements because that will be handled by the apply/stage // step anyways: - workspace, query, false, false, c.Bool("parallel"), c.Bool("ignore-tool-version"), versions, + workspace, query, false, false, c.String("platform"), c.Bool("parallel"), + c.Bool("ignore-tool-version"), versions, ); err != nil { return err } @@ -669,7 +672,7 @@ func cloneAction(versions Versions) cli.ActionFunc { } if err = preparePallet( - workspace, query, true, !c.Bool("no-cache-req"), c.Bool("parallel"), + workspace, query, true, !c.Bool("no-cache-req"), c.String("platform"), c.Bool("parallel"), c.Bool("ignore-tool-version"), versions, ); err != nil { return err @@ -744,7 +747,8 @@ func pullAction(versions Versions) cli.ActionFunc { if !c.Bool("no-cache-req") { if _, _, err = fcli.CacheStagingReqs( - 0, plt, caches.m, caches.p, caches.r, caches.d, false, c.Bool("parallel"), + 0, plt, caches.m, caches.p, caches.r, caches.d, + c.String("platform"), false, c.Bool("parallel"), ); err != nil { return err } @@ -865,7 +869,8 @@ func stageAction(versions Versions) cli.ActionFunc { } if _, err = fcli.StagePallet( 0, plt, stageStore, caches.staging(), c.String("exports"), - versions.Staging, c.Bool("no-cache-img"), c.Bool("parallel"), c.Bool("ignore-tool-version"), + versions.Staging, c.Bool("no-cache-img"), c.String("platform"), c.Bool("parallel"), + c.Bool("ignore-tool-version"), ); err != nil { return err } @@ -900,7 +905,8 @@ func applyAction(versions Versions) cli.ActionFunc { } index, err := fcli.StagePallet( 0, plt, stageStore, caches.staging(), c.String("exports"), - versions.Staging, false, c.Bool("parallel"), c.Bool("ignore-tool-version"), + versions.Staging, false, c.String("platform"), c.Bool("parallel"), + c.Bool("ignore-tool-version"), ) if err != nil { return errors.Wrap(err, "couldn't stage pallet to be applied immediately") @@ -1011,7 +1017,8 @@ func addPltAction(versions Versions) cli.ActionFunc { return err } if _, _, err = fcli.CacheStagingReqs( - 0, plt, caches.m, caches.p, caches.r, caches.d, false, c.Bool("parallel"), + 0, plt, caches.m, caches.p, caches.r, caches.d, + c.String("platform"), false, c.Bool("parallel"), ); err != nil { return err } diff --git a/cmd/forklift/plt/repositories.go b/cmd/forklift/plt/repositories.go index 6165a358..19f62508 100644 --- a/cmd/forklift/plt/repositories.go +++ b/cmd/forklift/plt/repositories.go @@ -98,7 +98,8 @@ func addRepoAction(versions Versions) cli.ActionFunc { } if !c.Bool("no-cache-req") { if _, _, err = fcli.CacheStagingReqs( - 0, plt, caches.m, caches.p, caches.r, caches.d, false, c.Bool("parallel"), + 0, plt, caches.m, caches.p, caches.r, caches.d, + c.String("platform"), false, c.Bool("parallel"), ); err != nil { return err } diff --git a/cmd/forklift/stage/images.go b/cmd/forklift/stage/images.go index ac1e8182..6e30d714 100644 --- a/cmd/forklift/stage/images.go +++ b/cmd/forklift/stage/images.go @@ -22,7 +22,7 @@ func cacheImgAction(versions Versions) cli.ActionFunc { if err = fcli.DownloadImagesForStoreApply( 0, store, versions.Tool, versions.MinSupportedBundle, - c.Bool("parallel"), c.Bool("ignore-tool-version"), + c.String("platform"), c.Bool("parallel"), c.Bool("ignore-tool-version"), ); err != nil { return err } diff --git a/cmd/forklift/stage/store.go b/cmd/forklift/stage/store.go index 7ab7abdc..2b845d88 100644 --- a/cmd/forklift/stage/store.go +++ b/cmd/forklift/stage/store.go @@ -298,7 +298,8 @@ func setNextAction(versions Versions) cli.ActionFunc { if err = fcli.SetNextStagedBundle( 0, store, newNext, c.String("exports"), versions.Tool, versions.MinSupportedBundle, - c.Bool("no-cache-img"), c.Bool("parallel"), c.Bool("ignore-tool-version"), + c.Bool("no-cache-img"), c.String("platform"), c.Bool("parallel"), + c.Bool("ignore-tool-version"), ); err != nil { return err } diff --git a/internal/app/forklift/cli/caching-exports.go b/internal/app/forklift/cli/caching-exports.go index 17677b18..59578c69 100644 --- a/internal/app/forklift/cli/caching-exports.go +++ b/internal/app/forklift/cli/caching-exports.go @@ -40,7 +40,8 @@ func GetDownloadCache(wpath string, ensureCache bool) (*forklift.FSDownloadCache func DownloadExportFiles( indent int, deplsLoader ResolvedDeplsLoader, pkgLoader forklift.FSPkgLoader, - dlCache *forklift.FSDownloadCache, includeDisabled, parallel bool, + dlCache *forklift.FSDownloadCache, + platform string, includeDisabled, parallel bool, ) error { httpDownloads, ociDownloads, err := ListRequiredDownloads(deplsLoader, pkgLoader, includeDisabled) if err != nil { @@ -83,9 +84,9 @@ func DownloadExportFiles( } if parallel { - return downloadParallel(indent, newHTTP, newOCI, dlCache, http.DefaultClient) + return downloadParallel(indent, newHTTP, newOCI, platform, dlCache, http.DefaultClient) } - return downloadSerial(indent, newHTTP, newOCI, dlCache, http.DefaultClient) + return downloadSerial(indent, newHTTP, newOCI, platform, dlCache, http.DefaultClient) } func ListRequiredDownloads( @@ -140,7 +141,8 @@ func ListRequiredDownloads( } func downloadParallel( - indent int, httpURLs, ociImageNames []string, cache *forklift.FSDownloadCache, hc *http.Client, + indent int, httpURLs, ociImageNames []string, platform string, cache *forklift.FSDownloadCache, + hc *http.Client, ) error { eg, egctx := errgroup.WithContext(context.Background()) for _, url := range httpURLs { @@ -164,7 +166,7 @@ func downloadParallel( if err != nil { return errors.Wrapf(err, "couldn't determine path to cache download for %s", imageName) } - if err = downloadOCIImage(egctx, imageName, outputPath); err != nil { + if err = downloadOCIImage(egctx, imageName, outputPath, platform); err != nil { return errors.Wrapf(err, "couldn't download %s", imageName) } IndentedPrintf(indent, "Downloaded %s\n", imageName) @@ -178,7 +180,8 @@ func downloadParallel( } func downloadSerial( - indent int, httpURLs, ociImageNames []string, cache *forklift.FSDownloadCache, hc *http.Client, + indent int, httpURLs, ociImageNames []string, platform string, cache *forklift.FSDownloadCache, + hc *http.Client, ) error { for _, url := range httpURLs { IndentedPrintf(indent, "Downloading file %s to cache...\n", url) @@ -197,7 +200,7 @@ func downloadSerial( if err != nil { return errors.Wrapf(err, "couldn't determine path to cache download for %s", imageName) } - if err = downloadOCIImage(context.Background(), imageName, outputPath); err != nil { + if err = downloadOCIImage(context.Background(), imageName, outputPath, platform); err != nil { return errors.Wrapf(err, "couldn't download %s", imageName) } IndentedPrintf(indent, "Downloaded %s\n", imageName) @@ -249,7 +252,7 @@ func downloadFile(ctx context.Context, url, outputPath string, hc *http.Client) return nil } -func downloadOCIImage(ctx context.Context, imageName, outputPath string) error { +func downloadOCIImage(ctx context.Context, imageName, outputPath, platform string) error { if err := forklift.EnsureExists(filepath.FromSlash(path.Dir(outputPath))); err != nil { return err } @@ -265,7 +268,7 @@ func downloadOCIImage(ctx context.Context, imageName, outputPath string) error { } }() - if err = crane.ExportOCIImage(ctx, imageName, file); err != nil { + if err = crane.ExportOCIImage(ctx, imageName, file, platform); err != nil { return errors.Wrapf(err, "couldn't download and export image as a tarball: %s", imageName) } diff --git a/internal/app/forklift/cli/caching.go b/internal/app/forklift/cli/caching.go index 8d1cb459..eb2232af 100644 --- a/internal/app/forklift/cli/caching.go +++ b/internal/app/forklift/cli/caching.go @@ -11,17 +11,23 @@ func CacheAllReqs( indent int, pallet *forklift.FSPallet, mirrorsCache core.Pather, palletCache forklift.PathedPalletCache, repoCache forklift.PathedRepoCache, dlCache *forklift.FSDownloadCache, - includeDisabled, parallel bool, + platform string, includeDisabled, parallel bool, ) error { pallet, repoCacheWithMerged, err := CacheStagingReqs( - indent, pallet, mirrorsCache, palletCache, repoCache, dlCache, includeDisabled, parallel, + indent, pallet, mirrorsCache, palletCache, repoCache, dlCache, + platform, includeDisabled, parallel, ) if err != nil { return err } - IndentedPrintln(indent, "Downloading Docker container images to be deployed by the local pallet...") - if err := DownloadImages(1, pallet, repoCacheWithMerged, includeDisabled, parallel); err != nil { + IndentedPrintln( + indent, + "Downloading Docker container images to be deployed by the local pallet...", + ) + if err := DownloadImages( + 1, pallet, repoCacheWithMerged, platform, includeDisabled, parallel, + ); err != nil { return err } return nil @@ -31,7 +37,7 @@ func CacheStagingReqs( indent int, pallet *forklift.FSPallet, mirrorsCache core.Pather, palletCache forklift.PathedPalletCache, repoCache forklift.PathedRepoCache, dlCache *forklift.FSDownloadCache, - includeDisabled, parallel bool, + platform string, includeDisabled, parallel bool, ) (merged *forklift.FSPallet, repoCacheWithMerged *forklift.LayeredRepoCache, err error) { IndentedPrintln(indent, "Caching everything needed to stage the pallet...") indent++ @@ -65,7 +71,7 @@ func CacheStagingReqs( } if err = DownloadExportFiles( - indent, merged, repoCacheWithMerged, dlCache, includeDisabled, parallel, + indent, merged, repoCacheWithMerged, dlCache, platform, includeDisabled, parallel, ); err != nil { return merged, repoCacheWithMerged, err } diff --git a/internal/app/forklift/cli/images.go b/internal/app/forklift/cli/images.go index 041f3015..4c1ad256 100644 --- a/internal/app/forklift/cli/images.go +++ b/internal/app/forklift/cli/images.go @@ -18,7 +18,7 @@ import ( // Download func DownloadImagesForStoreApply( - indent int, store *forklift.FSStageStore, toolVersion, bundleMinVersion string, + indent int, store *forklift.FSStageStore, platform, toolVersion, bundleMinVersion string, parallel, ignoreToolVersion bool, ) error { next, hasNext := store.GetNext() @@ -39,7 +39,7 @@ func DownloadImagesForStoreApply( "Downloading Docker container images specified by the last successfully-applied staged " + "pallet bundle, in case the next to be applied fails to be applied...", ) - if err := DownloadImages(indent, bundle, bundle, false, parallel); err != nil { + if err := DownloadImages(indent, bundle, bundle, platform, false, parallel); err != nil { return err } } @@ -57,7 +57,7 @@ func DownloadImagesForStoreApply( "Downloading Docker container images specified by the next staged pallet bundle to be " + "applied...", ) - if err := DownloadImages(indent, bundle, bundle, false, parallel); err != nil { + if err := DownloadImages(indent, bundle, bundle, platform, false, parallel); err != nil { return err } fmt.Println() @@ -67,7 +67,7 @@ func DownloadImagesForStoreApply( func DownloadImages( indent int, deplsLoader ResolvedDeplsLoader, pkgLoader forklift.FSPkgLoader, - includeDisabled, parallel bool, + platform string, includeDisabled, parallel bool, ) error { orderedImages, err := ListRequiredImages(deplsLoader, pkgLoader, includeDisabled) if err != nil { @@ -85,9 +85,9 @@ func DownloadImages( } if parallel { - return downloadImagesParallel(indent, orderedImages, dc) + return downloadImagesParallel(indent, orderedImages, platform, dc) } - return downloadImagesSerial(indent, orderedImages, dc) + return downloadImagesSerial(indent, orderedImages, platform, dc) } func ListRequiredImages( @@ -132,12 +132,12 @@ func ListRequiredImages( return orderedImages, nil } -func downloadImagesParallel(indent int, images []string, dc *docker.Client) error { +func downloadImagesParallel(indent int, images []string, platform string, dc *docker.Client) error { eg, egctx := errgroup.WithContext(context.Background()) for _, image := range images { eg.Go(func() error { IndentedPrintf(indent, "Downloading %s...\n", image) - pulled, err := dc.PullImage(egctx, image, docker.NewOutStream(io.Discard)) + pulled, err := dc.PullImage(egctx, image, platform, docker.NewOutStream(io.Discard)) if err != nil { return errors.Wrapf(err, "couldn't download %s", image) } @@ -153,11 +153,12 @@ func downloadImagesParallel(indent int, images []string, dc *docker.Client) erro return nil } -func downloadImagesSerial(indent int, images []string, dc *docker.Client) error { +func downloadImagesSerial(indent int, images []string, platform string, dc *docker.Client) error { for _, image := range images { IndentedPrintf(indent, "Downloading %s...\n", image) pulled, err := dc.PullImage( - context.Background(), image, docker.NewOutStream(cli.NewIndentedWriter(indent+1, os.Stdout)), + context.Background(), image, platform, + docker.NewOutStream(cli.NewIndentedWriter(indent+1, os.Stdout)), ) if err != nil { return errors.Wrapf(err, "couldn't download %s", image) diff --git a/internal/app/forklift/cli/staging.go b/internal/app/forklift/cli/staging.go index 3258b99c..2a5a878f 100644 --- a/internal/app/forklift/cli/staging.go +++ b/internal/app/forklift/cli/staging.go @@ -36,7 +36,8 @@ func GetStageStore( func SetNextStagedBundle( indent int, store *forklift.FSStageStore, index int, exportPath, - toolVersion, bundleMinVersion string, skipImageCaching, parallel, ignoreToolVersion bool, + toolVersion, bundleMinVersion string, skipImageCaching bool, platform string, parallel, + ignoreToolVersion bool, ) error { store.SetNext(index) fmt.Printf( @@ -51,7 +52,7 @@ func SetNextStagedBundle( } if err := DownloadImagesForStoreApply( - indent, store, toolVersion, bundleMinVersion, parallel, ignoreToolVersion, + indent, store, platform, toolVersion, bundleMinVersion, parallel, ignoreToolVersion, ); err != nil { return errors.Wrap(err, "couldn't cache Docker container images required by staged pallet") } @@ -76,14 +77,15 @@ type StagingCaches struct { func StagePallet( indent int, merged *forklift.FSPallet, stageStore *forklift.FSStageStore, caches StagingCaches, exportPath string, versions StagingVersions, - skipImageCaching, parallel, ignoreToolVersion bool, + skipImageCaching bool, platform string, parallel, ignoreToolVersion bool, ) (index int, err error) { if _, isMerged := merged.FS.(*forklift.MergeFS); isMerged { return 0, errors.Errorf("the pallet provided for staging should not be a merged pallet!") } merged, repoCacheWithMerged, err := CacheStagingReqs( - 0, merged, caches.Mirrors, caches.Pallets, caches.Repos, caches.Downloads, false, parallel, + 0, merged, caches.Mirrors, caches.Pallets, caches.Repos, caches.Downloads, + platform, false, parallel, ) if err != nil { return 0, errors.Wrap(err, "couldn't cache requirements for staging the pallet") @@ -110,7 +112,7 @@ func StagePallet( } if err = SetNextStagedBundle( indent, stageStore, index, exportPath, versions.Core.Tool, versions.MinSupportedBundle, - skipImageCaching, parallel, ignoreToolVersion, + skipImageCaching, platform, parallel, ignoreToolVersion, ); err != nil { return index, errors.Wrapf( err, "couldn't prepare staged pallet bundle %d to be applied next", index, @@ -178,7 +180,9 @@ func newBundleManifest( desc.Pallet.Version, desc.Pallet.Clean = CheckGitRepoVersion(merged.FS.Path()) palletReqs, err := merged.LoadFSPalletReqs("**") if err != nil { - return desc, errors.Wrapf(err, "couldn't determine pallets required by pallet %s", merged.Path()) + return desc, errors.Wrapf( + err, "couldn't determine pallets required by pallet %s", merged.Path(), + ) } for _, req := range palletReqs { if desc.Includes.Pallets[req.RequiredPath], err = newBundlePalletInclusion( diff --git a/internal/clients/crane/export.go b/internal/clients/crane/export.go index c6f15a08..66d0521a 100644 --- a/internal/clients/crane/export.go +++ b/internal/clients/crane/export.go @@ -12,14 +12,22 @@ import ( "github.com/pkg/errors" ) -func ExportOCIImage(ctx context.Context, imageName string, w io.Writer) error { +type Platform = v1.Platform + +func ExportOCIImage( + ctx context.Context, imageName string, w io.Writer, platform string, +) error { ref, err := name.ParseReference(imageName, name.StrictValidation) if err != nil { return errors.Wrapf(err, "couldn't parse image name: %s", imageName) } imageName = ref.Name() - desc, err := crane.Get(imageName, crane.WithContext(ctx), crane.WithPlatform(detectPlatform())) + parsedPlatform, err := v1.ParsePlatform(platform) + if err != nil { + return errors.Wrapf(err, "couldn't parse platform: %s", platform) + } + desc, err := crane.Get(imageName, crane.WithContext(ctx), crane.WithPlatform(parsedPlatform)) if err != nil { return errors.Wrapf(err, "couldn't pull image %s", imageName) } @@ -36,9 +44,9 @@ func ExportOCIImage(ctx context.Context, imageName string, w io.Writer) error { return crane.Export(image, w) } -func detectPlatform() *v1.Platform { +func DetectPlatform() Platform { detectedPlatform := platforms.Normalize(platforms.DefaultSpec()) - return &v1.Platform{ + return Platform{ Architecture: detectedPlatform.Architecture, OS: detectedPlatform.OS, Variant: detectedPlatform.Variant, diff --git a/internal/clients/docker/images.go b/internal/clients/docker/images.go index cf221cca..58121a29 100644 --- a/internal/clients/docker/images.go +++ b/internal/clients/docker/images.go @@ -134,7 +134,7 @@ func CompareDeletedImages(i, j dti.DeleteResponse) int { } func (c *Client) PullImage( - ctx context.Context, taggedName string, outStream *streams.Out, + ctx context.Context, taggedName, platform string, outStream *streams.Out, ) (trust.ImageRefAndAuth, error) { // This function is adapted from the github.com/docker/cli/cli/command/image // package's RunPull function, which is licensed under Apache-2.0. This function was changed to @@ -157,7 +157,7 @@ func (c *Client) PullImage( ) } - if err = c.pullImage(ctx, imgRefAndAuth, outStream); err != nil { + if err = c.pullImage(ctx, imgRefAndAuth, platform, outStream); err != nil { return trust.ImageRefAndAuth{}, err } @@ -173,7 +173,7 @@ func authResolver(ctx context.Context, index *dtr.IndexInfo) dtr.AuthConfig { } func (c *Client) pullImage( - ctx context.Context, imgRefAndAuth trust.ImageRefAndAuth, out *streams.Out, + ctx context.Context, imgRefAndAuth trust.ImageRefAndAuth, platform string, out *streams.Out, ) (err error) { // This function is adapted from the github.com/docker/cli/cli/command/image // package's imagePullPrivileged function, which is licensed under Apache-2.0. This function was @@ -186,6 +186,7 @@ func (c *Client) pullImage( responseBody, err := c.Client.ImagePull( ctx, reference.FamiliarString(imgRefAndAuth.Reference()), dti.PullOptions{ RegistryAuth: encodedAuth, + Platform: platform, }, ) if err != nil {