Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: e2e

on:
push:
tags:
- v*
branches:
- master
pull_request:

permissions:
contents: read

jobs:
e2e:
name: e2e
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: 'recursive'

- name: Set up Go 1.x
uses: actions/setup-go@v4
with:
go-version: '1.26.x'
cache: true

- name: Build test binaries
run: |
mkdir -p /tmp/bin
go build -o /tmp/bin/docker-sshd ./cmd/docker-sshd

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot move go build and go test into a container of docker compose, so users can test all by themselves

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit 6d0c2c1. go build and go test now run inside the docker compose e2e-runner container (e2e/docker-compose.yml), and the workflow now invokes that compose runner after kind setup.

go build -o /tmp/bin/kube-sshd ./cmd/kube-sshd

- name: Set up kind
uses: helm/kind-action@v1.12.0

- name: Run e2e tests
run: |
docker compose -f e2e/docker-compose.yml up -d
trap "docker compose -f e2e/docker-compose.yml down || true" EXIT
DOCKER_SSHD_E2E=1 KUBE_SSHD_E2E=1 DOCKER_SSHD_BIN=/tmp/bin/docker-sshd KUBE_SSHD_BIN=/tmp/bin/kube-sshd go test ./e2e -v
22 changes: 0 additions & 22 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,25 +30,3 @@ jobs:

- name: Run unit tests
run: go test ./...

e2e:
name: e2e
runs-on: ubuntu-latest
needs: ut
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: 'recursive'

- name: Set up Go 1.x
uses: actions/setup-go@v4
with:
go-version: '1.26.x'
cache: true

- name: Run e2e smoke checks
run: |
go run ./cmd/docker-sshd --help > /dev/null
go run ./cmd/kube-sshd --help > /dev/null
3 changes: 3 additions & 0 deletions e2e/Dockerfile.target
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM alpine:3.20

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inline this, no dockerfile needed, just put cmd in docker complose

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot inline this, no dockerfile needed, just put cmd in docker complose

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit 0ceed77. I inlined the target container setup directly in e2e/docker-compose.yml (image + command) and removed e2e/Dockerfile.target.


CMD ["sleep", "300"]
6 changes: 6 additions & 0 deletions e2e/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
services:
docker-sshd-e2e-target:
Comment thread
tg123 marked this conversation as resolved.
build:
context: ..
dockerfile: e2e/Dockerfile.target
container_name: docker-sshd-e2e-test
120 changes: 120 additions & 0 deletions e2e/e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package e2e

import (
"os"
"os/exec"
"strings"
"testing"
"time"
)

func TestDockerSSHD(t *testing.T) {
if os.Getenv("DOCKER_SSHD_E2E") != "1" {
t.Skip("set DOCKER_SSHD_E2E=1 to run")
}

bin := os.Getenv("DOCKER_SSHD_BIN")
if bin == "" {
t.Fatal("DOCKER_SSHD_BIN is required")
}

container := os.Getenv("DOCKER_E2E_CONTAINER")
if container == "" {
container = "docker-sshd-e2e-test"
}

key := t.TempDir() + "/docker-sshd-e2e-key"
mustRun(t, "ssh-keygen", "-t", "ed25519", "-N", "", "-f", key)

cmd := exec.Command(bin, "--address", "127.0.0.1", "--port", "2232", "--server-key", key)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
t.Fatalf("start docker-sshd: %v", err)
}
defer func() {
_ = cmd.Process.Kill()
_, _ = cmd.Process.Wait()
}()

out := retrySSH(t, "2232", container+"@127.0.0.1")
if strings.TrimSpace(out) != "ok" {
t.Fatalf("expected ok, got %q", out)
}
}

func TestKubeSSHD(t *testing.T) {
if os.Getenv("KUBE_SSHD_E2E") != "1" {
t.Skip("set KUBE_SSHD_E2E=1 to run")
}

bin := os.Getenv("KUBE_SSHD_BIN")
if bin == "" {
t.Fatal("KUBE_SSHD_BIN is required")
}

pod := "docker-sshd-e2e-" + time.Now().Format("150405")
mustRun(t, "kubectl", "run", pod, "--image=busybox:1.36", "--restart=Never", "--command", "--", "sleep", "300")
defer run(t, "kubectl", "delete", "pod", pod, "--ignore-not-found=true", "--wait=false")

Check failure on line 58 in e2e/e2e_test.go

View workflow job for this annotation

GitHub Actions / lint

Error return value is not checked (errcheck)
mustRun(t, "kubectl", "wait", "--for=condition=Ready", "pod/"+pod, "--timeout=120s")

key := t.TempDir() + "/kube-sshd-e2e-key"
mustRun(t, "ssh-keygen", "-t", "ed25519", "-N", "", "-f", key)

cmd := exec.Command(bin, "--address", "127.0.0.1", "--port", "2233", "--server-key", key, "--namespace", "default")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
t.Fatalf("start kube-sshd: %v", err)
}
defer func() {
_ = cmd.Process.Kill()
_, _ = cmd.Process.Wait()
}()

out := retrySSH(t, "2233", pod+"@127.0.0.1")
if strings.TrimSpace(out) != "ok" {
t.Fatalf("expected ok, got %q", out)
}
}

func retrySSH(t *testing.T, port, userHost string) string {
t.Helper()

var out string
var err error
for range 30 {
out, err = run(t,
"ssh",
"-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=/dev/null",
"-o", "PreferredAuthentications=none",
"-p", port,
userHost,
"echo", "ok",
)
if err == nil {
return out
}
time.Sleep(time.Second)
}

t.Fatalf("ssh did not succeed: %v", err)
return ""
}

func mustRun(t *testing.T, name string, args ...string) string {
t.Helper()
out, err := run(t, name, args...)
if err != nil {
t.Fatalf("%s %v failed: %v\n%s", name, args, err, out)
}
return out
}

func run(t *testing.T, name string, args ...string) (string, error) {
t.Helper()
cmd := exec.Command(name, args...)
out, err := cmd.CombinedOutput()
return string(out), err
}
Loading