Skip to content

Commit f98b390

Browse files
committed
chore: run SSH daemon in test dind container to create e2e test for docker-pussh
1 parent e5d4743 commit f98b390

File tree

8 files changed

+109
-70
lines changed

8 files changed

+109
-70
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
!internal/
77
!scripts/
88
!go.*
9+
!test/e2e/ssh/test_key.pub
910

1011
# Ignore unnecessary files inside allowed directories.
1112
**/.DS_Store

Dockerfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,3 @@ EXPOSE 5000
2424
# Run as root user by default to allow access to the containerd socket. This in unfortunate as running as non-root user
2525
# requires changing the containerd socket permissions which still can be manually done by advanced users.
2626
ENTRYPOINT ["unregistry"]
27-

Dockerfile.test

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ FROM docker:28.3.3-dind AS unregistry-dind
2121

2222
ENV UNREGISTRY_CONTAINERD_SOCK="/run/docker/containerd/containerd.sock"
2323

24+
RUN apk add --no-cache openssh && ssh-keygen -A && mkdir /root/.ssh
25+
2426
COPY scripts/dind-entrypoint.sh /usr/local/bin/entrypoint.sh
27+
COPY test/e2e/ssh/test_key.pub /root/.ssh/authorized_keys
2528
COPY --from=builder /build/unregistry /usr/local/bin/
2629

2730
EXPOSE 5000

scripts/dind-entrypoint.sh

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ cleanup() {
1515
kill "$(cat /run/docker.pid)" 2>/dev/null || true
1616
fi
1717

18+
# Terminate SSH daemon if PID file exists.
19+
if [ -f /run/sshd.pid ]; then
20+
kill "$(cat /run/sshd.pid)" 2>/dev/null || true
21+
fi
22+
1823
# Wait for processes to terminate.
1924
wait
2025
}
@@ -28,7 +33,8 @@ else
2833
echo "Using the default Docker image store."
2934
fi
3035

31-
dind dockerd --host=tcp://0.0.0.0:2375 --tls=false &
36+
dind dockerd --host unix:///run/docker.sock --host=tcp://0.0.0.0:2375 --tls=false &
37+
/usr/sbin/sshd -o AllowTcpForwarding=yes
3238

3339
# Execute the passed command and wait for it while maintaining signal handling.
3440
"$@" &

test/e2e/registry_test.go

Lines changed: 3 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ import (
1111
"slices"
1212
"strings"
1313
"testing"
14-
"time"
1514

16-
"github.com/docker/docker/api/types"
1715
"github.com/docker/docker/api/types/filters"
1816
"github.com/docker/docker/pkg/jsonmessage"
1917
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
@@ -26,83 +24,20 @@ import (
2624

2725
"github.com/docker/docker/api/types/image"
2826
"github.com/docker/docker/client"
29-
"github.com/testcontainers/testcontainers-go"
30-
"github.com/testcontainers/testcontainers-go/wait"
3127
)
3228

3329
func TestRegistryPushPull(t *testing.T) {
3430
ctx := context.Background()
35-
36-
// Start unregistry in a Docker-in-Docker container with Docker using containerd image store.
37-
req := testcontainers.GenericContainerRequest{
38-
ContainerRequest: testcontainers.ContainerRequest{
39-
FromDockerfile: testcontainers.FromDockerfile{
40-
Context: filepath.Join("..", ".."),
41-
Dockerfile: "Dockerfile.test",
42-
BuildOptionsModifier: func(buildOptions *types.ImageBuildOptions) {
43-
buildOptions.Target = "unregistry-dind"
44-
},
45-
},
46-
Env: map[string]string{
47-
"UNREGISTRY_LOG_LEVEL": "debug",
48-
},
49-
Privileged: true,
50-
// Explicitly specify the host port for the registry because if not specified, 'docker push' from Docker
51-
// Desktop is unable to reach the automatically mapped one for some reason.
52-
ExposedPorts: []string{"2375", "50000:5000"},
53-
WaitingFor: wait.ForAll(
54-
wait.ForListeningPort("2375"),
55-
wait.ForListeningPort("5000"),
56-
).WithStartupTimeoutDefault(15 * time.Second),
57-
},
58-
Started: true,
59-
}
60-
unregistryContainer, err := testcontainers.GenericContainer(ctx, req)
61-
require.NoError(t, err)
62-
63-
t.Cleanup(func() {
64-
// Print last 20 lines of unregistry container logs.
65-
logs, err := unregistryContainer.Logs(ctx)
66-
assert.NoError(t, err, "Failed to get logs from unregistry container.")
67-
if err == nil {
68-
defer logs.Close()
69-
logsContent, err := io.ReadAll(logs)
70-
assert.NoError(t, err, "Failed to read logs from unregistry container.")
71-
if err == nil {
72-
73-
lines := strings.Split(string(logsContent), "\n")
74-
start := len(lines) - 20
75-
if start < 0 {
76-
start = 0
77-
}
78-
79-
t.Log("=== Last 20 lines of unregistry container logs ===")
80-
for i := start; i < len(lines); i++ {
81-
if lines[i] != "" {
82-
t.Log(lines[i])
83-
}
84-
}
85-
t.Log("=== End of unregistry container logs ===")
86-
}
87-
}
88-
89-
// Ensure the container is terminated after the test.
90-
assert.NoError(t, unregistryContainer.Terminate(ctx))
91-
})
92-
93-
mappedDockerPort, err := unregistryContainer.MappedPort(ctx, "2375")
94-
require.NoError(t, err)
95-
mappedRegistryPort, err := unregistryContainer.MappedPort(ctx, "5000")
96-
require.NoError(t, err)
31+
mappedDockerPort, mappedRegistryPort := runUnregistryDinD(t, true)
9732

9833
remoteCli, err := client.NewClientWithOpts(
99-
client.WithHost("tcp://localhost:"+mappedDockerPort.Port()),
34+
client.WithHost("tcp://localhost:"+mappedDockerPort),
10035
client.WithAPIVersionNegotiation(),
10136
)
10237
require.NoError(t, err)
10338
defer remoteCli.Close()
10439

105-
registryAddr := "localhost:" + mappedRegistryPort.Port()
40+
registryAddr := "localhost:" + mappedRegistryPort
10641
t.Logf("Unregistry started at %s", registryAddr)
10742

10843
localCli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())

test/e2e/ssh/test_key

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-----BEGIN OPENSSH PRIVATE KEY-----
2+
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
3+
QyNTUxOQAAACBS9CsZhkOgawNzvNPRSIOJiKhz9xEqUkPGeMGXZV2UmQAAAJBT8W4dU/Fu
4+
HQAAAAtzc2gtZWQyNTUxOQAAACBS9CsZhkOgawNzvNPRSIOJiKhz9xEqUkPGeMGXZV2UmQ
5+
AAAECiXqC92PMhl7Xe1TtjWTVtd9r+PORzw4iGLmdzmbwE9FL0KxmGQ6BrA3O809FIg4mI
6+
qHP3ESpSQ8Z4wZdlXZSZAAAACGUyZUB0ZXN0AQIDBAU=
7+
-----END OPENSSH PRIVATE KEY-----

test/e2e/ssh/test_key.pub

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFL0KxmGQ6BrA3O809FIg4mIqHP3ESpSQ8Z4wZdlXZSZ e2e@test

test/e2e/unregistry.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package e2e
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io"
7+
"path/filepath"
8+
"strings"
9+
"testing"
10+
"time"
11+
12+
"github.com/docker/docker/api/types"
13+
"github.com/stretchr/testify/assert"
14+
"github.com/stretchr/testify/require"
15+
"github.com/testcontainers/testcontainers-go"
16+
"github.com/testcontainers/testcontainers-go/wait"
17+
)
18+
19+
// runUnregistryDinD starts unregistry in a Docker-in-Docker container. It returns the mapped Docker
20+
// port and the mapped unregistry port. The containerdStore parameter specifies whether to use containerd image store.
21+
func runUnregistryDinD(t *testing.T, containerdStore bool) (string, string) {
22+
ctx := context.Background()
23+
// Start unregistry in a Docker-in-Docker container with Docker using containerd image store.
24+
req := testcontainers.GenericContainerRequest{
25+
ContainerRequest: testcontainers.ContainerRequest{
26+
FromDockerfile: testcontainers.FromDockerfile{
27+
Context: filepath.Join("..", ".."),
28+
Dockerfile: "Dockerfile.test",
29+
BuildOptionsModifier: func(buildOptions *types.ImageBuildOptions) {
30+
buildOptions.Target = "unregistry-dind"
31+
},
32+
},
33+
Env: map[string]string{
34+
"DOCKER_CONTAINERD_STORE": fmt.Sprintf("%t", containerdStore),
35+
"UNREGISTRY_LOG_LEVEL": "debug",
36+
},
37+
Privileged: true,
38+
// Explicitly specify the host port for the registry because if not specified, 'docker push' from Docker
39+
// Desktop is unable to reach the automatically mapped one for some reason.
40+
ExposedPorts: []string{"2375", "50000:5000"},
41+
WaitingFor: wait.ForAll(
42+
wait.ForListeningPort("2375"),
43+
wait.ForListeningPort("5000"),
44+
).WithStartupTimeoutDefault(15 * time.Second),
45+
},
46+
Started: true,
47+
}
48+
ctr, err := testcontainers.GenericContainer(ctx, req)
49+
require.NoError(t, err)
50+
51+
t.Cleanup(func() {
52+
// Print last 20 lines of unregistry container logs.
53+
logs, err := ctr.Logs(ctx)
54+
assert.NoError(t, err, "Failed to get logs from unregistry container.")
55+
if err == nil {
56+
defer logs.Close()
57+
logsContent, err := io.ReadAll(logs)
58+
assert.NoError(t, err, "Failed to read logs from unregistry container.")
59+
if err == nil {
60+
61+
lines := strings.Split(string(logsContent), "\n")
62+
start := len(lines) - 20
63+
if start < 0 {
64+
start = 0
65+
}
66+
67+
t.Log("=== Last 20 lines of unregistry container logs ===")
68+
for i := start; i < len(lines); i++ {
69+
if lines[i] != "" {
70+
t.Log(lines[i])
71+
}
72+
}
73+
t.Log("=== End of unregistry container logs ===")
74+
}
75+
}
76+
77+
// Ensure the container is terminated after the test.
78+
assert.NoError(t, ctr.Terminate(ctx))
79+
})
80+
81+
mappedDockerPort, err := ctr.MappedPort(ctx, "2375")
82+
require.NoError(t, err)
83+
mappedRegistryPort, err := ctr.MappedPort(ctx, "5000")
84+
require.NoError(t, err)
85+
86+
return mappedDockerPort.Port(), mappedRegistryPort.Port()
87+
}

0 commit comments

Comments
 (0)