test: automate NFS integration
This commit is contained in:
parent
dfd787c8ae
commit
1526e3d441
8 changed files with 89 additions and 56 deletions
|
|
@ -4,8 +4,6 @@ set -euo pipefail
|
|||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
TARGET_HOST="192.168.2.41"
|
||||
TARGET="root@$TARGET_HOST"
|
||||
FIXTURE_UID="1000"
|
||||
FIXTURE_GID="1000"
|
||||
|
||||
ARTIFACTS_DIR="$SCRIPT_DIR/artifacts/$TARGET_HOST"
|
||||
KNOWN_HOSTS_PATH="$ARTIFACTS_DIR/known_hosts"
|
||||
|
|
@ -30,12 +28,10 @@ chmod 700 "$ARTIFACTS_DIR"
|
|||
|
||||
SFTP_PASSWORD="$(read_or_create_secret "$SFTP_PASSWORD_FILE")"
|
||||
|
||||
ssh "$TARGET" bash -s -- "$FIXTURE_UID" "$FIXTURE_GID" "$SFTP_PASSWORD" <<'REMOTE'
|
||||
ssh "$TARGET" bash -s -- "$SFTP_PASSWORD" <<'REMOTE'
|
||||
set -euo pipefail
|
||||
|
||||
fixture_uid="$1"
|
||||
fixture_gid="$2"
|
||||
sftp_password="$3"
|
||||
sftp_password="$1"
|
||||
legacy_sshd_dir="/etc/ssh/zerobyte-backend-integration-legacy"
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
|
@ -46,30 +42,19 @@ write_file() {
|
|||
}
|
||||
|
||||
apt-get update
|
||||
apt-get install -y nfs-kernel-server openssh-server rpcbind
|
||||
apt-get install -y openssh-server
|
||||
|
||||
id -u zerobyte-sftp >/dev/null 2>&1 || useradd --create-home --home-dir /home/zerobyte-sftp --shell /bin/bash zerobyte-sftp
|
||||
|
||||
install -d -m 0755 /srv/zerobyte-backend-integration/fixtures/case-a/docs
|
||||
printf 'hello from zerobyte integration\n' >/srv/zerobyte-backend-integration/fixtures/case-a/hello.txt
|
||||
printf 'fixture documentation\n' >/srv/zerobyte-backend-integration/fixtures/case-a/docs/readme.md
|
||||
chown -R "$fixture_uid:$fixture_gid" /srv/zerobyte-backend-integration/fixtures
|
||||
find /srv/zerobyte-backend-integration/fixtures -type d -exec chmod 0755 {} +
|
||||
find /srv/zerobyte-backend-integration/fixtures -type f -exec chmod 0644 {} +
|
||||
|
||||
printf 'zerobyte-sftp:%s\n' "$sftp_password" | chpasswd
|
||||
passwd -u zerobyte-sftp >/dev/null 2>&1 || true
|
||||
|
||||
write_file /etc/exports <<'EOF'
|
||||
/srv/zerobyte-backend-integration/fixtures *(ro,sync,no_subtree_check,insecure)
|
||||
EOF
|
||||
exportfs -ra
|
||||
systemctl unmask rpcbind rpcbind.socket >/dev/null 2>&1
|
||||
systemctl start rpcbind.socket
|
||||
systemctl start rpcbind
|
||||
systemctl start proc-fs-nfsd.mount
|
||||
systemctl restart nfs-kernel-server
|
||||
|
||||
install -d -m 0700 "$legacy_sshd_dir"
|
||||
if [[ ! -f "$legacy_sshd_dir/ssh_host_rsa_key" ]]; then
|
||||
ssh-keygen -q -t rsa -b 2048 -N "" -f "$legacy_sshd_dir/ssh_host_rsa_key"
|
||||
|
|
@ -143,8 +128,6 @@ if ! ssh-keyscan -T 5 -p 2222 "$TARGET_HOST" >>"$KNOWN_HOSTS_PATH" 2>/dev/null;
|
|||
fi
|
||||
|
||||
INTEGRATION_HOST="$TARGET_HOST" \
|
||||
FIXTURE_UID="$FIXTURE_UID" \
|
||||
FIXTURE_GID="$FIXTURE_GID" \
|
||||
SFTP_PASSWORD="$SFTP_PASSWORD" \
|
||||
KNOWN_HOSTS_PATH="$KNOWN_HOSTS_PATH" \
|
||||
CONFIG_PATH="$CONFIG_PATH" \
|
||||
|
|
|
|||
|
|
@ -4,13 +4,7 @@ function getRequiredEnv(name: string) {
|
|||
return process.env[name]!;
|
||||
}
|
||||
|
||||
function getRequiredNumberEnv(name: string) {
|
||||
return Number.parseInt(getRequiredEnv(name), 10);
|
||||
}
|
||||
|
||||
const host = getRequiredEnv("INTEGRATION_HOST");
|
||||
const fixtureUid = getRequiredNumberEnv("FIXTURE_UID");
|
||||
const fixtureGid = getRequiredNumberEnv("FIXTURE_GID");
|
||||
const sftpPassword = getRequiredEnv("SFTP_PASSWORD");
|
||||
const knownHosts = fs.readFileSync(getRequiredEnv("KNOWN_HOSTS_PATH"), "utf8");
|
||||
const configPath = getRequiredEnv("CONFIG_PATH");
|
||||
|
|
@ -18,12 +12,6 @@ const configPath = getRequiredEnv("CONFIG_PATH");
|
|||
const fileText = "hello from zerobyte integration\n";
|
||||
const readmeText = "fixture documentation\n";
|
||||
|
||||
const nfsEntries = [
|
||||
{ path: "hello.txt", type: "file", uid: fixtureUid, gid: fixtureGid, mode: "0644", text: fileText },
|
||||
{ path: "docs", type: "directory", uid: fixtureUid, gid: fixtureGid, mode: "0755" },
|
||||
{ path: "docs/readme.md", type: "file", uid: fixtureUid, gid: fixtureGid, mode: "0644", text: readmeText },
|
||||
];
|
||||
|
||||
const contentOnlyEntries = [
|
||||
{ path: "hello.txt", type: "file", text: fileText },
|
||||
{ path: "docs", type: "directory" },
|
||||
|
|
@ -33,20 +21,6 @@ const contentOnlyEntries = [
|
|||
const config = {
|
||||
version: 1,
|
||||
scenarios: [
|
||||
{
|
||||
id: "nfs-local-repo",
|
||||
volume: {
|
||||
backend: "nfs",
|
||||
server: host,
|
||||
exportPath: "/srv/zerobyte-backend-integration/fixtures",
|
||||
port: 2049,
|
||||
version: "4.1",
|
||||
readOnly: true,
|
||||
},
|
||||
repository: { backend: "local", path: "repo-nfs" },
|
||||
fixtureRoot: "case-a",
|
||||
expectedEntries: nfsEntries,
|
||||
},
|
||||
{
|
||||
id: "sftp-legacy-rsa-hostkey-local-repo",
|
||||
volume: {
|
||||
|
|
|
|||
|
|
@ -81,6 +81,18 @@ services:
|
|||
timeout: 5s
|
||||
retries: 30
|
||||
|
||||
nfs:
|
||||
build:
|
||||
context: ./nfs
|
||||
privileged: true
|
||||
volumes:
|
||||
- nfs-data:/srv/zerobyte-integration
|
||||
healthcheck:
|
||||
test: ["CMD", "sh", "-c", "exportfs -v | grep -q /srv/zerobyte-integration/fixtures"]
|
||||
interval: 1s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
|
||||
integration:
|
||||
build:
|
||||
context: ../../../..
|
||||
|
|
@ -96,6 +108,8 @@ services:
|
|||
condition: service_healthy
|
||||
smb:
|
||||
condition: service_healthy
|
||||
nfs:
|
||||
condition: service_healthy
|
||||
cap_add:
|
||||
- SYS_ADMIN
|
||||
devices:
|
||||
|
|
@ -111,3 +125,4 @@ services:
|
|||
volumes:
|
||||
rustfs-data:
|
||||
sftp-data:
|
||||
nfs-data:
|
||||
|
|
|
|||
10
app/test/integration/infra/nfs/Dockerfile
Normal file
10
app/test/integration/infra/nfs/Dockerfile
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
FROM alpine:3.21
|
||||
|
||||
RUN apk add --no-cache nfs-utils
|
||||
|
||||
COPY entrypoint.sh /usr/local/bin/zerobyte-nfs-entrypoint
|
||||
RUN chmod +x /usr/local/bin/zerobyte-nfs-entrypoint
|
||||
|
||||
EXPOSE 2049
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/zerobyte-nfs-entrypoint"]
|
||||
30
app/test/integration/infra/nfs/entrypoint.sh
Normal file
30
app/test/integration/infra/nfs/entrypoint.sh
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
FIXTURE_ROOT="/srv/zerobyte-integration/fixtures"
|
||||
|
||||
install -d -m 0755 "$FIXTURE_ROOT/case-a/docs"
|
||||
printf 'hello from zerobyte integration\n' >"$FIXTURE_ROOT/case-a/hello.txt"
|
||||
printf 'fixture documentation\n' >"$FIXTURE_ROOT/case-a/docs/readme.md"
|
||||
find "$FIXTURE_ROOT" -type d -exec chmod 0755 {} +
|
||||
find "$FIXTURE_ROOT" -type f -exec chmod 0644 {} +
|
||||
|
||||
mkdir -p /run/rpc_pipefs /proc/fs/nfsd
|
||||
mountpoint -q /proc/fs/nfsd || mount -t nfsd nfsd /proc/fs/nfsd
|
||||
mountpoint -q /run/rpc_pipefs || mount -t rpc_pipefs rpc_pipefs /run/rpc_pipefs
|
||||
printf '1\n' >/proc/fs/nfsd/nfsv4gracetime
|
||||
printf '1\n' >/proc/fs/nfsd/nfsv4leasetime
|
||||
|
||||
cat >/etc/exports <<EOF
|
||||
$FIXTURE_ROOT *(ro,sync,no_subtree_check,insecure,fsid=0)
|
||||
EOF
|
||||
|
||||
rpcbind -w
|
||||
rpc.mountd --no-udp --foreground &
|
||||
exportfs -ra
|
||||
rpc.nfsd -V 4 -V 4.1 8
|
||||
|
||||
trap 'exportfs -ua; rpc.nfsd 0' INT TERM
|
||||
while kill -0 "$(pidof rpc.mountd)" 2>/dev/null; do
|
||||
sleep 1
|
||||
done
|
||||
|
|
@ -36,7 +36,7 @@ if [[ "$exit_code" -eq 0 ]]; then
|
|||
fi
|
||||
|
||||
if [[ "$exit_code" -eq 0 ]]; then
|
||||
"${compose[@]}" up --build --no-color --detach --wait sftp webdav smb >>"$docker_output_log" 2>&1 || exit_code=$?
|
||||
"${compose[@]}" up --build --no-color --detach --wait sftp webdav smb nfs >>"$docker_output_log" 2>&1 || exit_code=$?
|
||||
fi
|
||||
|
||||
if [[ "$exit_code" -eq 0 ]]; then
|
||||
|
|
|
|||
14
app/test/integration/src/helpers/nfs.ts
Normal file
14
app/test/integration/src/helpers/nfs.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import type { BackendConfig } from "@zerobyte/contracts/volumes";
|
||||
|
||||
export const NFS_HOST = "nfs";
|
||||
export const NFS_PORT = 2049;
|
||||
export const NFS_EXPORT_PATH = "/";
|
||||
|
||||
export const buildNfsVolumeConfig = (): BackendConfig => ({
|
||||
backend: "nfs",
|
||||
server: NFS_HOST,
|
||||
exportPath: NFS_EXPORT_PATH,
|
||||
port: NFS_PORT,
|
||||
version: "4.1",
|
||||
readOnly: true,
|
||||
});
|
||||
|
|
@ -4,6 +4,7 @@ import path from "node:path";
|
|||
import type { RepositoryConfig } from "@zerobyte/core/restic";
|
||||
import { Effect } from "effect";
|
||||
import { expect, test } from "vitest";
|
||||
import { makeNfsBackend } from "../../../../apps/agent/src/volume-host/backends/nfs";
|
||||
import { makeSmbBackend } from "../../../../apps/agent/src/volume-host/backends/smb";
|
||||
import { makeSftpBackend } from "../../../../apps/agent/src/volume-host/backends/sftp";
|
||||
import { makeWebdavBackend } from "../../../../apps/agent/src/volume-host/backends/webdav";
|
||||
|
|
@ -11,6 +12,7 @@ import type { VolumeBackend } from "../../../../apps/agent/src/volume-host/types
|
|||
import { INTEGRATION_ORGANIZATION_ID, INTEGRATION_RUNS_DIR } from "./constants";
|
||||
import { assertFixtureSourceExists, assertRestoredFixture, assertSnapshotContainsFixture } from "./helpers/assertions";
|
||||
import { createStaticVolumeFixture } from "./helpers/fixture";
|
||||
import { buildNfsVolumeConfig } from "./helpers/nfs";
|
||||
import { createIntegrationRestic } from "./helpers/restic";
|
||||
import {
|
||||
buildSftpPasswordVolumeConfig,
|
||||
|
|
@ -57,6 +59,11 @@ const scenarios: VolumeScenario[] = [
|
|||
name: "SMB volume with local repository",
|
||||
createBackend: async (mountPath) => makeSmbBackend(buildSmbVolumeConfig(), mountPath),
|
||||
},
|
||||
{
|
||||
id: "nfs-local-repo",
|
||||
name: "NFS volume with local repository",
|
||||
createBackend: async (mountPath) => makeNfsBackend(buildNfsVolumeConfig(), mountPath),
|
||||
},
|
||||
];
|
||||
|
||||
const volumeMountTest = process.env.SKIP_VOLUME_MOUNT_INTEGRATION_TESTS === "true" ? test.skip : test;
|
||||
|
|
@ -97,6 +104,15 @@ volumeMountTest.concurrent.each(scenarios)("$name can backup and restore static
|
|||
try {
|
||||
await fs.mkdir(workspace, { recursive: true });
|
||||
|
||||
const initResult = await Effect.runPromise(
|
||||
restic.init(repositoryConfig, {
|
||||
organizationId: INTEGRATION_ORGANIZATION_ID,
|
||||
timeoutMs: 120_000,
|
||||
}),
|
||||
);
|
||||
expect(initResult.success).toBe(true);
|
||||
expect(initResult.error).toBeNull();
|
||||
|
||||
backend = await scenario.createBackend(mountPath);
|
||||
|
||||
const mountResult = await backend.mount();
|
||||
|
|
@ -108,15 +124,6 @@ volumeMountTest.concurrent.each(scenarios)("$name can backup and restore static
|
|||
const fixture = createStaticVolumeFixture(path.join(mountPath, "case-a"));
|
||||
await assertFixtureSourceExists(fixture);
|
||||
|
||||
const initResult = await Effect.runPromise(
|
||||
restic.init(repositoryConfig, {
|
||||
organizationId: INTEGRATION_ORGANIZATION_ID,
|
||||
timeoutMs: 120_000,
|
||||
}),
|
||||
);
|
||||
expect(initResult.success).toBe(true);
|
||||
expect(initResult.error).toBeNull();
|
||||
|
||||
const backupResult = await Effect.runPromise(
|
||||
restic.backup(repositoryConfig, fixture.sourceRoot, {
|
||||
organizationId: INTEGRATION_ORGANIZATION_ID,
|
||||
|
|
|
|||
Loading…
Reference in a new issue