@@ -3,11 +3,14 @@ package local
33import (
44 "context"
55 "fmt"
6+ "runtime"
67
78 cerrdefs "github.com/containerd/errdefs"
89 v1 "github.com/google/go-containerregistry/pkg/v1"
910 "github.com/moby/moby/api/types/image"
1011 "github.com/moby/moby/client"
12+ "github.com/moby/moby/client/pkg/versions"
13+ ocispec "github.com/opencontainers/image-spec/specs-go/v1"
1114
1215 "github.com/buildpacks/imgutil"
1316)
@@ -21,12 +24,13 @@ func NewImage(repoName string, dockerClient DockerClient, ops ...imgutil.ImageOp
2124 }
2225
2326 var err error
24- options .Platform , err = processPlatformOption (options .Platform , dockerClient )
27+ var isPlatformAware bool
28+ options .Platform , isPlatformAware , err = processPlatformOption (options .Platform , dockerClient )
2529 if err != nil {
2630 return nil , err
2731 }
2832
29- previousImage , err := processImageOption (options .PreviousImageRepoName , dockerClient , true )
33+ previousImage , err := processImageOption (options .PreviousImageRepoName , isPlatformAware , options . Platform , dockerClient , true )
3034 if err != nil {
3135 return nil , err
3236 }
@@ -38,7 +42,7 @@ func NewImage(repoName string, dockerClient DockerClient, ops ...imgutil.ImageOp
3842 baseIdentifier string
3943 store * Store
4044 )
41- baseImage , err := processImageOption (options .BaseImageRepoName , dockerClient , false )
45+ baseImage , err := processImageOption (options .BaseImageRepoName , isPlatformAware , options . Platform , dockerClient , false )
4246 if err != nil {
4347 return nil , err
4448 }
@@ -47,7 +51,11 @@ func NewImage(repoName string, dockerClient DockerClient, ops ...imgutil.ImageOp
4751 baseIdentifier = baseImage .identifier
4852 store = baseImage .layerStore
4953 } else {
50- store = NewStore (dockerClient )
54+ if isPlatformAware {
55+ store = NewStoreWithPlatform (dockerClient , options .Platform )
56+ } else {
57+ store = NewStore (dockerClient )
58+ }
5159 }
5260
5361 cnbImage , err := imgutil .NewCNBImage (* options )
@@ -64,30 +72,35 @@ func NewImage(repoName string, dockerClient DockerClient, ops ...imgutil.ImageOp
6472 }, nil
6573}
6674
67- func defaultPlatform (dockerClient DockerClient ) (imgutil.Platform , error ) {
75+ func defaultPlatform (dockerClient DockerClient ) (imgutil.Platform , bool , error ) {
6876 daemonInfo , err := dockerClient .ServerVersion (context .Background (), client.ServerVersionOptions {})
6977 if err != nil {
70- return imgutil.Platform {}, err
78+ return imgutil.Platform {}, false , err
79+ }
80+ isPlatformAware := versions .GreaterThanOrEqualTo (daemonInfo .APIVersion , "1.49" )
81+ // When running on a different architecture than the daemon, we want to use images matching our own architecture
82+ // https://github.com/buildpacks/lifecycle/issues/1599
83+ if isPlatformAware {
84+ return imgutil.Platform {
85+ OS : runtime .GOOS ,
86+ Architecture : runtime .GOARCH ,
87+ }, isPlatformAware , nil
7188 }
7289 return imgutil.Platform {
7390 OS : daemonInfo .Os ,
7491 Architecture : daemonInfo .Arch ,
75- }, nil
92+ }, isPlatformAware , nil
7693}
7794
78- func processPlatformOption (requestedPlatform imgutil.Platform , dockerClient DockerClient ) (imgutil.Platform , error ) {
79- dockerPlatform , err := defaultPlatform (dockerClient )
95+ func processPlatformOption (requestedPlatform imgutil.Platform , dockerClient DockerClient ) (imgutil.Platform , bool , error ) {
96+ defaultPlatform , isPlatformAware , err := defaultPlatform (dockerClient )
8097 if err != nil {
81- return imgutil.Platform {}, err
98+ return imgutil.Platform {}, false , err
8299 }
83100 if (requestedPlatform == imgutil.Platform {}) {
84- return dockerPlatform , nil
85- }
86- if requestedPlatform .OS != "" && requestedPlatform .OS != dockerPlatform .OS {
87- return imgutil.Platform {},
88- fmt .Errorf ("invalid os: platform os %q must match the daemon os %q" , requestedPlatform .OS , dockerPlatform .OS )
101+ return defaultPlatform , isPlatformAware , nil
89102 }
90- return requestedPlatform , nil
103+ return requestedPlatform , isPlatformAware , nil
91104}
92105
93106type imageResult struct {
@@ -96,25 +109,52 @@ type imageResult struct {
96109 layerStore * Store
97110}
98111
99- func processImageOption (repoName string , dockerClient DockerClient , downloadLayersOnAccess bool ) (imageResult , error ) {
112+ func processImageOption (repoName string , isPlatformAware bool , platform imgutil. Platform , dockerClient DockerClient , downloadLayersOnAccess bool ) (imageResult , error ) {
100113 if repoName == "" {
101114 return imageResult {}, nil
102115 }
103116 inspect , history , err := getInspectAndHistory (repoName , dockerClient )
104117 if err != nil {
105118 return imageResult {}, err
106119 }
120+
107121 if inspect == nil {
108122 return imageResult {}, nil
109123 }
110- layerStore := NewStore (dockerClient )
124+
125+ // Always use the platform-unaware image ID
126+ identifier := inspect .ID
127+
128+ // Try using the platform-specific inspected value if possible, otherwise fall back to the generic inspect
129+ if isPlatformAware {
130+ platformInspect , platformHistory , err := getPlatformAwareInspectAndHistory (repoName , platform , dockerClient )
131+ if err != nil {
132+ return imageResult {}, err
133+ }
134+ if platformInspect != nil && platformHistory != nil {
135+ inspect = platformInspect
136+ history = platformHistory
137+ }
138+ }
139+
140+ var layerStore * Store
141+ if isPlatformAware {
142+ layerStore = NewStoreWithPlatform (dockerClient , imgutil.Platform {
143+ Architecture : inspect .Architecture ,
144+ OS : inspect .Os ,
145+ OSVersion : inspect .OsVersion ,
146+ Variant : inspect .Variant ,
147+ })
148+ } else {
149+ layerStore = NewStore (dockerClient )
150+ }
111151 v1Image , err := newV1ImageFacadeFromInspect (* inspect , history , layerStore , downloadLayersOnAccess )
112152 if err != nil {
113153 return imageResult {}, err
114154 }
115155 return imageResult {
116156 image : v1Image ,
117- identifier : inspect . ID ,
157+ identifier : identifier ,
118158 layerStore : layerStore ,
119159 }, nil
120160}
@@ -131,5 +171,33 @@ func getInspectAndHistory(repoName string, dockerClient DockerClient) (*image.In
131171 if err != nil {
132172 return nil , nil , fmt .Errorf ("get history for image %q: %w" , repoName , err )
133173 }
174+
134175 return & inspect .InspectResponse , historyResult .Items , nil
135176}
177+
178+ func getPlatformAwareInspectAndHistory (repoName string , platform imgutil.Platform , dockerClient DockerClient ) (* image.InspectResponse , []image.HistoryResponseItem , error ) {
179+ ociPlatform := ocispec.Platform {
180+ Architecture : platform .Architecture ,
181+ OS : platform .OS ,
182+ OSVersion : platform .OSVersion ,
183+ Variant : platform .Variant ,
184+ }
185+
186+ platformHistoryResult , err := dockerClient .ImageHistory (context .Background (), repoName , client .ImageHistoryWithPlatform (ociPlatform ))
187+ if err != nil {
188+ if cerrdefs .IsNotFound (err ) {
189+ return nil , nil , nil
190+ }
191+ return nil , nil , fmt .Errorf ("get history for image %q: %w" , repoName , err )
192+ }
193+
194+ platformInspect , err := dockerClient .ImageInspect (context .Background (), repoName , client .ImageInspectWithPlatform (& ociPlatform ))
195+ if err != nil {
196+ if cerrdefs .IsNotFound (err ) {
197+ return nil , nil , nil
198+ }
199+ return nil , nil , fmt .Errorf ("inspecting platform-specific image %q: %w" , repoName , err )
200+ }
201+
202+ return & platformInspect .InspectResponse , platformHistoryResult .Items , nil
203+ }
0 commit comments