feat(containers): introduce separate container commands (#17541)
This patch introduces hidden commands to control container lifecycle: - `npx playwright docker install-server-deps` to install fluxbox, vnc, novnc & to configure them. - `npx playwright docker run-server` to run a server inside the container. Drive-by: remove old version of container image when building a new version with the same name. This way we won't pile up untagged container images.
This commit is contained in:
parent
6b4afbb8df
commit
4cd2176155
35
packages/playwright-core/src/containers/build_docker_image.sh → packages/playwright-core/bin/container_install_deps.sh
Normal file → Executable file
35
packages/playwright-core/src/containers/build_docker_image.sh → packages/playwright-core/bin/container_install_deps.sh
Normal file → Executable file
|
|
@ -88,38 +88,3 @@ $center $full|/ms-playwright-agent/node_modules/playwright-core/lib/server/chrom
|
||||||
$center $full|/ms-playwright-agent/node_modules/playwright-core/lib/server/chromium/appIcon.png||:99.0
|
$center $full|/ms-playwright-agent/node_modules/playwright-core/lib/server/chromium/appIcon.png||:99.0
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Create entrypoint.sh
|
|
||||||
cat <<'EOF' > /entrypoint.sh
|
|
||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
SCREEN_WIDTH=1360
|
|
||||||
SCREEN_HEIGHT=1020
|
|
||||||
SCREEN_DEPTH=24
|
|
||||||
SCREEN_DPI=96
|
|
||||||
GEOMETRY="$SCREEN_WIDTH""x""$SCREEN_HEIGHT""x""$SCREEN_DEPTH"
|
|
||||||
|
|
||||||
nohup /usr/bin/xvfb-run --server-num=$DISPLAY_NUM \
|
|
||||||
--listen-tcp \
|
|
||||||
--server-args="-screen 0 "$GEOMETRY" -fbdir /var/tmp -dpi "$SCREEN_DPI" -listen tcp -noreset -ac +extension RANDR" \
|
|
||||||
/usr/bin/fluxbox -display "$DISPLAY" >/dev/null 2>&1 &
|
|
||||||
|
|
||||||
for i in $(seq 1 500); do
|
|
||||||
if xdpyinfo -display $DISPLAY >/dev/null 2>&1; then
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
echo "Waiting for Xvfb..."
|
|
||||||
sleep 0.2
|
|
||||||
done
|
|
||||||
|
|
||||||
nohup x11vnc -noprimary -nosetprimary -forever -shared -rfbport 5900 -rfbportv6 5900 -display "$DISPLAY" >/dev/null 2>&1 &
|
|
||||||
nohup /opt/bin/noVNC/utils/novnc_proxy --listen 7900 --vnc localhost:5900 >/dev/null 2>&1 &
|
|
||||||
|
|
||||||
cd /ms-playwright-agent
|
|
||||||
|
|
||||||
NOVNC_UUID=$(cat /proc/sys/kernel/random/uuid)
|
|
||||||
echo "novnc is listening on http://127.0.0.1:7900?path=$NOVNC_UUID&resize=scale&autoconnect=1"
|
|
||||||
|
|
||||||
PW_UUID=$(cat /proc/sys/kernel/random/uuid)
|
|
||||||
npx playwright run-server --port=5400 --path=/$PW_UUID
|
|
||||||
EOF
|
|
||||||
chmod 755 /entrypoint.sh
|
|
||||||
31
packages/playwright-core/bin/container_run_server.sh
Executable file
31
packages/playwright-core/bin/container_run_server.sh
Executable file
|
|
@ -0,0 +1,31 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
SCREEN_WIDTH=1360
|
||||||
|
SCREEN_HEIGHT=1020
|
||||||
|
SCREEN_DEPTH=24
|
||||||
|
SCREEN_DPI=96
|
||||||
|
GEOMETRY="$SCREEN_WIDTH""x""$SCREEN_HEIGHT""x""$SCREEN_DEPTH"
|
||||||
|
|
||||||
|
nohup /usr/bin/xvfb-run --server-num=$DISPLAY_NUM \
|
||||||
|
--listen-tcp \
|
||||||
|
--server-args="-screen 0 "$GEOMETRY" -fbdir /var/tmp -dpi "$SCREEN_DPI" -listen tcp -noreset -ac +extension RANDR" \
|
||||||
|
/usr/bin/fluxbox -display "$DISPLAY" >/dev/null 2>&1 &
|
||||||
|
|
||||||
|
for i in $(seq 1 500); do
|
||||||
|
if xdpyinfo -display $DISPLAY >/dev/null 2>&1; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo "Waiting for Xvfb..."
|
||||||
|
sleep 0.2
|
||||||
|
done
|
||||||
|
|
||||||
|
nohup x11vnc -noprimary -nosetprimary -forever -shared -rfbport 5900 -rfbportv6 5900 -display "$DISPLAY" >/dev/null 2>&1 &
|
||||||
|
nohup /opt/bin/noVNC/utils/novnc_proxy --listen 7900 --vnc localhost:5900 >/dev/null 2>&1 &
|
||||||
|
|
||||||
|
cd /ms-playwright-agent
|
||||||
|
|
||||||
|
NOVNC_UUID=$(cat /proc/sys/kernel/random/uuid)
|
||||||
|
echo "novnc is listening on http://127.0.0.1:7900?path=$NOVNC_UUID&resize=scale&autoconnect=1"
|
||||||
|
|
||||||
|
PW_UUID=$(cat /proc/sys/kernel/random/uuid)
|
||||||
|
npx playwright run-server --port=5400 --path=/$PW_UUID
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
|
||||||
import { spawnAsync } from '../utils/spawnAsync';
|
import { spawnAsync } from '../utils/spawnAsync';
|
||||||
import * as utils from '../utils';
|
import * as utils from '../utils';
|
||||||
import { getPlaywrightVersion } from '../common/userAgent';
|
import { getPlaywrightVersion } from '../common/userAgent';
|
||||||
|
|
@ -102,13 +101,15 @@ async function buildPlaywrightImage() {
|
||||||
const dockerImage = await findDockerImage(baseImageName);
|
const dockerImage = await findDockerImage(baseImageName);
|
||||||
if (!dockerImage)
|
if (!dockerImage)
|
||||||
throw new Error(`Failed to pull ${baseImageName}`);
|
throw new Error(`Failed to pull ${baseImageName}`);
|
||||||
// 3. Launch container and install VNC in it
|
// 3. Delete previous build of the playwright image to avoid untagged images.
|
||||||
|
await deletePlaywrightImage();
|
||||||
|
// 4. Launch container and install VNC in it
|
||||||
console.log(`Building ${VRT_IMAGE_NAME}...`);
|
console.log(`Building ${VRT_IMAGE_NAME}...`);
|
||||||
const buildScriptText = await fs.promises.readFile(path.join(__dirname, 'build_docker_image.sh'), 'utf8');
|
|
||||||
const containerId = await dockerApi.launchContainer({
|
const containerId = await dockerApi.launchContainer({
|
||||||
imageId: dockerImage.imageId,
|
imageId: dockerImage.imageId,
|
||||||
autoRemove: false,
|
autoRemove: false,
|
||||||
command: ['/bin/bash', '-c', buildScriptText],
|
workingDir: '/ms-playwright-agent',
|
||||||
|
command: ['npx', 'playwright', 'docker', 'install-server-deps'],
|
||||||
waitUntil: 'not-running',
|
waitUntil: 'not-running',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -118,7 +119,8 @@ async function buildPlaywrightImage() {
|
||||||
containerId,
|
containerId,
|
||||||
repo: vrtRepo,
|
repo: vrtRepo,
|
||||||
tag: vrtTag,
|
tag: vrtTag,
|
||||||
entrypoint: '/entrypoint.sh',
|
workingDir: '/ms-playwright-agent',
|
||||||
|
entrypoint: ['npx', 'playwright', 'docker', 'run-server'],
|
||||||
env: {
|
env: {
|
||||||
'DISPLAY_NUM': '99',
|
'DISPLAY_NUM': '99',
|
||||||
'DISPLAY': ':99',
|
'DISPLAY': ':99',
|
||||||
|
|
@ -317,6 +319,20 @@ export function addDockerCLI(program: Command) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
dockerCommand.command('install-server-deps', { hidden: true })
|
||||||
|
.description('delete docker image, if any')
|
||||||
|
.action(async function() {
|
||||||
|
const { code } = await spawnAsync('bash', [path.join(__dirname, '..', '..', 'bin', 'container_install_deps.sh')], { stdio: 'inherit' });
|
||||||
|
if (code !== 0)
|
||||||
|
throw new Error('Failed to install server dependencies!');
|
||||||
|
});
|
||||||
|
|
||||||
|
dockerCommand.command('run-server', { hidden: true })
|
||||||
|
.description('delete docker image, if any')
|
||||||
|
.action(async function() {
|
||||||
|
await spawnAsync('bash', [path.join(__dirname, '..', '..', 'bin', 'container_run_server.sh')], { stdio: 'inherit' });
|
||||||
|
});
|
||||||
|
|
||||||
dockerCommand.command('print-status-json', { hidden: true })
|
dockerCommand.command('print-status-json', { hidden: true })
|
||||||
.description('print docker status')
|
.description('print docker status')
|
||||||
.action(async function(options) {
|
.action(async function(options) {
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ interface LaunchContainerOptions {
|
||||||
labels?: Record<string, string>;
|
labels?: Record<string, string>;
|
||||||
ports?: Number[];
|
ports?: Number[];
|
||||||
name?: string;
|
name?: string;
|
||||||
|
workingDir?: string;
|
||||||
waitUntil?: 'not-running' | 'next-exit' | 'removed';
|
waitUntil?: 'not-running' | 'next-exit' | 'removed';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,6 +78,7 @@ export async function launchContainer(options: LaunchContainerOptions): Promise<
|
||||||
}
|
}
|
||||||
const container = await postJSON(`/containers/create` + (options.name ? '?name=' + options.name : ''), {
|
const container = await postJSON(`/containers/create` + (options.name ? '?name=' + options.name : ''), {
|
||||||
Cmd: options.command,
|
Cmd: options.command,
|
||||||
|
WorkingDir: options.workingDir,
|
||||||
Labels: options.labels ?? {},
|
Labels: options.labels ?? {},
|
||||||
AttachStdout: true,
|
AttachStdout: true,
|
||||||
AttachStderr: true,
|
AttachStderr: true,
|
||||||
|
|
@ -134,7 +136,8 @@ interface CommitContainerOptions {
|
||||||
containerId: string,
|
containerId: string,
|
||||||
repo: string,
|
repo: string,
|
||||||
tag: string,
|
tag: string,
|
||||||
entrypoint?: string,
|
entrypoint?: string[],
|
||||||
|
workingDir?: string,
|
||||||
env?: {[key: string]: string | number | boolean | undefined},
|
env?: {[key: string]: string | number | boolean | undefined},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,7 +146,8 @@ export async function commitContainer(options: CommitContainerOptions) {
|
||||||
for (const [key, value] of Object.entries(options.env ?? {}))
|
for (const [key, value] of Object.entries(options.env ?? {}))
|
||||||
Env.push(`${key}=${value}`);
|
Env.push(`${key}=${value}`);
|
||||||
await postJSON(`/commit?container=${options.containerId}&repo=${options.repo}&tag=${options.tag}`, {
|
await postJSON(`/commit?container=${options.containerId}&repo=${options.repo}&tag=${options.tag}`, {
|
||||||
Entrypoint: options.entrypoint ?? '',
|
Entrypoint: options.entrypoint,
|
||||||
|
WorkingDir: options.workingDir,
|
||||||
Env,
|
Env,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -301,7 +301,7 @@ copyFiles.push({
|
||||||
// Babel doesn't touch JS files, so copy them manually.
|
// Babel doesn't touch JS files, so copy them manually.
|
||||||
// For example: diff_match_patch.js
|
// For example: diff_match_patch.js
|
||||||
copyFiles.push({
|
copyFiles.push({
|
||||||
files: 'packages/playwright-core/src/**/*.(js|sh)',
|
files: 'packages/playwright-core/src/**/*.js',
|
||||||
from: 'packages/playwright-core/src',
|
from: 'packages/playwright-core/src',
|
||||||
to: 'packages/playwright-core/lib',
|
to: 'packages/playwright-core/lib',
|
||||||
ignored: ['**/.eslintrc.js', '**/webpack*.config.js', '**/injected/**/*']
|
ignored: ['**/.eslintrc.js', '**/webpack*.config.js', '**/injected/**/*']
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue