Files
dbbackup/scripts/test_azure_storage.sh
Renz 64f1458e9a feat: Sprint 4 - Azure Blob Storage and Google Cloud Storage support
Implemented full native support for Azure Blob Storage and Google Cloud Storage:

**Azure Blob Storage (internal/cloud/azure.go):**
- Native Azure SDK integration (github.com/Azure/azure-sdk-for-go)
- Block blob upload for large files (>256MB with 100MB blocks)
- Azurite emulator support for local testing
- Production Azure authentication (account name + key)
- SHA-256 integrity verification with metadata
- Streaming uploads with progress tracking

**Google Cloud Storage (internal/cloud/gcs.go):**
- Native GCS SDK integration (cloud.google.com/go/storage)
- Chunked upload for large files (16MB chunks)
- fake-gcs-server emulator support for local testing
- Application Default Credentials support
- Service account JSON key file support
- SHA-256 integrity verification with metadata
- Streaming uploads with progress tracking

**Backend Integration:**
- Updated NewBackend() factory to support azure/azblob and gs/gcs providers
- Added Name() methods to both backends
- Fixed ProgressReader usage across all backends
- Updated Config comments to document Azure/GCS support

**Testing Infrastructure:**
- docker-compose.azurite.yml: Azurite + PostgreSQL + MySQL test environment
- docker-compose.gcs.yml: fake-gcs-server + PostgreSQL + MySQL test environment
- scripts/test_azure_storage.sh: 8 comprehensive Azure integration tests
- scripts/test_gcs_storage.sh: 8 comprehensive GCS integration tests
- Both test scripts validate upload/download/verify/cleanup/restore operations

**Documentation:**
- AZURE.md: Complete guide (600+ lines) covering setup, authentication, usage
- GCS.md: Complete guide (600+ lines) covering setup, authentication, usage
- Updated CLOUD.md with Azure and GCS sections
- Updated internal/config/config.go with Azure/GCS field documentation

**Test Coverage:**
- Large file uploads (300MB for Azure, 200MB for GCS)
- Block/chunked upload verification
- Backup verification with SHA-256 checksums
- Restore from cloud URIs
- Cleanup and retention policies
- Emulator support for both providers

**Dependencies Added:**
- Azure: github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.3
- GCS: cloud.google.com/go/storage v1.57.2
- Plus transitive dependencies (~50+ packages)

**Build:**
- Compiles successfully: 68MB binary
- All imports resolved
- No compilation errors

Sprint 4 closes the multi-cloud gap identified in Sprint 3 evaluation.
Users can now use Azure and GCS URIs that were previously parsed but unsupported.
2025-11-25 21:31:21 +00:00

383 lines
11 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# Azure Blob Storage (Azurite) Testing Script for dbbackup
# Tests backup, restore, verify, and cleanup with Azure emulator
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Test configuration
AZURITE_ENDPOINT="http://localhost:10000"
CONTAINER_NAME="test-backups"
ACCOUNT_NAME="devstoreaccount1"
ACCOUNT_KEY="Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="
# Database connection details (from docker-compose)
POSTGRES_HOST="localhost"
POSTGRES_PORT="5434"
POSTGRES_USER="testuser"
POSTGRES_PASS="testpass"
POSTGRES_DB="testdb"
MYSQL_HOST="localhost"
MYSQL_PORT="3308"
MYSQL_USER="testuser"
MYSQL_PASS="testpass"
MYSQL_DB="testdb"
# Test counters
TESTS_PASSED=0
TESTS_FAILED=0
# Functions
print_header() {
echo -e "\n${BLUE}=== $1 ===${NC}\n"
}
print_success() {
echo -e "${GREEN}$1${NC}"
((TESTS_PASSED++))
}
print_error() {
echo -e "${RED}$1${NC}"
((TESTS_FAILED++))
}
print_info() {
echo -e "${YELLOW} $1${NC}"
}
wait_for_azurite() {
print_info "Waiting for Azurite to be ready..."
for i in {1..30}; do
if curl -f -s "${AZURITE_ENDPOINT}/devstoreaccount1?restype=account&comp=properties" > /dev/null 2>&1; then
print_success "Azurite is ready"
return 0
fi
sleep 1
done
print_error "Azurite failed to start"
return 1
}
# Build dbbackup if needed
build_dbbackup() {
print_header "Building dbbackup"
if [ ! -f "./dbbackup" ]; then
go build -o dbbackup .
print_success "Built dbbackup binary"
else
print_info "Using existing dbbackup binary"
fi
}
# Start services
start_services() {
print_header "Starting Azurite and Database Services"
docker-compose -f docker-compose.azurite.yml up -d
# Wait for services
sleep 5
wait_for_azurite
print_info "Waiting for PostgreSQL..."
sleep 3
print_info "Waiting for MySQL..."
sleep 3
print_success "All services started"
}
# Stop services
stop_services() {
print_header "Stopping Services"
docker-compose -f docker-compose.azurite.yml down
print_success "Services stopped"
}
# Create test data in databases
create_test_data() {
print_header "Creating Test Data"
# PostgreSQL
PGPASSWORD=$POSTGRES_PASS psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U $POSTGRES_USER -d $POSTGRES_DB <<EOF
DROP TABLE IF EXISTS test_table;
CREATE TABLE test_table (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO test_table (name) VALUES ('Azure Test 1'), ('Azure Test 2'), ('Azure Test 3');
EOF
print_success "Created PostgreSQL test data"
# MySQL
mysql -h $MYSQL_HOST -P $MYSQL_PORT -u $MYSQL_USER -p$MYSQL_PASS $MYSQL_DB <<EOF
DROP TABLE IF EXISTS test_table;
CREATE TABLE test_table (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO test_table (name) VALUES ('Azure Test 1'), ('Azure Test 2'), ('Azure Test 3');
EOF
print_success "Created MySQL test data"
}
# Test 1: PostgreSQL backup to Azure
test_postgres_backup() {
print_header "Test 1: PostgreSQL Backup to Azure"
./dbbackup backup postgres \
--host $POSTGRES_HOST \
--port $POSTGRES_PORT \
--user $POSTGRES_USER \
--password $POSTGRES_PASS \
--database $POSTGRES_DB \
--output ./backups/pg_azure_test.sql \
--cloud "azure://$CONTAINER_NAME/postgres/backup1.sql?endpoint=$AZURITE_ENDPOINT&account=$ACCOUNT_NAME&key=$ACCOUNT_KEY"
if [ $? -eq 0 ]; then
print_success "PostgreSQL backup uploaded to Azure"
else
print_error "PostgreSQL backup failed"
return 1
fi
}
# Test 2: MySQL backup to Azure
test_mysql_backup() {
print_header "Test 2: MySQL Backup to Azure"
./dbbackup backup mysql \
--host $MYSQL_HOST \
--port $MYSQL_PORT \
--user $MYSQL_USER \
--password $MYSQL_PASS \
--database $MYSQL_DB \
--output ./backups/mysql_azure_test.sql \
--cloud "azure://$CONTAINER_NAME/mysql/backup1.sql?endpoint=$AZURITE_ENDPOINT&account=$ACCOUNT_NAME&key=$ACCOUNT_KEY"
if [ $? -eq 0 ]; then
print_success "MySQL backup uploaded to Azure"
else
print_error "MySQL backup failed"
return 1
fi
}
# Test 3: List backups in Azure
test_list_backups() {
print_header "Test 3: List Azure Backups"
./dbbackup cloud list "azure://$CONTAINER_NAME/postgres/?endpoint=$AZURITE_ENDPOINT&account=$ACCOUNT_NAME&key=$ACCOUNT_KEY"
if [ $? -eq 0 ]; then
print_success "Listed Azure backups"
else
print_error "Failed to list backups"
return 1
fi
}
# Test 4: Verify backup in Azure
test_verify_backup() {
print_header "Test 4: Verify Azure Backup"
./dbbackup verify "azure://$CONTAINER_NAME/postgres/backup1.sql?endpoint=$AZURITE_ENDPOINT&account=$ACCOUNT_NAME&key=$ACCOUNT_KEY"
if [ $? -eq 0 ]; then
print_success "Backup verification successful"
else
print_error "Backup verification failed"
return 1
fi
}
# Test 5: Restore from Azure
test_restore_from_azure() {
print_header "Test 5: Restore from Azure"
# Drop and recreate database
PGPASSWORD=$POSTGRES_PASS psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U $POSTGRES_USER -d postgres <<EOF
DROP DATABASE IF EXISTS testdb_restored;
CREATE DATABASE testdb_restored;
EOF
./dbbackup restore postgres \
--source "azure://$CONTAINER_NAME/postgres/backup1.sql?endpoint=$AZURITE_ENDPOINT&account=$ACCOUNT_NAME&key=$ACCOUNT_KEY" \
--host $POSTGRES_HOST \
--port $POSTGRES_PORT \
--user $POSTGRES_USER \
--password $POSTGRES_PASS \
--database testdb_restored
if [ $? -eq 0 ]; then
print_success "Restored from Azure backup"
# Verify restored data
COUNT=$(PGPASSWORD=$POSTGRES_PASS psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U $POSTGRES_USER -d testdb_restored -t -c "SELECT COUNT(*) FROM test_table;")
if [ "$COUNT" -eq 3 ]; then
print_success "Restored data verified (3 rows)"
else
print_error "Restored data incorrect (expected 3 rows, got $COUNT)"
fi
else
print_error "Restore from Azure failed"
return 1
fi
}
# Test 6: Large file upload (block blob)
test_large_file_upload() {
print_header "Test 6: Large File Upload (Block Blob)"
# Create a large test file (300MB)
print_info "Creating 300MB test file..."
dd if=/dev/urandom of=./backups/large_test.dat bs=1M count=300 2>/dev/null
print_info "Uploading large file to Azure..."
./dbbackup cloud upload \
./backups/large_test.dat \
"azure://$CONTAINER_NAME/large/large_test.dat?endpoint=$AZURITE_ENDPOINT&account=$ACCOUNT_NAME&key=$ACCOUNT_KEY"
if [ $? -eq 0 ]; then
print_success "Large file uploaded successfully (block blob)"
# Verify file exists and has correct size
print_info "Downloading large file..."
./dbbackup cloud download \
"azure://$CONTAINER_NAME/large/large_test.dat?endpoint=$AZURITE_ENDPOINT&account=$ACCOUNT_NAME&key=$ACCOUNT_KEY" \
./backups/large_test_downloaded.dat
if [ $? -eq 0 ]; then
ORIGINAL_SIZE=$(stat -f%z ./backups/large_test.dat 2>/dev/null || stat -c%s ./backups/large_test.dat)
DOWNLOADED_SIZE=$(stat -f%z ./backups/large_test_downloaded.dat 2>/dev/null || stat -c%s ./backups/large_test_downloaded.dat)
if [ "$ORIGINAL_SIZE" -eq "$DOWNLOADED_SIZE" ]; then
print_success "Downloaded file size matches original ($ORIGINAL_SIZE bytes)"
else
print_error "File size mismatch (original: $ORIGINAL_SIZE, downloaded: $DOWNLOADED_SIZE)"
fi
else
print_error "Large file download failed"
fi
# Cleanup
rm -f ./backups/large_test.dat ./backups/large_test_downloaded.dat
else
print_error "Large file upload failed"
return 1
fi
}
# Test 7: Delete from Azure
test_delete_backup() {
print_header "Test 7: Delete Backup from Azure"
./dbbackup cloud delete "azure://$CONTAINER_NAME/mysql/backup1.sql?endpoint=$AZURITE_ENDPOINT&account=$ACCOUNT_NAME&key=$ACCOUNT_KEY"
if [ $? -eq 0 ]; then
print_success "Deleted backup from Azure"
# Verify deletion
if ! ./dbbackup cloud list "azure://$CONTAINER_NAME/mysql/?endpoint=$AZURITE_ENDPOINT&account=$ACCOUNT_NAME&key=$ACCOUNT_KEY" | grep -q "backup1.sql"; then
print_success "Verified backup was deleted"
else
print_error "Backup still exists after deletion"
fi
else
print_error "Failed to delete backup"
return 1
fi
}
# Test 8: Cleanup old backups
test_cleanup() {
print_header "Test 8: Cleanup Old Backups"
# Create multiple backups with different timestamps
for i in {1..5}; do
./dbbackup backup postgres \
--host $POSTGRES_HOST \
--port $POSTGRES_PORT \
--user $POSTGRES_USER \
--password $POSTGRES_PASS \
--database $POSTGRES_DB \
--output "./backups/pg_cleanup_$i.sql" \
--cloud "azure://$CONTAINER_NAME/cleanup/backup_$i.sql?endpoint=$AZURITE_ENDPOINT&account=$ACCOUNT_NAME&key=$ACCOUNT_KEY"
sleep 1
done
print_success "Created 5 test backups"
# Cleanup, keeping only 2
./dbbackup cleanup "azure://$CONTAINER_NAME/cleanup/?endpoint=$AZURITE_ENDPOINT&account=$ACCOUNT_NAME&key=$ACCOUNT_KEY" --keep 2
if [ $? -eq 0 ]; then
print_success "Cleanup completed"
# Count remaining backups
COUNT=$(./dbbackup cloud list "azure://$CONTAINER_NAME/cleanup/?endpoint=$AZURITE_ENDPOINT&account=$ACCOUNT_NAME&key=$ACCOUNT_KEY" | grep -c "backup_")
if [ "$COUNT" -le 2 ]; then
print_success "Verified cleanup (kept 2 backups)"
else
print_error "Cleanup failed (expected 2 backups, found $COUNT)"
fi
else
print_error "Cleanup failed"
return 1
fi
}
# Main test execution
main() {
print_header "Azure Blob Storage (Azurite) Integration Tests"
# Setup
build_dbbackup
start_services
create_test_data
# Run tests
test_postgres_backup
test_mysql_backup
test_list_backups
test_verify_backup
test_restore_from_azure
test_large_file_upload
test_delete_backup
test_cleanup
# Cleanup
print_header "Cleanup"
rm -rf ./backups
# Summary
print_header "Test Summary"
echo -e "${GREEN}Passed: $TESTS_PASSED${NC}"
echo -e "${RED}Failed: $TESTS_FAILED${NC}"
if [ $TESTS_FAILED -eq 0 ]; then
print_success "All tests passed!"
stop_services
exit 0
else
print_error "Some tests failed"
print_info "Leaving services running for debugging"
print_info "Run 'docker-compose -f docker-compose.azurite.yml down' to stop services"
exit 1
fi
}
# Run main
main