fix: re-init repo on setup
This commit is contained in:
parent
2cf95685f7
commit
7476fcb261
9 changed files with 239 additions and 440 deletions
|
|
@ -97,7 +97,6 @@ COPY . .
|
|||
|
||||
RUN bun run build
|
||||
RUN bun build apps/agent/src/index.ts --outfile .output/agent/index.mjs --target bun
|
||||
RUN bun run build:backend-integration
|
||||
|
||||
FROM base AS production
|
||||
|
||||
|
|
@ -124,7 +123,3 @@ COPY ./LICENSE ./LICENSE.md
|
|||
EXPOSE 4096
|
||||
|
||||
CMD ["bun", ".output/server/index.mjs"]
|
||||
|
||||
FROM production AS backend-integration
|
||||
|
||||
CMD ["bun", ".output/backend-integration/index.js"]
|
||||
|
|
|
|||
47
app/test/backend-integration/Dockerfile
Normal file
47
app/test/backend-integration/Dockerfile
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
FROM oven/bun:1.3.13-alpine@sha256:4de475389889577f346c636f956b42a5c31501b654664e9ae5726f94d7bb5349
|
||||
|
||||
ARG RESTIC_VERSION="0.18.1"
|
||||
ARG RCLONE_VERSION="1.74.0"
|
||||
ARG SHOUTRRR_VERSION="0.14.3"
|
||||
ARG TARGETARCH
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apk update --no-cache && \
|
||||
apk upgrade --no-cache && \
|
||||
apk add --no-cache acl attr cifs-utils curl bzip2 unzip tar davfs2=1.6.1-r2 openssh-client fuse3 sshfs tini tzdata
|
||||
|
||||
COPY ./package.json ./bun.lock ./
|
||||
COPY ./packages/core/package.json ./packages/core/package.json
|
||||
COPY ./packages/contracts/package.json ./packages/contracts/package.json
|
||||
COPY ./apps/agent/package.json ./apps/agent/package.json
|
||||
COPY ./apps/docs/package.json ./apps/docs/package.json
|
||||
|
||||
RUN VITE_GIT_HOOKS=0 bun install --frozen-lockfile
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN bun run build:backend-integration
|
||||
|
||||
RUN echo "Building for ${TARGETARCH}" && if [ "${TARGETARCH}" = "arm64" ]; then \
|
||||
curl -fL -o restic.bz2 "https://github.com/restic/restic/releases/download/v${RESTIC_VERSION}/restic_${RESTIC_VERSION}_linux_arm64.bz2"; \
|
||||
curl -fL -o rclone.zip "https://github.com/rclone/rclone/releases/download/v${RCLONE_VERSION}/rclone-v${RCLONE_VERSION}-linux-arm64.zip"; \
|
||||
unzip rclone.zip; \
|
||||
curl -fL -o shoutrrr.tar.gz "https://github.com/nicholas-fedor/shoutrrr/releases/download/v${SHOUTRRR_VERSION}/shoutrrr_linux_arm64v8_${SHOUTRRR_VERSION}.tar.gz"; \
|
||||
elif [ "${TARGETARCH}" = "amd64" ]; then \
|
||||
curl -fL -o restic.bz2 "https://github.com/restic/restic/releases/download/v${RESTIC_VERSION}/restic_${RESTIC_VERSION}_linux_amd64.bz2"; \
|
||||
curl -fL -o rclone.zip "https://github.com/rclone/rclone/releases/download/v${RCLONE_VERSION}/rclone-v${RCLONE_VERSION}-linux-amd64.zip"; \
|
||||
unzip rclone.zip; \
|
||||
curl -fL -o shoutrrr.tar.gz "https://github.com/nicholas-fedor/shoutrrr/releases/download/v${SHOUTRRR_VERSION}/shoutrrr_linux_amd64_${SHOUTRRR_VERSION}.tar.gz"; \
|
||||
else \
|
||||
echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
RUN bzip2 -d restic.bz2 && install -m 0755 restic /usr/local/bin/restic
|
||||
RUN mv rclone-v*-linux-*/rclone /usr/local/bin/rclone && chmod +x /usr/local/bin/rclone
|
||||
RUN tar -xzf shoutrrr.tar.gz && install -m 0755 shoutrrr /usr/local/bin/shoutrrr
|
||||
|
||||
ENTRYPOINT ["/sbin/tini", "-s", "--"]
|
||||
|
||||
CMD ["bun", ".output/backend-integration/index.js"]
|
||||
|
|
@ -11,7 +11,7 @@ if [[ ! -f "$config_path" ]]; then
|
|||
if [[ "$config_path" == "$default_config_path" ]]; then
|
||||
echo "Generated config not found: $config_path" >&2
|
||||
echo "Run the target bootstrap first:" >&2
|
||||
echo " bash app/test/backend-integration/setup-debian-target.sh" >&2
|
||||
echo " bash app/test/backend-integration/setup-target.sh" >&2
|
||||
else
|
||||
echo "Config file not found: $config_path" >&2
|
||||
fi
|
||||
|
|
@ -25,7 +25,7 @@ else
|
|||
config_path="$config_dir/$(basename "$config_path")"
|
||||
fi
|
||||
|
||||
docker build --target backend-integration -t "$image_tag" "$repo_root"
|
||||
docker build -f "$script_dir/Dockerfile" -t "$image_tag" "$repo_root"
|
||||
docker run --rm \
|
||||
--cap-add SYS_ADMIN \
|
||||
--device /dev/fuse:/dev/fuse \
|
||||
|
|
|
|||
|
|
@ -172,9 +172,27 @@ async function restoreSnapshot(
|
|||
}
|
||||
|
||||
async function cleanupScenario(backend: VolumeBackend, restoreTarget: string, scenarioWorkspace: string) {
|
||||
await backend.unmount();
|
||||
await fs.rm(restoreTarget, { recursive: true, force: true });
|
||||
await fs.rm(scenarioWorkspace, { recursive: true, force: true });
|
||||
const cleanupErrors: string[] = [];
|
||||
const unmountResult = await backend.unmount();
|
||||
const didUnmount = unmountResult.status !== "error";
|
||||
|
||||
if (!didUnmount) {
|
||||
cleanupErrors.push(unmountResult.error ?? `Unmount returned ${unmountResult.status}`);
|
||||
}
|
||||
|
||||
await fs.rm(restoreTarget, { recursive: true, force: true }).catch((error) => {
|
||||
cleanupErrors.push(`Failed to remove restore target: ${formatError(error)}`);
|
||||
});
|
||||
|
||||
if (didUnmount) {
|
||||
await fs.rm(scenarioWorkspace, { recursive: true, force: true }).catch((error) => {
|
||||
cleanupErrors.push(`Failed to remove scenario workspace: ${formatError(error)}`);
|
||||
});
|
||||
}
|
||||
|
||||
if (cleanupErrors.length > 0) {
|
||||
throw new Error(cleanupErrors.join("; "));
|
||||
}
|
||||
}
|
||||
|
||||
async function runScenario(scenario: IntegrationScenario, runId: string): Promise<ScenarioReport> {
|
||||
|
|
@ -256,16 +274,30 @@ async function runScenario(scenario: IntegrationScenario, runId: string): Promis
|
|||
await runStage(stages, "verify-restore", async () => {
|
||||
await verifyFilesystemEntries(restoreTarget, scenario.expectedEntries, "restored output");
|
||||
});
|
||||
|
||||
logScenario(scenario.id, "passed");
|
||||
} catch (error) {
|
||||
status = "failed";
|
||||
errorMessage = formatError(error);
|
||||
logScenario(scenario.id, `failed: ${errorMessage}`);
|
||||
} finally {
|
||||
await runStage(stages, "cleanup", async () => {
|
||||
await cleanupScenario(backend, restoreTarget, scenarioWorkspace);
|
||||
});
|
||||
try {
|
||||
await runStage(stages, "cleanup", async () => {
|
||||
await cleanupScenario(backend, restoreTarget, scenarioWorkspace);
|
||||
});
|
||||
} catch (error) {
|
||||
status = "failed";
|
||||
const cleanupError = formatError(error);
|
||||
|
||||
if (!errorMessage) {
|
||||
errorMessage = cleanupError;
|
||||
logScenario(scenario.id, `failed: ${cleanupError}`);
|
||||
} else {
|
||||
logScenario(scenario.id, `cleanup failed: ${cleanupError}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (status === "passed") {
|
||||
logScenario(scenario.id, "passed");
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -42,19 +42,19 @@ function assertMetadata(
|
|||
expected: ExpectedEntry,
|
||||
actual: { uid?: number; gid?: number; mode?: number },
|
||||
) {
|
||||
if (expected.uid && actual.uid !== expected.uid) {
|
||||
if (expected.uid !== undefined && actual.uid !== expected.uid) {
|
||||
throw new Error(`${entryLabel} uid mismatch: expected ${expected.uid}, got ${String(actual.uid)}`);
|
||||
}
|
||||
|
||||
if (expected.gid && actual.gid !== expected.gid) {
|
||||
if (expected.gid !== undefined && actual.gid !== expected.gid) {
|
||||
throw new Error(`${entryLabel} gid mismatch: expected ${expected.gid}, got ${String(actual.gid)}`);
|
||||
}
|
||||
|
||||
if (!expected.mode) {
|
||||
if (expected.mode === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!actual.mode) {
|
||||
if (actual.mode === undefined) {
|
||||
throw new Error(`${entryLabel} mode is missing`);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,9 @@ sftp_password="$4"
|
|||
webdav_password="$5"
|
||||
restic_password="$6"
|
||||
public_key="$(printf '%s' "$7" | base64 -d)"
|
||||
repo_path="/srv/zerobyte-backend-integration/restic-repo"
|
||||
repo_password_fingerprint_path="$repo_path/.zerobyte-password-sha256"
|
||||
repo_password_fingerprint="$(printf '%s' "$restic_password" | sha256sum | cut -d' ' -f1)"
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
|
|
@ -62,6 +65,23 @@ write_file() {
|
|||
cat >"$file_path"
|
||||
}
|
||||
|
||||
initialize_restic_repo() {
|
||||
local password_file
|
||||
|
||||
rm -rf "$repo_path"
|
||||
install -d -o zerobyte-sftp -g zerobyte-sftp -m 0700 "$repo_path"
|
||||
|
||||
password_file="$(mktemp)"
|
||||
printf '%s\n' "$restic_password" >"$password_file"
|
||||
chown zerobyte-sftp:zerobyte-sftp "$password_file"
|
||||
chmod 0600 "$password_file"
|
||||
su -s /bin/sh -c "restic init --repo '$repo_path' --password-file '$password_file'" zerobyte-sftp
|
||||
rm -f "$password_file"
|
||||
|
||||
printf '%s\n' "$repo_password_fingerprint" >"$repo_password_fingerprint_path"
|
||||
chmod 0600 "$repo_password_fingerprint_path"
|
||||
}
|
||||
|
||||
apt-get update
|
||||
apt-get install -y apache2 apache2-utils nfs-kernel-server openssh-server restic rpcbind samba
|
||||
|
||||
|
|
@ -75,7 +95,6 @@ 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 {} +
|
||||
|
||||
install -d -o zerobyte-sftp -g zerobyte-sftp -m 0700 /srv/zerobyte-backend-integration/restic-repo
|
||||
install -d -o zerobyte-sftp -g zerobyte-sftp -m 0700 /home/zerobyte-sftp
|
||||
install -d -o zerobyte-sftp -g zerobyte-sftp -m 0700 /home/zerobyte-sftp/.ssh
|
||||
printf '%s\n' "$public_key" >/home/zerobyte-sftp/.ssh/authorized_keys
|
||||
|
|
@ -88,13 +107,10 @@ printf 'zerobyte-sftp:%s\n' "$sftp_password" | chpasswd
|
|||
passwd -u zerobyte-sftp >/dev/null 2>&1 || true
|
||||
htpasswd -bc /etc/apache2/zerobyte-backend-integration.htpasswd zerobyte-webdav "$webdav_password" >/dev/null
|
||||
|
||||
if [[ ! -f /srv/zerobyte-backend-integration/restic-repo/config ]]; then
|
||||
password_file="$(mktemp)"
|
||||
printf '%s\n' "$restic_password" >"$password_file"
|
||||
chown zerobyte-sftp:zerobyte-sftp "$password_file"
|
||||
chmod 0600 "$password_file"
|
||||
su -s /bin/sh -c "restic init --repo /srv/zerobyte-backend-integration/restic-repo --password-file '$password_file'" zerobyte-sftp
|
||||
rm -f "$password_file"
|
||||
if [[ ! -f "$repo_path/config" ]]; then
|
||||
initialize_restic_repo
|
||||
elif [[ ! -f "$repo_password_fingerprint_path" ]] || [[ "$(cat "$repo_password_fingerprint_path")" != "$repo_password_fingerprint" ]]; then
|
||||
initialize_restic_repo
|
||||
fi
|
||||
|
||||
write_file /etc/exports <<'EOF'
|
||||
|
|
|
|||
|
|
@ -27,6 +27,12 @@ const nfsEntries = [
|
|||
{ path: "docs/readme.md", type: "file", uid: fixtureUid, gid: fixtureGid, mode: "0644", text: readmeText },
|
||||
];
|
||||
|
||||
const smbEntries = [
|
||||
{ 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" },
|
||||
|
|
@ -58,13 +64,14 @@ const config = {
|
|||
share: "zerobyte-backend-integration",
|
||||
username: "zerobyte-smb",
|
||||
password: smbPassword,
|
||||
mapToContainerUidGid: false,
|
||||
vers: "3.0",
|
||||
port: 445,
|
||||
readOnly: true,
|
||||
},
|
||||
repository: { backend: "local", path: "repo-smb" },
|
||||
fixtureRoot: "case-a",
|
||||
expectedEntries: nfsEntries,
|
||||
expectedEntries: smbEntries,
|
||||
},
|
||||
{
|
||||
id: "sftp-local-repo",
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@
|
|||
"check": "vp check",
|
||||
"dev": "NODE_ENV=development bunx --bun vite",
|
||||
"build": "bunx --bun vite build",
|
||||
"build:backend-integration": "bun build app/test/backend-integration/index.ts --outdir .output/backend-integration --target bun",
|
||||
"start": "bun run .output/server/index.mjs",
|
||||
"build:backend-integration": "bun build app/test/backend-integration/index.ts --outdir .output/backend-integration --target bun",
|
||||
"test:integration:backends": "bash app/test/backend-integration/run.sh",
|
||||
"preview": "bunx --bun vite preview",
|
||||
"cli:dev": "bun run app/server/cli/main.ts",
|
||||
|
|
|
|||
Loading…
Reference in a new issue