diff --git a/lib/utils/device/deploy.ts b/lib/utils/device/deploy.ts index 49ed34ff..788a14fd 100644 --- a/lib/utils/device/deploy.ts +++ b/lib/utils/device/deploy.ts @@ -30,9 +30,11 @@ import { Readable } from 'stream'; import { BALENA_ENGINE_TMP_PATH } from '../../config'; import { checkBuildSecretsRequirements, makeBuildTasks } from '../compose_ts'; +import { workaroundWindowsDnsIssue } from '../helpers'; import Logger = require('../logger'); import { DeviceAPI, DeviceInfo } from './api'; import * as LocalPushErrors from './errors'; +import { DeviceAPIError } from './errors'; import LivepushManager from './live'; import { displayBuildLog } from './logs'; @@ -74,7 +76,7 @@ async function environmentFromInput( const varRegex = /^(?:([^\s:]+):)?([^\s]+?)=(.*)$/; const ret: ParsedEnvironment = {}; - // Propolulate the object with the servicenames, as it + // Populate the object with the servicenames, as it // also means that we can do a fast lookup of whether a // service exists for (const service of serviceNames) { @@ -139,6 +141,8 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise { ); } + await workaroundWindowsDnsIssue(opts.deviceHost); + const versionError = new Error( 'The supervisor version on this remote device does not support multicontainer local mode. ' + 'Please update your device to balenaOS v2.20.0 or greater from the dashboard.', @@ -156,10 +160,18 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise { ); opts.nolive = true; } - } catch { - exitWithExpectedError(versionError); + } catch (e) { + // Very old supervisor versions do not support /version endpoint + // a DeviceAPIError is expected in this case + if (e instanceof DeviceAPIError) { + exitWithExpectedError(versionError); + } else { + throw e; + } } + await workaroundWindowsDnsIssue(opts.deviceHost); + globalLogger.logInfo(`Starting build on device ${opts.deviceHost}`); const project = await loadProject( @@ -183,6 +195,8 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise { // Try to detect the device information const deviceInfo = await api.getDeviceInformation(); + await workaroundWindowsDnsIssue(opts.deviceHost); + let buildLogs: Dictionary | undefined; if (!opts.nolive) { buildLogs = {}; @@ -218,6 +232,8 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise { await api.setTargetState(targetState); + await workaroundWindowsDnsIssue(opts.deviceHost); + // Now that we've set the target state, the device will do it's thing // so we can either just display the logs, or start a livepush session // (whilst also display logs) diff --git a/lib/utils/helpers.ts b/lib/utils/helpers.ts index 1fbee686..4ccdd672 100644 --- a/lib/utils/helpers.ts +++ b/lib/utils/helpers.ts @@ -338,3 +338,21 @@ function windowsCmdExeEscapeArg(arg: string): string { // duplicate internal double quotes, and double quote overall return `"${arg.replace(/["]/g, '""')}"`; } + +/* + * Workaround a window system bug which causes multiple rapid DNS lookups + * to fail for mDNS. + * + * It introduces a simple pause, and should be used between operations that + * trigger mDNS resolutions. + * + * Windows bug: https://support.microsoft.com/en-gb/help/4057932/getaddrinfo-failed-with-wsahost-not-found-11001-error + */ +export async function workaroundWindowsDnsIssue(ipOrHostname: string) { + // 300ms seemed to be the smallest delay that worked reliably but may + // vary between systems. + const delay = 500; + if (process.platform === 'win32' && ipOrHostname.includes('.local')) { + await new Promise(r => setTimeout(r, delay)); + } +}