@@ -26,9 +26,10 @@ import (
2626 "slices"
2727 "time"
2828
29+ "github.com/containerd/containerd/v2/core/remotes"
2930 pusherrors "github.com/containerd/containerd/v2/core/remotes/errors"
31+ "github.com/containerd/errdefs"
3032 "github.com/distribution/reference"
31- "github.com/docker/buildx/util/imagetools"
3233 "github.com/docker/compose/v2/pkg/api"
3334 "github.com/opencontainers/go-digest"
3435 "github.com/opencontainers/image-spec/specs-go"
@@ -67,11 +68,6 @@ var clientAuthStatusCodes = []int{
6768 http .StatusProxyAuthRequired ,
6869}
6970
70- type Pushable struct {
71- Descriptor v1.Descriptor
72- Data []byte
73- }
74-
7571func DescriptorForComposeFile (path string , content []byte ) v1.Descriptor {
7672 return v1.Descriptor {
7773 MediaType : ComposeYAMLMediaType ,
@@ -81,6 +77,7 @@ func DescriptorForComposeFile(path string, content []byte) v1.Descriptor {
8177 "com.docker.compose.version" : api .ComposeVersion ,
8278 "com.docker.compose.file" : filepath .Base (path ),
8379 },
80+ Data : content ,
8481 }
8582}
8683
@@ -93,27 +90,23 @@ func DescriptorForEnvFile(path string, content []byte) v1.Descriptor {
9390 "com.docker.compose.version" : api .ComposeVersion ,
9491 "com.docker.compose.envfile" : filepath .Base (path ),
9592 },
93+ Data : content ,
9694 }
9795}
9896
99- func PushManifest (
100- ctx context.Context ,
101- resolver * imagetools.Resolver ,
102- named reference.Named ,
103- layers []Pushable ,
104- ociVersion api.OCIVersion ,
105- ) error {
97+ func PushManifest (ctx context.Context , resolver remotes.Resolver , named reference.Named , layers []v1.Descriptor , ociVersion api.OCIVersion ) error {
10698 // Check if we need an extra empty layer for the manifest config
10799 if ociVersion == api .OCIVersion1_1 || ociVersion == "" {
108- if err := resolver .Push (ctx , named , v1 .DescriptorEmptyJSON , v1 .DescriptorEmptyJSON .Data ); err != nil {
100+ err := push (ctx , resolver , named , v1 .DescriptorEmptyJSON )
101+ if err != nil {
109102 return err
110103 }
111104 }
112105 // prepare to push the manifest by pushing the layers
113106 layerDescriptors := make ([]v1.Descriptor , len (layers ))
114107 for i := range layers {
115- layerDescriptors [i ] = layers [i ]. Descriptor
116- if err := resolver . Push (ctx , named , layers [ i ]. Descriptor , layers [i ]. Data ); err != nil {
108+ layerDescriptors [i ] = layers [i ]
109+ if err := push (ctx , resolver , named , layers [i ]); err != nil {
117110 return err
118111 }
119112 }
@@ -135,19 +128,38 @@ func PushManifest(
135128 return err
136129}
137130
138- func createAndPushManifest (
139- ctx context.Context ,
140- resolver * imagetools.Resolver ,
141- named reference.Named ,
142- layers []v1.Descriptor ,
143- ociVersion api.OCIVersion ,
144- ) error {
131+ func push (ctx context.Context , resolver remotes.Resolver , ref reference.Named , descriptor v1.Descriptor ) error {
132+ fullRef , err := reference .WithDigest (reference .TagNameOnly (ref ), descriptor .Digest )
133+ if err != nil {
134+ return err
135+ }
136+
137+ pusher , err := resolver .Pusher (ctx , fullRef .String ())
138+ if err != nil {
139+ return err
140+ }
141+ push , err := pusher .Push (ctx , descriptor )
142+ if errdefs .IsAlreadyExists (err ) {
143+ return nil
144+ }
145+ if err != nil {
146+ return err
147+ }
148+ defer func () {
149+ _ = push .Close ()
150+ }()
151+
152+ _ , err = push .Write (descriptor .Data )
153+ return err
154+ }
155+
156+ func createAndPushManifest (ctx context.Context , resolver remotes.Resolver , named reference.Named , layers []v1.Descriptor , ociVersion api.OCIVersion ) error {
145157 toPush , err := generateManifest (layers , ociVersion )
146158 if err != nil {
147159 return err
148160 }
149161 for _ , p := range toPush {
150- err = resolver . Push (ctx , named , p . Descriptor , p . Data )
162+ err = push (ctx , resolver , named , p )
151163 if err != nil {
152164 return err
153165 }
@@ -163,8 +175,8 @@ func isNonAuthClientError(statusCode int) bool {
163175 return ! slices .Contains (clientAuthStatusCodes , statusCode )
164176}
165177
166- func generateManifest (layers []v1.Descriptor , ociCompat api.OCIVersion ) ([]Pushable , error ) {
167- var toPush []Pushable
178+ func generateManifest (layers []v1.Descriptor , ociCompat api.OCIVersion ) ([]v1. Descriptor , error ) {
179+ var toPush []v1. Descriptor
168180 var config v1.Descriptor
169181 var artifactType string
170182 switch ociCompat {
@@ -184,16 +196,17 @@ func generateManifest(layers []v1.Descriptor, ociCompat api.OCIVersion) ([]Pusha
184196 MediaType : ComposeEmptyConfigMediaType ,
185197 Digest : digest .FromBytes (configData ),
186198 Size : int64 (len (configData )),
199+ Data : configData ,
187200 }
188201 // N.B. OCI 1.0 does NOT support specifying the artifact type, so it's
189202 // left as an empty string to omit it from the marshaled JSON
190203 artifactType = ""
191- toPush = append (toPush , Pushable { Descriptor : config , Data : configData } )
204+ toPush = append (toPush , config )
192205 case api .OCIVersion1_1 :
193206 config = v1 .DescriptorEmptyJSON
194207 artifactType = ComposeProjectArtifactType
195208 // N.B. the descriptor has the data embedded in it
196- toPush = append (toPush , Pushable { Descriptor : config , Data : make ([] byte , len ( config . Data ))} )
209+ toPush = append (toPush , config )
197210 default :
198211 return nil , fmt .Errorf ("unsupported OCI version: %s" , ociCompat )
199212 }
@@ -220,7 +233,8 @@ func generateManifest(layers []v1.Descriptor, ociCompat api.OCIVersion) ([]Pusha
220233 "com.docker.compose.version" : api .ComposeVersion ,
221234 },
222235 ArtifactType : artifactType ,
236+ Data : manifest ,
223237 }
224- toPush = append (toPush , Pushable { Descriptor : manifestDescriptor , Data : manifest } )
238+ toPush = append (toPush , manifestDescriptor )
225239 return toPush , nil
226240}
0 commit comments