-
Notifications
You must be signed in to change notification settings - Fork 0
feat(cliproxy): back up manager analytics to S3 #2233
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -88,3 +88,63 @@ cliproxy_download_usage_from_s3() { | |
| "$(cliproxy_usage_s3_uri)" \ | ||
| "$dst" || true | ||
| } | ||
|
|
||
| cliproxy_manager_backup_s3_uri() { | ||
|
cubic-dev-ai[bot] marked this conversation as resolved.
|
||
| local object_name="${1:-analytics-backup.tar.gz}" | ||
| printf 's3://%s/cpa-manager-plus/%s' "${OBJECTSTORE_BUCKET:?OBJECTSTORE_BUCKET is required}" "$object_name" | ||
| } | ||
|
|
||
| cliproxy_backup_manager_data() ( | ||
| set -euo pipefail | ||
| umask 077 | ||
|
|
||
| local data_dir="$1" | ||
| local database_path="${data_dir}/usage.sqlite" | ||
| local data_key_path="${data_dir}/data.key" | ||
| local backup_root snapshot_dir snapshot_path archive_path integrity hourly_object | ||
|
|
||
| if [ ! -f "$database_path" ] || [ ! -f "$data_key_path" ]; then | ||
| echo "⚠️ CPA Manager Plus database or data key is missing; skipping analytics backup" >&2 | ||
| return 0 | ||
| fi | ||
|
|
||
| backup_root="$(mktemp -d "${TMPDIR:-/tmp}/cpa-manager-plus-backup.XXXXXX")" | ||
| trap 'rm -rf -- "$backup_root"' EXIT | ||
| snapshot_dir="${backup_root}/cpa-manager-plus" | ||
| snapshot_path="${snapshot_dir}/usage.sqlite" | ||
| archive_path="${backup_root}/analytics-backup.tar.gz" | ||
| mkdir -p "$snapshot_dir" | ||
|
|
||
| # The live database uses WAL mode. SQLite's online backup command produces a | ||
| # consistent standalone database without copying transient -wal/-shm files. | ||
| @sqlite3@ "$database_path" ".timeout 5000" ".backup '${snapshot_path}'" | ||
|
|
||
| integrity="$(@sqlite3@ "$snapshot_path" "PRAGMA integrity_check;")" | ||
| if [ "$integrity" != "ok" ]; then | ||
| echo "CPA Manager Plus SQLite snapshot failed its integrity check" >&2 | ||
| return 1 | ||
| fi | ||
|
|
||
| install -m 600 "$data_key_path" "${snapshot_dir}/data.key" | ||
| @tar@ -czf "$archive_path" -C "$snapshot_dir" usage.sqlite data.key | ||
| chmod 600 "$archive_path" | ||
|
|
||
| # Keep one rollback point per UTC hour in addition to the convenient latest | ||
| # object. Auth-file triggers within the same hour replace only that hour's slot. | ||
| hourly_object="analytics-backup-$(date -u +%H).tar.gz" | ||
| AWS_ACCESS_KEY_ID="$OBJECTSTORE_ACCESS_KEY" \ | ||
| AWS_SECRET_ACCESS_KEY="$OBJECTSTORE_SECRET_KEY" \ | ||
| @aws@ s3 cp \ | ||
| --endpoint-url="$OBJECTSTORE_ENDPOINT" \ | ||
| --only-show-errors \ | ||
| "$archive_path" \ | ||
| "$(cliproxy_manager_backup_s3_uri "$hourly_object")" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: The new hourly-rollback upload runs before the primary "latest" upload, and neither Prompt for AI agents |
||
|
|
||
| AWS_ACCESS_KEY_ID="$OBJECTSTORE_ACCESS_KEY" \ | ||
| AWS_SECRET_ACCESS_KEY="$OBJECTSTORE_SECRET_KEY" \ | ||
| @aws@ s3 cp \ | ||
| --endpoint-url="$OBJECTSTORE_ENDPOINT" \ | ||
| --only-show-errors \ | ||
| "$archive_path" \ | ||
| "$(cliproxy_manager_backup_s3_uri)" | ||
| ) | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -51,10 +51,23 @@ if ! ensure_container_removed; then | |||||
| exit 1 | ||||||
| fi | ||||||
|
|
||||||
| host_uid="$(id -u)" | ||||||
| host_gid="$(id -g)" | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: The fallback wrapper can make this capture the Docker group's GID instead of the service user's primary GID, causing the data tree and container process to run as Prompt for AI agents
Suggested change
|
||||||
|
|
||||||
| # Older launches ran as root inside the container. Migrate the bind-mounted data | ||||||
| # before switching to the service user's numeric identity. | ||||||
| @docker@ run --rm \ | ||||||
| --user 0:0 \ | ||||||
| -v "$DATA_DIR:/data" \ | ||||||
| --entrypoint chown \ | ||||||
| "$IMAGE" \ | ||||||
| -R "${host_uid}:${host_gid}" /data | ||||||
|
|
||||||
| docker_args=( | ||||||
| run | ||||||
| --rm | ||||||
| --name "$CONTAINER_NAME" | ||||||
| --user "${host_uid}:${host_gid}" | ||||||
| --network host | ||||||
| --ulimit nofile=65536:65536 | ||||||
| -v "$DATA_DIR:/data" | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.