Skip to content

Commit aa3a10d

Browse files
committed
e2e: add private registry pull/push regression test
Add authenticated private-registry service to the e2e compose stack, fixture auth config, and TestPullPushPrivateRepository to verify unauthenticated pull/push fails and authenticated pull/push succeeds. Fix compose volume path: paths are resolved relative to e2e/, so use ./testdata/registry/auth instead of ./e2e/testdata/... Regression coverage for #5963 and #5965. Carries forward #6940. Signed-off-by: aryansharma9917 <sharmaaryan9837@gmail.com> Signed-off-by: Lohit Kolluri <lohitkolluri@gmail.com>
1 parent 9f16882 commit aa3a10d

4 files changed

Lines changed: 130 additions & 1 deletion

File tree

e2e/compose-env.yaml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,22 @@ services:
33
registry:
44
image: 'registry:3'
55

6+
privateregistry:
7+
image: 'registry:3'
8+
environment:
9+
- REGISTRY_HTTP_ADDR=0.0.0.0:5001
10+
- REGISTRY_AUTH=htpasswd
11+
- REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm
12+
- REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd
13+
volumes:
14+
- ./testdata/registry/auth:/auth:ro
15+
616
engine:
717
image: 'docker:${ENGINE_VERSION:-29}-dind'
818
privileged: true
9-
command: ['--insecure-registry=registry:5000', '--experimental']
19+
depends_on:
20+
- registry
21+
- privateregistry
22+
command: ['--insecure-registry=registry:5000', '--insecure-registry=privateregistry:5001', '--experimental']
1023
environment:
1124
- DOCKER_TLS_CERTDIR=

e2e/image/private_test.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package image
2+
3+
import (
4+
"strings"
5+
"testing"
6+
"time"
7+
8+
"github.com/docker/cli/e2e/internal/fixtures"
9+
"gotest.tools/v3/assert"
10+
"gotest.tools/v3/icmd"
11+
)
12+
13+
const privateRegistryPrefix = "privateregistry:5001"
14+
15+
// Regression test for https://github.com/docker/cli/issues/5963
16+
func TestPullPushPrivateRepository(t *testing.T) {
17+
t.Parallel()
18+
19+
dir := fixtures.SetupConfigFile(t)
20+
t.Cleanup(dir.Remove)
21+
emptyConfigDir := t.TempDir()
22+
23+
sourceImage := fixtures.AlpineImage
24+
privateImage := privateRegistryPrefix + "/private/alpine:test-private-pull-push"
25+
26+
runWithPrivateRegistryRetry(t,
27+
icmd.Command("docker", "pull", sourceImage),
28+
).Assert(t, icmd.Success)
29+
t.Cleanup(func() {
30+
icmd.RunCommand("docker", "image", "rm", "-f", privateImage).Assert(t, icmd.Success)
31+
})
32+
33+
icmd.RunCommand("docker", "tag", sourceImage, privateImage).Assert(t, icmd.Success)
34+
35+
pushNoAuth := runWithPrivateRegistryRetry(t,
36+
icmd.Command("docker", "push", privateImage),
37+
fixtures.WithConfig(emptyConfigDir),
38+
)
39+
pushNoAuth.Assert(t, icmd.Expected{ExitCode: 1})
40+
assertAuthDenied(t, pushNoAuth)
41+
42+
pushWithAuth := runWithPrivateRegistryRetry(t,
43+
icmd.Command("docker", "push", privateImage),
44+
fixtures.WithConfig(dir.Path()),
45+
)
46+
pushWithAuth.Assert(t, icmd.Success)
47+
assert.Check(t, strings.Contains(pushWithAuth.Combined(), "The push refers to repository ["+privateImage+"]"), pushWithAuth.Combined())
48+
49+
icmd.RunCommand("docker", "image", "rm", "-f", privateImage).Assert(t, icmd.Success)
50+
51+
pullNoAuth := runWithPrivateRegistryRetry(t,
52+
icmd.Command("docker", "pull", privateImage),
53+
fixtures.WithConfig(emptyConfigDir),
54+
)
55+
pullNoAuth.Assert(t, icmd.Expected{ExitCode: 1})
56+
assertAuthDenied(t, pullNoAuth)
57+
58+
pullWithAuth := runWithPrivateRegistryRetry(t,
59+
icmd.Command("docker", "pull", privateImage),
60+
fixtures.WithConfig(dir.Path()),
61+
)
62+
pullWithAuth.Assert(t, icmd.Success)
63+
assert.Check(t, strings.Contains(pullWithAuth.Combined(), privateImage), pullWithAuth.Combined())
64+
}
65+
66+
func assertAuthDenied(t *testing.T, result *icmd.Result) {
67+
t.Helper()
68+
output := result.Combined()
69+
if isPrivateRegistryTransient(output) {
70+
t.Fatalf("private registry unavailable while expecting auth failure: %s", output)
71+
}
72+
73+
assert.Check(t,
74+
strings.Contains(output, "requested access to the resource is denied") ||
75+
strings.Contains(output, "no basic auth credentials") ||
76+
strings.Contains(output, "unauthorized") ||
77+
strings.Contains(output, "authentication required"),
78+
output,
79+
)
80+
}
81+
82+
func runWithPrivateRegistryRetry(t *testing.T, cmd icmd.Cmd, opts ...icmd.CmdOp) *icmd.Result {
83+
t.Helper()
84+
85+
deadline := time.Now().Add(90 * time.Second)
86+
for {
87+
result := icmd.RunCmd(cmd, opts...)
88+
output := result.Combined()
89+
if isPrivateRegistryTransient(output) {
90+
if time.Now().Before(deadline) {
91+
t.Logf("waiting for private registry availability: %s", output)
92+
time.Sleep(500 * time.Millisecond)
93+
continue
94+
}
95+
}
96+
return result
97+
}
98+
}
99+
100+
func isPrivateRegistryTransient(output string) bool {
101+
return strings.Contains(output, "lookup privateregistry") ||
102+
strings.Contains(output, "lookup registry") ||
103+
strings.Contains(output, "no such host") ||
104+
strings.Contains(output, "server misbehaving") ||
105+
strings.Contains(output, "Temporary failure in name resolution") ||
106+
strings.Contains(output, "connection refused") ||
107+
strings.Contains(output, "i/o timeout") ||
108+
strings.Contains(output, "TLS handshake timeout") ||
109+
strings.Contains(output, "context deadline exceeded") ||
110+
strings.Contains(output, "connection reset by peer") ||
111+
strings.Contains(output, "unexpected EOF")
112+
}

e2e/internal/fixtures/fixtures.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ func SetupConfigFile(t *testing.T) fs.Dir {
2323
"auths": {
2424
"registry:5000": {
2525
"auth": "ZWlhaXM6cGFzc3dvcmQK"
26+
},
27+
"privateregistry:5001": {
28+
"auth": "ZTJlOnBhc3N3b3Jk"
2629
}
2730
}}`), fs.WithDir("trust", fs.WithDir("private")))
2831
return *dir
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
e2e:$2y$05$UozlY7.SA2NMcojF.qocv.W9Q4rsr75uLMW.mVEsAPx90BVeMgveC

0 commit comments

Comments
 (0)