Files
threshold-ui/e2e-tests/helpers/boundary-cli/connect.js
T
Michael Li 1cbf3b227e test(e2e): Add additional assertions to UI tests (#2634)
* test(e2e): Address deprecation

* test(e2e): Add additional connection assertions on tests

* CR: Fix flake when waiting for sessions

If no sessions are available and the page needs to reload, we should wait to ensure that the page has loaded before checking what the state of the sessions are

* CR: Address flake in connect methods

Noticed an issue where a session recording was not playable. The theory is that the session didn't last long enough before it was cancelled. This adds a 2 second wait after establishing a connection before continuing.

* CR: Use authenticated state by default

* CR: Update dynamic host catalog assertion

* CR: Simplify host set checks

- Use .all() when looping
- Add check to see if we need to reload the page or not

* CR: Be more specific with locator

Ran into a conflict with the Static/Dynamic host catalog type and the Static/Dynamic credential type.

* CR: Update connection methods to be more resilient

Rather than using a hardcoded sleep, we will now run a command on the target and wait for a specific string to return before continuing.

* CR
2025-01-06 11:40:32 -05:00

168 lines
5.1 KiB
JavaScript

/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { spawn } from 'node:child_process';
const remoteCommand =
'for i in {1..15}; do echo helloworld\\$i; sleep 1s; done';
/**
* Spawns a child process for a Boundary connection and returns a promise.
* Resolves on any output from stdout or stderr.
* @param {string} command
* @param {Array<string>} args
*/
async function spawnConnection(command, args) {
return new Promise((resolve, reject) => {
const childProcess = spawn(command, args, {
shell: true,
});
childProcess.stdout.on('data', (data) => {
if (data.toString().includes('helloworld3')) {
resolve({ childProcess, stdout: data.toString() });
}
});
childProcess.stderr.on('data', (data) => {
if (!data.toString().includes('Warning: Permanently added')) {
reject(data.toString());
}
});
childProcess.on('error', (err) => {
reject(err);
});
// In case the process has no stdio and didn't error out, resolve a
// promise once the child process closes so we're not waiting forever.
// Windows doesn't seem to return any error nor any output from stderr when
// a timeout occurs so this guarantees we return a response to the caller.
// Otherwise this should not get hit as we should be returning a response
// from one of the handlers above.
childProcess.on('close', () => {
resolve({
childProcess,
stderr: JSON.stringify({ error: 'Process was closed.' }),
});
});
});
}
/**
* Connects to the specified target
* exec is used here to keep the session open
* @param {string} targetId ID of the target to be connected to
* @param {string} sshUser User to be used for the ssh connection
* @param {string} sshKeyPath Path to the ssh key to be used for the ssh connection
* @returns ChildProcess representing the result of the command execution
*/
export async function connectToTarget(targetId, sshUser, sshKeyPath) {
try {
const { childProcess } = await spawnConnection('boundary', [
'connect',
`-target-id=${targetId}`,
'-exec',
'/usr/bin/ssh',
'--',
'-T', // forces tty allocation
`-l ${sshUser}`,
`-i ${sshKeyPath}`,
'-o UserKnownHostsFile=/dev/null',
'-o StrictHostKeyChecking=no',
'-o IdentitiesOnly=yes', // forces the use of the provided key
'-p {{boundary.port}}',
'{{boundary.ip}}',
`"${remoteCommand}"`,
]);
return childProcess;
} catch (e) {
throw new Error(`Failed to connect to the target: ${e}`);
}
}
/**
* Connects to the specified alias
* exec is used here to keep the session open
* @param {string} alias alias of the target to be connected to
* @param {string} sshUser User to be used for the ssh connection
* @param {string} sshKeyPath Path to the ssh key to be used for the ssh connection
* @returns ChildProcess representing the result of the command execution
*/
export async function connectToAlias(alias, sshUser, sshKeyPath) {
try {
const { childProcess } = await spawnConnection('boundary', [
'connect',
`${alias}`,
'-exec',
'/usr/bin/ssh',
'--',
'-T', // forces tty allocation
`-l ${sshUser}`,
`-i ${sshKeyPath}`,
'-o UserKnownHostsFile=/dev/null',
'-o StrictHostKeyChecking=no',
'-o IdentitiesOnly=yes', // forces the use of the provided key
'-p {{boundary.port}}',
'{{boundary.ip}}',
`"${remoteCommand}"`,
]);
return childProcess;
} catch (e) {
throw new Error(`Failed to connect to the target: ${e}`);
}
}
/**
* Connects via ssh to the specified target
* exec is used here to keep the session open
* @param {string} targetId ID of the target to be connected to
* @returns ChildProcess representing the result of the command execution
*/
export async function connectSshToTarget(targetId) {
try {
const { childProcess } = await spawnConnection('boundary', [
'connect',
'ssh',
`-target-id=${targetId}`,
`-remote-command="${remoteCommand}"`,
'--',
'-T', // forces tty allocation
'-o UserKnownHostsFile=/dev/null',
'-o StrictHostKeyChecking=no',
'-o IdentitiesOnly=yes', // forces the use of the provided key
]);
return childProcess;
} catch (e) {
throw new Error(`Failed to connect to the target: ${e}`);
}
}
/**
* Connects via ssh to the specified alias
* exec is used here to keep the session open
* @param {string} alias alias of the target to be connected to
* @returns ChildProcess representing the result of the command execution
*/
export async function connectSshToAlias(alias) {
try {
const { childProcess } = await spawnConnection('boundary', [
'connect',
`ssh`,
`${alias}`,
`-remote-command="${remoteCommand}"`,
'--',
'-T', // forces tty allocation
'-o UserKnownHostsFile=/dev/null',
'-o StrictHostKeyChecking=no',
'-o IdentitiesOnly=yes', // forces the use of the provided key
]);
return childProcess;
} catch (e) {
throw new Error(`Failed to connect to the target: ${e}`);
}
}