mirror of
https://github.com/balena-io/balena-cli.git
synced 2025-01-28 23:24:16 +00:00
Add ability to push to remote device UUID
Change-type: minor Signed-off-by: Josh Bowling <josh@balena.io>
This commit is contained in:
parent
cc60e86507
commit
1de95a5f6c
@ -2913,12 +2913,14 @@ Examples:
|
|||||||
|
|
||||||
$ balena push 23c73a1.local --system
|
$ balena push 23c73a1.local --system
|
||||||
$ balena push 23c73a1.local --system --service my-service
|
$ balena push 23c73a1.local --system --service my-service
|
||||||
|
|
||||||
|
$ balena push 9f0cc5e4-0707-487c-ba84-edd0c36ff1c9
|
||||||
|
|
||||||
### Arguments
|
### Arguments
|
||||||
|
|
||||||
#### FLEETORDEVICE
|
#### FLEETORDEVICE
|
||||||
|
|
||||||
fleet name or slug, or local device IP address or ".local" hostname
|
fleet name or slug, device UUID, or local device IP address or ".local" hostname
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
|
@ -112,13 +112,15 @@ export default class PushCmd extends Command {
|
|||||||
'',
|
'',
|
||||||
'$ balena push 23c73a1.local --system',
|
'$ balena push 23c73a1.local --system',
|
||||||
'$ balena push 23c73a1.local --system --service my-service',
|
'$ balena push 23c73a1.local --system --service my-service',
|
||||||
|
'',
|
||||||
|
'$ balena push 9f0cc5e4-0707-487c-ba84-edd0c36ff1c9',
|
||||||
];
|
];
|
||||||
|
|
||||||
public static args = [
|
public static args = [
|
||||||
{
|
{
|
||||||
name: 'fleetOrDevice',
|
name: 'fleetOrDevice',
|
||||||
description:
|
description:
|
||||||
'fleet name or slug, or local device IP address or ".local" hostname',
|
'fleet name or slug, device UUID, or local device IP address or ".local" hostname',
|
||||||
required: true,
|
required: true,
|
||||||
parse: lowercaseIfSlug,
|
parse: lowercaseIfSlug,
|
||||||
},
|
},
|
||||||
@ -315,7 +317,7 @@ export default class PushCmd extends Command {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case BuildTarget.Device:
|
case BuildTarget.Device:
|
||||||
logger.logDebug(`Pushing to local device: ${params.fleetOrDevice}`);
|
logger.logDebug(`Pushing to device: ${params.fleetOrDevice}`);
|
||||||
await this.pushToDevice(
|
await this.pushToDevice(
|
||||||
params.fleetOrDevice,
|
params.fleetOrDevice,
|
||||||
options,
|
options,
|
||||||
@ -442,9 +444,9 @@ export default class PushCmd extends Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async getBuildTarget(appOrDevice: string): Promise<BuildTarget> {
|
protected async getBuildTarget(appOrDevice: string): Promise<BuildTarget> {
|
||||||
const { validateLocalHostnameOrIp } = await import('../utils/validation');
|
const { validateDeviceAddress } = await import('../utils/validation');
|
||||||
|
|
||||||
return validateLocalHostnameOrIp(appOrDevice)
|
return validateDeviceAddress(appOrDevice)
|
||||||
? BuildTarget.Device
|
? BuildTarget.Device
|
||||||
: BuildTarget.Cloud;
|
: BuildTarget.Cloud;
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,10 @@ import { DeviceAPI, DeviceInfo } from './api';
|
|||||||
import * as LocalPushErrors from './errors';
|
import * as LocalPushErrors from './errors';
|
||||||
import LivepushManager from './live';
|
import LivepushManager from './live';
|
||||||
import { displayBuildLog } from './logs';
|
import { displayBuildLog } from './logs';
|
||||||
import { stripIndent } from '../lazy';
|
import { getBalenaSdk, stripIndent } from '../lazy';
|
||||||
|
import { validateIPAddress } from '../validation';
|
||||||
|
import { Server, Socket } from 'net';
|
||||||
|
import { BalenaSDK, Device } from 'balena-sdk';
|
||||||
|
|
||||||
const LOCAL_APPNAME = 'localapp';
|
const LOCAL_APPNAME = 'localapp';
|
||||||
const LOCAL_RELEASEHASH = 'localrelease';
|
const LOCAL_RELEASEHASH = 'localrelease';
|
||||||
@ -121,7 +124,110 @@ async function environmentFromInput(
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const logConnection = (
|
||||||
|
logger: Logger,
|
||||||
|
fromHost: string,
|
||||||
|
fromPort: number,
|
||||||
|
localAddress: string,
|
||||||
|
localPort: number,
|
||||||
|
deviceAddress: string,
|
||||||
|
devicePort: number,
|
||||||
|
err?: Error,
|
||||||
|
) => {
|
||||||
|
const logMessage = `${fromHost}:${fromPort} => ${localAddress}:${localPort} ===> ${deviceAddress}:${devicePort}`;
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
logger.logError(`${logMessage} :: ${err.message}`);
|
||||||
|
} else {
|
||||||
|
logger.logLogs(logMessage);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function openTunnel(
|
||||||
|
logger: Logger,
|
||||||
|
device: Device,
|
||||||
|
sdk: BalenaSDK,
|
||||||
|
port: number,
|
||||||
|
): Promise<any> {
|
||||||
|
const localhost = 'localhost';
|
||||||
|
try {
|
||||||
|
const { tunnelConnectionToDevice } = await import('../tunnel');
|
||||||
|
const handler = await tunnelConnectionToDevice(device.uuid, port, sdk);
|
||||||
|
|
||||||
|
const { createServer } = await import('net');
|
||||||
|
const server = createServer(async (client: Socket) => {
|
||||||
|
try {
|
||||||
|
await handler(client);
|
||||||
|
logConnection(
|
||||||
|
logger,
|
||||||
|
client.remoteAddress || '',
|
||||||
|
client.remotePort || 0,
|
||||||
|
client.localAddress,
|
||||||
|
client.localPort,
|
||||||
|
device.vpn_address || '',
|
||||||
|
port,
|
||||||
|
);
|
||||||
|
} catch (err: any) {
|
||||||
|
logConnection(
|
||||||
|
logger,
|
||||||
|
client.remoteAddress || '',
|
||||||
|
client.remotePort || 0,
|
||||||
|
client.localAddress,
|
||||||
|
client.localPort,
|
||||||
|
device.vpn_address || '',
|
||||||
|
port,
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await new Promise<Server>((resolve, reject) => {
|
||||||
|
server.on('error', reject);
|
||||||
|
server.listen(port, localhost, () => {
|
||||||
|
resolve(server);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.logInfo(
|
||||||
|
` - tunnelling ${localhost}:${port} to ${device.uuid}:${port}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// opts.deviceHost = localhost;
|
||||||
|
} catch (err: any) {
|
||||||
|
logger.logWarn(
|
||||||
|
` - tunnel failed ${localhost}:${port} to ${
|
||||||
|
device.uuid
|
||||||
|
}:${port}, failed ${JSON.stringify(err.message)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
|
export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
|
||||||
|
// Can only communicate with device using IP if local
|
||||||
|
const isLocal =
|
||||||
|
opts.deviceHost.includes('.local') || validateIPAddress(opts.deviceHost)
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
|
if (!isLocal) {
|
||||||
|
// 1. Open tunnel from remote device to localhost
|
||||||
|
// 2. Deploy to localhost
|
||||||
|
const logger = Logger.getLogger();
|
||||||
|
const sdk = getBalenaSdk();
|
||||||
|
|
||||||
|
// Ascertain device uuid
|
||||||
|
const { getOnlineTargetDeviceUuid } = await import('../patterns');
|
||||||
|
const uuid = await getOnlineTargetDeviceUuid(sdk, opts.deviceHost);
|
||||||
|
const device = await sdk.models.device.get(uuid);
|
||||||
|
logger.logInfo(`Opening a tunnel to ${device.uuid}...`);
|
||||||
|
|
||||||
|
await openTunnel(logger, device, sdk, 48484);
|
||||||
|
await openTunnel(logger, device, sdk, 2375);
|
||||||
|
|
||||||
|
logger.logInfo('Opened tunnels to supervisor and docker...');
|
||||||
|
|
||||||
|
opts.deviceHost = 'localhost';
|
||||||
|
}
|
||||||
|
|
||||||
// Resolve .local addresses to IP to avoid
|
// Resolve .local addresses to IP to avoid
|
||||||
// issue with Windows and rapid repeat lookups.
|
// issue with Windows and rapid repeat lookups.
|
||||||
// see: https://github.com/balena-io/balena-cli/issues/1518
|
// see: https://github.com/balena-io/balena-cli/issues/1518
|
||||||
@ -145,7 +251,7 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
|
|||||||
throw new ExpectedError(stripIndent`
|
throw new ExpectedError(stripIndent`
|
||||||
Could not communicate with device supervisor at address ${opts.deviceHost}:${port}.
|
Could not communicate with device supervisor at address ${opts.deviceHost}:${port}.
|
||||||
Device may not have local mode enabled. Check with:
|
Device may not have local mode enabled. Check with:
|
||||||
balena device local-mode <device-uuid>
|
balena device local-mode <device-uuid>
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +57,10 @@ export function validateDotLocalUrl(input: string): boolean {
|
|||||||
return DOTLOCAL_REGEX.test(input);
|
return DOTLOCAL_REGEX.test(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function validateDeviceAddress(input: string): boolean {
|
||||||
|
return validateLocalHostnameOrIp(input) || validateUuid(input);
|
||||||
|
}
|
||||||
|
|
||||||
export function validateLocalHostnameOrIp(input: string): boolean {
|
export function validateLocalHostnameOrIp(input: string): boolean {
|
||||||
return validateIPAddress(input) || validateDotLocalUrl(input);
|
return validateIPAddress(input) || validateDotLocalUrl(input);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user