Skip to content

Commit 49f274a

Browse files
committed
Use Desktop proxy for OCI registry operations
Assisted-By: docker-agent
1 parent f5c79ea commit 49f274a

7 files changed

Lines changed: 100 additions & 9 deletions

File tree

cmd/root/push.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func runPushCommand(cmd *cobra.Command, args []string) error {
5151

5252
out.Printf("Pushing agent %s to %s\n", agentFilename, tag)
5353

54-
err = remote.Push(tag)
54+
err = remote.Push(ctx, tag)
5555
if err != nil {
5656
return fmt.Errorf("failed to push artifact: %w", err)
5757
}

pkg/remote/pull.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212

1313
// Pull pulls an artifact from a registry and stores it in the content store
1414
func Pull(ctx context.Context, registryRef string, force bool, opts ...crane.Option) (string, error) {
15-
opts = append(opts, crane.WithContext(ctx))
15+
opts = append(opts, crane.WithContext(ctx), crane.WithTransport(newTransport(ctx)))
1616

1717
ref, err := name.ParseReference(registryRef)
1818
if err != nil {

pkg/remote/pull_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func TestPullIntegration(t *testing.T) {
6868
require.NoError(t, err)
6969
assert.NotNil(t, retrievedImg)
7070

71-
err = Push("invalid:reference:with:too:many:colons")
71+
err = Push(t.Context(), "invalid:reference:with:too:many:colons")
7272
require.Error(t, err)
7373
}
7474

pkg/remote/push.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package remote
22

33
import (
4+
"context"
45
"fmt"
56

67
"github.com/google/go-containerregistry/pkg/crane"
@@ -13,7 +14,7 @@ import (
1314
)
1415

1516
// Push pushes an artifact from the content store to an OCI registry
16-
func Push(reference string) error {
17+
func Push(ctx context.Context, reference string) error {
1718
store, err := content.NewStore()
1819
if err != nil {
1920
return fmt.Errorf("creating content store: %w", err)
@@ -45,7 +46,7 @@ func Push(reference string) error {
4546
return fmt.Errorf("parsing registry reference %s: %w", reference, err)
4647
}
4748

48-
if err := crane.Push(img, ref.String()); err != nil {
49+
if err := crane.Push(img, ref.String(), crane.WithContext(ctx), crane.WithTransport(newTransport(ctx))); err != nil {
4950
return fmt.Errorf("pushing image to registry %s: %w", reference, err)
5051
}
5152

pkg/remote/push_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,18 @@ func TestPush(t *testing.T) {
3838
require.NoError(t, err)
3939
assert.NotNil(t, loadedImg)
4040

41-
err = Push("invalid:reference:with:too:many:colons")
41+
err = Push(t.Context(), "invalid:reference:with:too:many:colons")
4242
require.Error(t, err)
4343

44-
err = Push("invalid:reference:with:too:many:colons")
44+
err = Push(t.Context(), "invalid:reference:with:too:many:colons")
4545
require.Error(t, err)
4646
}
4747

4848
func TestPushNonExistentArtifact(t *testing.T) {
49-
err := Push("registry.example.com/test:latest")
49+
err := Push(t.Context(), "registry.example.com/test:latest")
5050
require.Error(t, err)
5151

52-
err = Push("registry.example.com/test:latest")
52+
err = Push(t.Context(), "registry.example.com/test:latest")
5353
require.Error(t, err)
5454
}
5555

pkg/remote/transport.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package remote
2+
3+
import (
4+
"context"
5+
"net"
6+
"net/http"
7+
"net/url"
8+
9+
"github.com/docker/docker-agent/pkg/desktop"
10+
socket "github.com/docker/docker-agent/pkg/desktop/socket"
11+
)
12+
13+
// newTransport returns an HTTP transport that uses Docker Desktop proxy if available.
14+
func newTransport(ctx context.Context) http.RoundTripper {
15+
t, ok := http.DefaultTransport.(*http.Transport)
16+
if !ok {
17+
return http.DefaultTransport
18+
}
19+
transport := t.Clone()
20+
21+
if desktop.IsDockerDesktopRunning(ctx) {
22+
// Route all traffic through Docker Desktop's HTTP proxy socket
23+
// Set a dummy proxy URL - the actual connection happens via DialContext
24+
transport.Proxy = http.ProxyURL(&url.URL{
25+
Scheme: "http",
26+
})
27+
// Override the dialer to connect to the Unix socket for the proxy
28+
transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
29+
return socket.DialUnix(desktop.Paths().ProxySocket)
30+
}
31+
}
32+
33+
return transport
34+
}

pkg/remote/transport_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package remote
2+
3+
import (
4+
"net/http"
5+
"net/http/httptest"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
11+
"github.com/docker/docker-agent/pkg/desktop"
12+
)
13+
14+
func TestNewTransport_UsesDesktopProxyWhenAvailable(t *testing.T) {
15+
t.Parallel()
16+
17+
ctx := t.Context()
18+
19+
// Create a transport
20+
transport := newTransport(ctx)
21+
require.NotNil(t, transport)
22+
23+
// Verify that it's an http.Transport
24+
httpTransport, ok := transport.(*http.Transport)
25+
require.True(t, ok, "transport should be *http.Transport")
26+
27+
// If Docker Desktop is running, verify proxy is configured
28+
if desktop.IsDockerDesktopRunning(ctx) {
29+
assert.NotNil(t, httpTransport.Proxy, "proxy should be configured when Docker Desktop is running")
30+
assert.NotNil(t, httpTransport.DialContext, "custom DialContext should be set when Docker Desktop is running")
31+
}
32+
}
33+
34+
func TestNewTransport_WorksWithoutDesktopProxy(t *testing.T) {
35+
t.Parallel()
36+
37+
// Create a test server to simulate a registry
38+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
39+
w.WriteHeader(http.StatusOK)
40+
}))
41+
defer server.Close()
42+
43+
ctx := t.Context()
44+
45+
// Create a transport (should work whether Desktop is running or not)
46+
transport := newTransport(ctx)
47+
require.NotNil(t, transport)
48+
49+
// Make a simple HTTP request to verify the transport works
50+
client := &http.Client{Transport: transport}
51+
resp, err := client.Get(server.URL)
52+
require.NoError(t, err)
53+
defer resp.Body.Close()
54+
55+
assert.Equal(t, http.StatusOK, resp.StatusCode)
56+
}

0 commit comments

Comments
 (0)