diff --git a/duplicacy/duplicacy_main.go b/duplicacy/duplicacy_main.go index b00686b..0bc931d 100644 --- a/duplicacy/duplicacy_main.go +++ b/duplicacy/duplicacy_main.go @@ -623,12 +623,14 @@ func backupRepository(context *cli.Context) { enableVSS := context.Bool("vss") + dryRun := context.Bool("dry-run") uploadRateLimit := context.Int("limit-rate") storage.SetRateLimits(0, uploadRateLimit) backupManager := duplicacy.CreateBackupManager(preference.SnapshotID, storage, repository, password) duplicacy.SavePassword(*preference, "password", password) backupManager.SetupSnapshotCache(preference.Name) + backupManager.SetDryRun(dryRun) backupManager.Backup(repository, quickMode, threads, context.String("t"), showStatistics, enableVSS) runScript(context, preference.Name, "post") @@ -1202,6 +1204,10 @@ func main() { Usage: "the maximum upload rate (in kilobytes/sec)", Argument: "", }, + cli.BoolFlag { + Name: "dry-run", + Usage: "Dry run for testing, don't backup anything. Use with -stats and -d", + }, cli.BoolFlag { Name: "vss", Usage: "enable the Volume Shadow Copy service (Windows only)", diff --git a/src/duplicacy_backupmanager.go b/src/duplicacy_backupmanager.go index 9090653..9fa9a16 100644 --- a/src/duplicacy_backupmanager.go +++ b/src/duplicacy_backupmanager.go @@ -36,6 +36,10 @@ type BackupManager struct { } +func (manager *BackupManager) SetDryRun(dryRun bool) { + manager.config.dryRun = dryRun +} + // CreateBackupManager creates a backup manager using the specified 'storage'. 'snapshotID' is a unique id to // identify snapshots created for this repository. 'top' is the top directory of the repository. 'password' is the @@ -630,7 +634,9 @@ func (manager *BackupManager) Backup(top string, quickMode bool, threads int, ta } skippedFiles = append(skippedFiles, fileReader.SkippedFiles...) - manager.SnapshotManager.CleanSnapshotCache(localSnapshot, nil) + if !manager.config.dryRun { + manager.SnapshotManager.CleanSnapshotCache(localSnapshot, nil) + } LOG_INFO("BACKUP_END", "Backup for %s at revision %d completed", top, localSnapshot.Revision) RunAtError = func() {} @@ -1048,7 +1054,7 @@ func (manager *BackupManager) UploadSnapshot(chunkMaker *ChunkMaker, uploader *C if _, found := chunkCache[chunkID]; found { completionFunc(chunk, 0, true, chunk.GetLength(), 0) } else { - uploader.StartChunk(chunk, len(sequence)) + uploader.StartChunk(chunk, len(sequence)) } sequence = append(sequence, chunk.GetHash()) }, @@ -1108,8 +1114,9 @@ func (manager *BackupManager) UploadSnapshot(chunkMaker *ChunkMaker, uploader *C } path := fmt.Sprintf("snapshots/%s/%d", manager.snapshotID, snapshot.Revision) - manager.SnapshotManager.UploadFile(path, path, description) - + if !manager.config.dryRun { + manager.SnapshotManager.UploadFile(path, path, description) + } return totalSnapshotChunkSize, numberOfNewSnapshotChunks, totalUploadedSnapshotChunkSize, totalUploadedSnapshotChunkBytes } @@ -1598,7 +1605,9 @@ func (manager *BackupManager) CopySnapshots(otherManager *BackupManager, snapsho chunkUploader.Stop() for _, snapshot := range snapshots { - otherManager.storage.CreateDirectory(0, fmt.Sprintf("snapshots/%s", manager.snapshotID)) + if !manager.config.dryRun { + otherManager.storage.CreateDirectory(0, fmt.Sprintf("snapshots/%s", manager.snapshotID)) + } description, _ := snapshot.MarshalJSON() path := fmt.Sprintf("snapshots/%s/%d", manager.snapshotID, snapshot.Revision) otherManager.SnapshotManager.UploadFile(path, path, description) diff --git a/src/duplicacy_chunkuploader.go b/src/duplicacy_chunkuploader.go index a9f2777..88c409e 100644 --- a/src/duplicacy_chunkuploader.go +++ b/src/duplicacy_chunkuploader.go @@ -134,10 +134,12 @@ func (uploader *ChunkUploader) Upload(threadIndex int, task ChunkUploadTask) boo return false } - err = uploader.storage.UploadFile(threadIndex, chunkPath, chunk.GetBytes()) - if err != nil { - LOG_ERROR("UPLOAD_CHUNK", "Failed to upload the chunk %s: %v", chunkID, err) - return false + if !uploader.config.dryRun { + err = uploader.storage.UploadFile(threadIndex, chunkPath, chunk.GetBytes()) + if err != nil { + LOG_ERROR("UPLOAD_CHUNK", "Failed to upload the chunk %s: %v", chunkID, err) + return false + } } LOG_DEBUG("CHUNK_UPLOAD", "Chunk %s has been uploaded", chunkID) diff --git a/src/duplicacy_config.go b/src/duplicacy_config.go index 8c3fb8e..e92f2f2 100644 --- a/src/duplicacy_config.go +++ b/src/duplicacy_config.go @@ -55,6 +55,7 @@ type Config struct { chunkPool chan *Chunk `json:"-"` numberOfChunks int32 + dryRun bool } // Create an alias to avoid recursive calls on Config.MarshalJSON diff --git a/src/duplicacy_snapshotmanager.go b/src/duplicacy_snapshotmanager.go index 16150ad..8b2df9a 100644 --- a/src/duplicacy_snapshotmanager.go +++ b/src/duplicacy_snapshotmanager.go @@ -2215,9 +2215,11 @@ func (manager *SnapshotManager) DownloadFile(path string, derivationKey string) return nil } - err = manager.snapshotCache.UploadFile(0, path, manager.fileChunk.GetBytes()) - if err != nil { - LOG_WARN("DOWNLOAD_FILE_CACHE", "Failed to add the file %s to the snapshot cache: %v", path, err) + if !manager.config.dryRun { + err = manager.snapshotCache.UploadFile(0, path, manager.fileChunk.GetBytes()) + if err != nil { + LOG_WARN("DOWNLOAD_FILE_CACHE", "Failed to add the file %s to the snapshot cache: %v", path, err) + } } LOG_DEBUG("DOWNLOAD_FILE", "Downloaded file %s", path) @@ -2227,6 +2229,9 @@ func (manager *SnapshotManager) DownloadFile(path string, derivationKey string) // UploadFile uploads a non-chunk file from the storage. func (manager *SnapshotManager) UploadFile(path string, derivationKey string, content []byte) bool { + if manager.config.dryRun { + return true + } manager.fileChunk.Reset(false) manager.fileChunk.Write(content)