stop pinging hosts as soon as any connectivity is available

Even if we just have DERP connectivity, stop pinging so that action can continue sooner.

Updates tailscale/corp#32862

Signed-off-by: Percy Wegmann <percy@tailscale.com>
This commit is contained in:
Percy Wegmann 2025-09-29 19:57:14 -05:00 committed by Percy Wegmann
parent fc77efa75d
commit cff51cdb0c
3 changed files with 73 additions and 59 deletions

View file

@ -9,7 +9,7 @@ on:
jobs:
# Matrix test for all supported platforms and architectures
integration-tests:
name: ${{ matrix.os }} (${{ matrix.arch }}) tailscale-${{ matrix.version }}
strategy:
@ -34,13 +34,13 @@ jobs:
runner-os: Linux
arch: amd64
version: 1.82.0
# Linux tests (ARM64)
- os: ubuntu-24.04-arm
runner-os: Linux
arch: arm64
version: latest
# Windows tests (AMD64)
- os: windows-latest
runner-os: Windows
@ -52,46 +52,46 @@ jobs:
runner-os: Windows
arch: amd64
version: unstable
# Windows tests (ARM64)
- os: windows-11-arm
runner-os: Windows
arch: arm64
version: latest
# macOS intel
- os: macos-13
runner-os: macOS
arch: amd64
version: latest
ping: 100.99.0.2 # hostnames aren't resolving on MacOS, just ping IP lax-pve.pineapplefish.ts.net,lax-pve
# macOS ARM
- os: macos-14
runner-os: macOS
arch: arm64
version: latest
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
node-version: "20"
cache: "npm"
- name: Install Dependencies
run: npm ci
- name: Build Action
run: npm run build
# Test with OAuth authentication
- name: Test Tailscale Setup (OAuth)
- name: Test Action
id: tailscale-oauth
uses: ./
with:
@ -103,7 +103,7 @@ jobs:
timeout: "5m"
retry: 3
ping: "${{ matrix.ping }}"
# Test Tailscale status command
- name: Check Tailscale Status
if: steps.tailscale-oauth.outcome == 'success'
@ -119,4 +119,3 @@ jobs:
tailscale version
fi
shell: bash

49
dist/index.js vendored
View file

@ -41123,6 +41123,7 @@ const fs = __importStar(__nccwpck_require__(79896));
const http = __importStar(__nccwpck_require__(58611));
const os = __importStar(__nccwpck_require__(70857));
const path = __importStar(__nccwpck_require__(16928));
const promises_1 = __nccwpck_require__(16460);
const cmdTailscale = "tailscale";
const cmdTailscaleFullPath = "/usr/local/bin/tailscale";
const cmdTailscaled = "tailscaled";
@ -41241,7 +41242,7 @@ async function run() {
core.debug(`Tailscale status: ${JSON.stringify(status)}`);
if (status.BackendState === "Running") {
core.info("✅ Tailscale is running and connected!");
pingHostsIfNecessary(config);
await pingHostsIfNecessary(config);
// Explicitly exit to prevent hanging
process.exit(0);
}
@ -41254,7 +41255,7 @@ async function run() {
core.warning(`Failed to get Tailscale status: ${err}`);
// Still exit successfully since the main connection worked
core.info("✅ Tailscale daemon is connected!");
pingHostsIfNecessary(config);
await pingHostsIfNecessary(config);
// Explicitly exit to prevent hanging
process.exit(0);
}
@ -41264,31 +41265,33 @@ async function run() {
}
}
async function pingHostsIfNecessary(config) {
const directConnectionWarning = "direct connection not established";
if (config.pingHosts.length == 0) {
return;
}
core.info(`Will ping hosts ${config.pingHosts.join(",")} up to 3 minutes in order to check connectivity`);
core.info(`Will ping hosts ${config.pingHosts.join(",")} up to 3 minutes each in order to check connectivity`);
for (const host of config.pingHosts) {
core.info(`Pinging host ${host}`);
let result = await exec.getExecOutput(cmdTailscale, [
"ping",
"-c",
"36",
host,
]);
await pingHost(host);
}
}
async function pingHost(host) {
core.info(`Pinging host ${host}`);
for (let i = 0; i <= 36; i++) {
if (i < 0) {
// wait 5 seconds before pinging
(0, promises_1.setTimeout)(5000);
}
let result = await exec.getExecOutput(cmdTailscale, ["ping", "-c", "1", host], { ignoreReturnCode: true });
if (result.exitCode === 0) {
core.info(`✅ Ping host ${host} responded!`);
core.info(`✅ Ping host ${host} reachable via direct connection!`);
return;
}
else if (result.stderr.includes(directConnectionWarning) ||
result.stdout.includes(directConnectionWarning)) {
core.warning(`⚠️ Ping host ${host} reachable only via DERP, not direct connection.`);
}
else {
core.setFailed(`❌ Ping host ${host} did not respond`);
process.exit(1);
else if (result.stderr.includes("direct connection not established")) {
core.info(`✅ Ping host ${host} reachable via DERP!`);
return;
}
}
core.setFailed(`❌ Ping host ${host} did not respond`);
process.exit(1);
}
async function getInputs() {
let ping = core.getInput("ping");
@ -42006,6 +42009,14 @@ module.exports = require("timers");
/***/ }),
/***/ 16460:
/***/ ((module) => {
"use strict";
module.exports = require("timers/promises");
/***/ }),
/***/ 64756:
/***/ ((module) => {

View file

@ -8,6 +8,7 @@ import * as fs from "fs";
import * as http from "http";
import * as os from "os";
import * as path from "path";
import { setTimeout as wait } from "timers/promises";
const cmdTailscale = "tailscale";
const cmdTailscaleFullPath = "/usr/local/bin/tailscale";
@ -167,7 +168,7 @@ async function run(): Promise<void> {
core.debug(`Tailscale status: ${JSON.stringify(status)}`);
if (status.BackendState === "Running") {
core.info("✅ Tailscale is running and connected!");
pingHostsIfNecessary(config);
await pingHostsIfNecessary(config);
// Explicitly exit to prevent hanging
process.exit(0);
} else {
@ -178,7 +179,7 @@ async function run(): Promise<void> {
core.warning(`Failed to get Tailscale status: ${err}`);
// Still exit successfully since the main connection worked
core.info("✅ Tailscale daemon is connected!");
pingHostsIfNecessary(config);
await pingHostsIfNecessary(config);
// Explicitly exit to prevent hanging
process.exit(0);
}
@ -188,8 +189,6 @@ async function run(): Promise<void> {
}
async function pingHostsIfNecessary(config: TailscaleConfig): Promise<void> {
const directConnectionWarning = "direct connection not established";
if (config.pingHosts.length == 0) {
return;
}
@ -197,30 +196,35 @@ async function pingHostsIfNecessary(config: TailscaleConfig): Promise<void> {
core.info(
`Will ping hosts ${config.pingHosts.join(
","
)} up to 3 minutes in order to check connectivity`
)} up to 3 minutes each in order to check connectivity`
);
for (const host of config.pingHosts) {
core.info(`Pinging host ${host}`);
let result = await exec.getExecOutput(cmdTailscale, [
"ping",
"-c",
"36",
host,
]);
await pingHost(host);
}
}
async function pingHost(host: string): Promise<void> {
core.info(`Pinging host ${host}`);
for (let i = 0; i <= 36; i++) {
if (i < 0) {
// wait 5 seconds before pinging
wait(5000);
}
let result = await exec.getExecOutput(
cmdTailscale,
["ping", "-c", "1", host],
{ ignoreReturnCode: true }
);
if (result.exitCode === 0) {
core.info(`✅ Ping host ${host} responded!`);
} else if (
result.stderr.includes(directConnectionWarning) ||
result.stdout.includes(directConnectionWarning)
) {
core.warning(
`⚠️ Ping host ${host} reachable only via DERP, not direct connection.`
);
} else {
core.setFailed(`❌ Ping host ${host} did not respond`);
process.exit(1);
core.info(`✅ Ping host ${host} reachable via direct connection!`);
return;
} else if (result.stderr.includes("direct connection not established")) {
core.info(`✅ Ping host ${host} reachable via DERP!`);
return;
}
}
core.setFailed(`❌ Ping host ${host} did not respond`);
process.exit(1);
}
async function getInputs(): Promise<TailscaleConfig> {