From f8400c1b1c63b4f139a04e749461c1a1e09f725e Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Sun, 3 May 2026 02:20:24 +0000 Subject: [PATCH] chore(ci): test protonvpn Wireguard with port forwarding --- .github/workflows/ci.yml | 6 ++++-- ci/cmd/main.go | 4 ++-- ci/internal/mullvad.go | 5 ++++- ci/internal/protonvpn.go | 8 ++++++-- ci/internal/simple.go | 36 +++++++++++++++++++++++------------- 5 files changed, 39 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f216b4b2..77eae270 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -97,8 +97,10 @@ jobs: - name: Run Gluetun container with Mullvad configuration run: echo -e "${{ secrets.MULLVAD_WIREGUARD_PRIVATE_KEY }}\n${{ secrets.MULLVAD_WIREGUARD_ADDRESS }}" | ./ci/runner mullvad - - name: Run Gluetun container with ProtonVPN configuration - run: echo -e "${{ secrets.PROTONVPN_WIREGUARD_PRIVATE_KEY }}" | ./ci/runner protonvpn + - name: Run Gluetun container with ProtonVPN Wireguard and port forwarding + configuration + run: echo -e "${{ secrets.PROTONVPN_WIREGUARD_PRIVATE_KEY }}" | ./ci/runner + protonvpn-wireguard-port-forwarding codeql: runs-on: ubuntu-latest diff --git a/ci/cmd/main.go b/ci/cmd/main.go index df61a29a..ec7c0646 100644 --- a/ci/cmd/main.go +++ b/ci/cmd/main.go @@ -23,8 +23,8 @@ func main() { switch os.Args[1] { case "mullvad": err = internal.MullvadTest(ctx, logger) - case "protonvpn": - err = internal.ProtonVPNTest(ctx, logger) + case "protonvpn-wireguard-port-forwarding": + err = internal.ProtonVPNWireguardPortForwardingTest(ctx, logger) default: err = fmt.Errorf("unknown command: %s", os.Args[1]) } diff --git a/ci/internal/mullvad.go b/ci/internal/mullvad.go index 199af5ab..47263f6f 100644 --- a/ci/internal/mullvad.go +++ b/ci/internal/mullvad.go @@ -3,6 +3,8 @@ package internal import ( "context" "fmt" + "regexp" + "time" ) func MullvadTest(ctx context.Context, logger Logger) error { @@ -23,5 +25,6 @@ func MullvadTest(ctx context.Context, logger Logger) error { "WIREGUARD_PRIVATE_KEY=" + secrets[0], "WIREGUARD_ADDRESSES=" + secrets[1], } - return simpleTest(ctx, env, logger) + const timeout = 60 * time.Second + return runContainerTest(ctx, env, []*regexp.Regexp{successRegexp}, timeout, logger) } diff --git a/ci/internal/protonvpn.go b/ci/internal/protonvpn.go index 940171c0..4e4f24c5 100644 --- a/ci/internal/protonvpn.go +++ b/ci/internal/protonvpn.go @@ -3,9 +3,11 @@ package internal import ( "context" "fmt" + "regexp" + "time" ) -func ProtonVPNTest(ctx context.Context, logger Logger) error { +func ProtonVPNWireguardPortForwardingTest(ctx context.Context, logger Logger) error { expectedSecrets := []string{ "Wireguard private key", } @@ -20,6 +22,8 @@ func ProtonVPNTest(ctx context.Context, logger Logger) error { "LOG_LEVEL=debug", "SERVER_COUNTRIES=United States", "WIREGUARD_PRIVATE_KEY=" + secrets[0], + "VPN_PORT_FORWARDING=on", } - return simpleTest(ctx, env, logger) + const timeout = 80 * time.Second + return runContainerTest(ctx, env, []*regexp.Regexp{successRegexp, portForwardingRegexp}, timeout, logger) } diff --git a/ci/internal/simple.go b/ci/internal/simple.go index 6b696e97..23d0eef1 100644 --- a/ci/internal/simple.go +++ b/ci/internal/simple.go @@ -16,8 +16,14 @@ import ( func ptrTo[T any](v T) *T { return &v } -func simpleTest(ctx context.Context, env []string, logger Logger) error { - const timeout = 60 * time.Second +var ( + successRegexp = regexp.MustCompile(`^.+Public IP address is .+$`) + portForwardingRegexp = regexp.MustCompile(`port forwarded is \d`) +) + +func runContainerTest(ctx context.Context, env []string, + regexps []*regexp.Regexp, timeout time.Duration, logger Logger, +) error { ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() @@ -57,7 +63,7 @@ func simpleTest(ctx context.Context, env []string, logger Logger) error { return fmt.Errorf("starting container: %w", err) } - return waitForLogLine(ctx, client, containerID, beforeStartTime, logger) + return waitForLogLines(ctx, client, containerID, beforeStartTime, regexps, logger) } func stopContainer(client *client.Client, containerID string) { @@ -71,10 +77,8 @@ func stopContainer(client *client.Client, containerID string) { } } -var successRegexp = regexp.MustCompile(`^.+Public IP address is .+$`) - -func waitForLogLine(ctx context.Context, client *client.Client, containerID string, - beforeStartTime time.Time, logger Logger, +func waitForLogLines(ctx context.Context, client *client.Client, containerID string, + beforeStartTime time.Time, regexps []*regexp.Regexp, logger Logger, ) error { logOptions := container.LogsOptions{ ShowStdout: true, @@ -88,6 +92,8 @@ func waitForLogLine(ctx context.Context, client *client.Client, containerID stri } defer reader.Close() + regexpMatched := 0 + var linesSeen []string scanner := bufio.NewScanner(reader) for ctx.Err() == nil { @@ -97,21 +103,25 @@ func waitForLogLine(ctx context.Context, client *client.Client, containerID stri line = line[8:] } linesSeen = append(linesSeen, line) - if successRegexp.MatchString(line) { - fmt.Println("✅ Success line logged") - return nil + regex := regexps[regexpMatched] + if regex.MatchString(line) { + fmt.Println("✅ Expected line logged:", line) + if regexpMatched == len(regexps)-1 { + return nil + } + regexpMatched++ } continue } err := scanner.Err() if err != nil && err != io.EOF { - logSeenLines(logger, linesSeen) + logSeenLines(linesSeen) return fmt.Errorf("reading log stream: %w", err) } // The scanner is either done or cannot read because of EOF logger.Info("the log scanner stopped") - logSeenLines(logger, linesSeen) + logSeenLines(linesSeen) // Check if the container is still running inspect, err := client.ContainerInspect(ctx, containerID) @@ -126,7 +136,7 @@ func waitForLogLine(ctx context.Context, client *client.Client, containerID stri return ctx.Err() } -func logSeenLines(logger Logger, lines []string) { +func logSeenLines(lines []string) { fmt.Println("Logs seen so far:") for _, line := range lines { fmt.Println(" " + line)