Skip to content

Commit d585ff9

Browse files
committed
use containerd registry client
Signed-off-by: Nicolas De Loof <[email protected]>
1 parent 9752fa5 commit d585ff9

File tree

6 files changed

+95
-73
lines changed

6 files changed

+95
-73
lines changed

internal/ocipush/push.go

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -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-
7571
func 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
}

internal/registry/registry.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,28 @@
1616

1717
package registry
1818

19-
import "github.com/distribution/reference"
20-
2119
const (
20+
// DefaultNamespace is the default namespace
21+
DefaultNamespace = "docker.io"
22+
// DefaultRegistryHost is the hostname for the default (Docker Hub) registry
23+
// used for pushing and pulling images. This hostname is hard-coded to handle
24+
// the conversion from image references without registry name (e.g. "ubuntu",
25+
// or "ubuntu:latest"), as well as references using the "docker.io" domain
26+
// name, which is used as canonical reference for images on Docker Hub, but
27+
// does not match the domain-name of Docker Hub's registry.
28+
DefaultRegistryHost = "registry-1.docker.io"
2229
// IndexHostname is the index hostname, used for authentication and image search.
2330
IndexHostname = "index.docker.io"
2431
// IndexServer is used for user auth and image search
25-
IndexServer = "https://index.docker.io/v1/"
32+
IndexServer = "https://" + IndexHostname + "/v1/"
2633
// IndexName is the name of the index
2734
IndexName = "docker.io"
2835
)
2936

3037
// GetAuthConfigKey special-cases using the full index address of the official
3138
// index as the AuthConfig key, and uses the (host)name[:port] for private indexes.
32-
func GetAuthConfigKey(reposName reference.Named) string {
33-
indexName := reference.Domain(reposName)
34-
if indexName == IndexName || indexName == IndexHostname {
39+
func GetAuthConfigKey(indexName string) string {
40+
if indexName == IndexName || indexName == IndexHostname || indexName == DefaultRegistryHost {
3541
return IndexServer
3642
}
3743
return indexName

pkg/compose/publish.go

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,18 @@ import (
2727

2828
"github.com/DefangLabs/secret-detector/pkg/scanner"
2929
"github.com/DefangLabs/secret-detector/pkg/secrets"
30-
3130
"github.com/compose-spec/compose-go/v2/loader"
3231
"github.com/compose-spec/compose-go/v2/types"
32+
"github.com/containerd/containerd/v2/core/remotes/docker"
3333
"github.com/distribution/reference"
34-
"github.com/docker/buildx/util/imagetools"
3534
"github.com/docker/cli/cli/command"
3635
"github.com/docker/compose/v2/internal/ocipush"
36+
"github.com/docker/compose/v2/internal/registry"
3737
"github.com/docker/compose/v2/pkg/api"
3838
"github.com/docker/compose/v2/pkg/compose/transform"
3939
"github.com/docker/compose/v2/pkg/progress"
4040
"github.com/docker/compose/v2/pkg/prompt"
41+
v1 "github.com/opencontainers/image-spec/specs-go/v1"
4142
)
4243

4344
func (s *composeService) Publish(ctx context.Context, project *types.Project, repository string, options api.PublishOptions) error {
@@ -64,11 +65,27 @@ func (s *composeService) publish(ctx context.Context, project *types.Project, re
6465
return err
6566
}
6667

67-
resolver := imagetools.New(imagetools.Opt{
68-
Auth: s.configFile(),
68+
config := s.dockerCli.ConfigFile()
69+
70+
resolver := docker.NewResolver(docker.ResolverOptions{
71+
Hosts: docker.ConfigureDefaultRegistries(
72+
docker.WithAuthorizer(docker.NewDockerAuthorizer(
73+
docker.WithAuthCreds(func(host string) (string, string, error) {
74+
host = registry.GetAuthConfigKey(host)
75+
auth, err := config.GetAuthConfig(host)
76+
if err != nil {
77+
return "", "", err
78+
}
79+
if auth.IdentityToken != "" {
80+
return "", auth.IdentityToken, nil
81+
}
82+
return auth.Username, auth.Password, nil
83+
}),
84+
)),
85+
),
6986
})
7087

71-
var layers []ocipush.Pushable
88+
var layers []v1.Descriptor
7289
extFiles := map[string]string{}
7390
for _, file := range project.ComposeFiles {
7491
data, err := processFile(ctx, file, project, extFiles)
@@ -77,10 +94,7 @@ func (s *composeService) publish(ctx context.Context, project *types.Project, re
7794
}
7895

7996
layerDescriptor := ocipush.DescriptorForComposeFile(file, data)
80-
layers = append(layers, ocipush.Pushable{
81-
Descriptor: layerDescriptor,
82-
Data: data,
83-
})
97+
layers = append(layers, layerDescriptor)
8498
}
8599

86100
extLayers, err := processExtends(ctx, project, extFiles)
@@ -100,10 +114,7 @@ func (s *composeService) publish(ctx context.Context, project *types.Project, re
100114
}
101115

102116
layerDescriptor := ocipush.DescriptorForComposeFile("image-digests.yaml", yaml)
103-
layers = append(layers, ocipush.Pushable{
104-
Descriptor: layerDescriptor,
105-
Data: yaml,
106-
})
117+
layers = append(layers, layerDescriptor)
107118
}
108119

109120
w := progress.ContextWriter(ctx)
@@ -131,8 +142,8 @@ func (s *composeService) publish(ctx context.Context, project *types.Project, re
131142
return nil
132143
}
133144

134-
func processExtends(ctx context.Context, project *types.Project, extFiles map[string]string) ([]ocipush.Pushable, error) {
135-
var layers []ocipush.Pushable
145+
func processExtends(ctx context.Context, project *types.Project, extFiles map[string]string) ([]v1.Descriptor, error) {
146+
var layers []v1.Descriptor
136147
moreExtFiles := map[string]string{}
137148
for xf, hash := range extFiles {
138149
data, err := processFile(ctx, xf, project, moreExtFiles)
@@ -142,10 +153,7 @@ func processExtends(ctx context.Context, project *types.Project, extFiles map[st
142153

143154
layerDescriptor := ocipush.DescriptorForComposeFile(hash, data)
144155
layerDescriptor.Annotations["com.docker.compose.extends"] = "true"
145-
layers = append(layers, ocipush.Pushable{
146-
Descriptor: layerDescriptor,
147-
Data: data,
148-
})
156+
layers = append(layers, layerDescriptor)
149157
}
150158
for f, hash := range moreExtFiles {
151159
if _, ok := extFiles[f]; ok {
@@ -343,8 +351,8 @@ func acceptPublishBindMountDeclarations(cli command.Cli) (bool, error) {
343351
return confirm, err
344352
}
345353

346-
func envFileLayers(project *types.Project) []ocipush.Pushable {
347-
var layers []ocipush.Pushable
354+
func envFileLayers(project *types.Project) []v1.Descriptor {
355+
var layers []v1.Descriptor
348356
for _, service := range project.Services {
349357
for _, envFile := range service.EnvFiles {
350358
f, err := os.ReadFile(envFile.Path)
@@ -353,10 +361,7 @@ func envFileLayers(project *types.Project) []ocipush.Pushable {
353361
continue
354362
}
355363
layerDescriptor := ocipush.DescriptorForEnvFile(envFile.Path, f)
356-
layers = append(layers, ocipush.Pushable{
357-
Descriptor: layerDescriptor,
358-
Data: f,
359-
})
364+
layers = append(layers, layerDescriptor)
360365
}
361366
}
362367
return layers

pkg/compose/publish_test.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323

2424
"github.com/compose-spec/compose-go/v2/loader"
2525
"github.com/compose-spec/compose-go/v2/types"
26-
"github.com/docker/compose/v2/internal/ocipush"
2726
"github.com/docker/compose/v2/pkg/api"
2827
v1 "github.com/opencontainers/image-spec/specs-go/v1"
2928
"gotest.tools/v3/assert"
@@ -58,17 +57,15 @@ services:
5857

5958
b, err := os.ReadFile("testdata/publish/common.yaml")
6059
assert.NilError(t, err)
61-
assert.DeepEqual(t, []ocipush.Pushable{
60+
assert.DeepEqual(t, []v1.Descriptor{
6261
{
63-
Descriptor: v1.Descriptor{
64-
MediaType: "application/vnd.docker.compose.file+yaml",
65-
Digest: "sha256:d3ba84507b56ec783f4b6d24306b99a15285f0a23a835f0b668c2dbf9c59c241",
66-
Size: 32,
67-
Annotations: map[string]string{
68-
"com.docker.compose.extends": "true",
69-
"com.docker.compose.file": "f8f9ede3d201ec37d5a5e3a77bbadab79af26035e53135e19571f50d541d390c.yaml",
70-
"com.docker.compose.version": api.ComposeVersion,
71-
},
62+
MediaType: "application/vnd.docker.compose.file+yaml",
63+
Digest: "sha256:d3ba84507b56ec783f4b6d24306b99a15285f0a23a835f0b668c2dbf9c59c241",
64+
Size: 32,
65+
Annotations: map[string]string{
66+
"com.docker.compose.extends": "true",
67+
"com.docker.compose.file": "f8f9ede3d201ec37d5a5e3a77bbadab79af26035e53135e19571f50d541d390c.yaml",
68+
"com.docker.compose.version": api.ComposeVersion,
7269
},
7370
Data: b,
7471
},

pkg/compose/pull.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ func ImageDigestResolver(ctx context.Context, file *configfile.ConfigFile, apiCl
280280
}
281281

282282
func encodedAuth(ref reference.Named, configFile driver.Auth) (string, error) {
283-
authConfig, err := configFile.GetAuthConfig(registry.GetAuthConfigKey(ref))
283+
authConfig, err := configFile.GetAuthConfig(registry.GetAuthConfigKey(reference.Domain(ref)))
284284
if err != nil {
285285
return "", err
286286
}

pkg/compose/push.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func (s *composeService) pushServiceImage(ctx context.Context, tag string, confi
9090
return err
9191
}
9292

93-
authConfig, err := configFile.GetAuthConfig(registry.GetAuthConfigKey(ref))
93+
authConfig, err := configFile.GetAuthConfig(registry.GetAuthConfigKey(reference.Domain(ref)))
9494
if err != nil {
9595
return err
9696
}

0 commit comments

Comments
 (0)