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:
Родитель
6b4afbb8df
Коммит
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
|
||||
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
|
|
@ -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 */
|
||||
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { spawnAsync } from '../utils/spawnAsync';
|
||||
import * as utils from '../utils';
|
||||
import { getPlaywrightVersion } from '../common/userAgent';
|
||||
|
@ -102,13 +101,15 @@ async function buildPlaywrightImage() {
|
|||
const dockerImage = await findDockerImage(baseImageName);
|
||||
if (!dockerImage)
|
||||
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}...`);
|
||||
const buildScriptText = await fs.promises.readFile(path.join(__dirname, 'build_docker_image.sh'), 'utf8');
|
||||
const containerId = await dockerApi.launchContainer({
|
||||
imageId: dockerImage.imageId,
|
||||
autoRemove: false,
|
||||
command: ['/bin/bash', '-c', buildScriptText],
|
||||
workingDir: '/ms-playwright-agent',
|
||||
command: ['npx', 'playwright', 'docker', 'install-server-deps'],
|
||||
waitUntil: 'not-running',
|
||||
});
|
||||
|
||||
|
@ -118,7 +119,8 @@ async function buildPlaywrightImage() {
|
|||
containerId,
|
||||
repo: vrtRepo,
|
||||
tag: vrtTag,
|
||||
entrypoint: '/entrypoint.sh',
|
||||
workingDir: '/ms-playwright-agent',
|
||||
entrypoint: ['npx', 'playwright', 'docker', 'run-server'],
|
||||
env: {
|
||||
'DISPLAY_NUM': '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 })
|
||||
.description('print docker status')
|
||||
.action(async function(options) {
|
||||
|
|
|
@ -65,6 +65,7 @@ interface LaunchContainerOptions {
|
|||
labels?: Record<string, string>;
|
||||
ports?: Number[];
|
||||
name?: string;
|
||||
workingDir?: string;
|
||||
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 : ''), {
|
||||
Cmd: options.command,
|
||||
WorkingDir: options.workingDir,
|
||||
Labels: options.labels ?? {},
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
|
@ -134,7 +136,8 @@ interface CommitContainerOptions {
|
|||
containerId: string,
|
||||
repo: string,
|
||||
tag: string,
|
||||
entrypoint?: string,
|
||||
entrypoint?: string[],
|
||||
workingDir?: string,
|
||||
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 ?? {}))
|
||||
Env.push(`${key}=${value}`);
|
||||
await postJSON(`/commit?container=${options.containerId}&repo=${options.repo}&tag=${options.tag}`, {
|
||||
Entrypoint: options.entrypoint ?? '',
|
||||
Entrypoint: options.entrypoint,
|
||||
WorkingDir: options.workingDir,
|
||||
Env,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -301,7 +301,7 @@ copyFiles.push({
|
|||
// Babel doesn't touch JS files, so copy them manually.
|
||||
// For example: diff_match_patch.js
|
||||
copyFiles.push({
|
||||
files: 'packages/playwright-core/src/**/*.(js|sh)',
|
||||
files: 'packages/playwright-core/src/**/*.js',
|
||||
from: 'packages/playwright-core/src',
|
||||
to: 'packages/playwright-core/lib',
|
||||
ignored: ['**/.eslintrc.js', '**/webpack*.config.js', '**/injected/**/*']
|
||||
|
|
Загрузка…
Ссылка в новой задаче