func removeSnapshots(snapshots []common.Snapshot) { for _, snapshot := range snapshots { if err := os.Remove(snapshot.File); err != nil { log.Error("Error removing snapshot '%s': %s", snapshot.File, err) } } }
func removeVersions(versionsToRemove []common.Version) (ok bool) { objectBatches := makeObjectBatches(versionsToRemove) common.ConfigureAws(common.Cfg.BackupSet.SlaveRegion) s3Client := s3.New(nil) for batch, objects := range objectBatches { params := &s3.DeleteObjectsInput{ Bucket: aws.String(common.Cfg.BackupSet.SlaveBucket), Delete: &s3.Delete{ Objects: objects, Quiet: aws.Bool(true), }, } log.Debug("params[%d] = %+v", batch, *params) resp, err := s3Client.DeleteObjects(params) if err != nil { awsErr, ok := err.(awserr.Error) if !ok { // According to aws-sdk-go documentation: // This case should never be hit, The SDK should alwsy return an // error which satisfies the awserr.Error interface. log.Error(err.Error()) return false } // Generic AWS Error with Code, Message, and original error (if any) log.Error(awsErr.Code(), awsErr.Message(), awsErr.OrigErr()) if reqErr, ok := err.(awserr.RequestFailure); ok { // A service error occurred log.Error(reqErr.Code(), reqErr.Message(), reqErr.StatusCode(), reqErr.RequestID()) } if awsErr.Code() != "NoSuchVersion" { return false } } log.Debug("response[%d] = %+v", batch, *resp) } return true }
func snapshotWorker(wid int, path string) { log.Info("[%d] Explore path '%s'.", wid, path) s3Client := s3.New(nil) params := &s3.ListObjectVersionsInput{ Bucket: aws.String(common.Cfg.BackupSet.SlaveBucket), Delimiter: aws.String("/"), // EncodingType: aws.String("EncodingType"), // KeyMarker: aws.String("KeyMarker"), MaxKeys: aws.Int64(common.SnapshotBatchSize), Prefix: aws.String(path), // VersionIdMarker: aws.String("VersionIdMarker"), } var discoveredVersions []common.Version buffer := make([]common.Version, common.SnapshotBatchSize) for batch := 1; ; batch++ { log.Debug("[%d] Request batch %d for path '%s'", wid, batch, path) resp, err := s3Client.ListObjectVersions(params) if err != nil { if awsErr, ok := err.(awserr.Error); ok { if reqErr, ok := err.(awserr.RequestFailure); ok { // A service error occurred log.Error(awsErr.Code(), awsErr.Message(), awsErr.OrigErr()) log.Fatal(reqErr.Code(), reqErr.Message(), reqErr.StatusCode(), reqErr.RequestID()) } else { log.Fatal(awsErr.Code(), awsErr.Message(), awsErr.OrigErr()) } } else { // This case should never be hit, The SDK should alway return an // error which satisfies the awserr.Error interface. log.Fatal(err.Error()) } } for _, cp := range resp.CommonPrefixes { discoveredPath := *cp.Prefix log.Info("[%d] Discover path '%s'.", wid, discoveredPath) workRequests <- discoveredPath } index := 0 for _, v := range resp.Versions { if v.IsLatest == nil { log.Fatal("[%d] IsLatest is nil", wid) } if v.IsLatest != nil && *v.IsLatest == true { if v.Key == nil { log.Fatal("[%d] Key is nil", wid) } if v.LastModified == nil { log.Fatal("[%d] LastModified is nil", wid) } if v.Size == nil { log.Fatal("[%d] Size is nil", wid) } if v.VersionId == nil { log.Fatal("[%d] VersionId is nil", wid) } version := common.Version{ Key: *v.Key, LastModified: *v.LastModified, Size: *v.Size, VersionId: *v.VersionId, } log.Debug("[%d] Discover latest version: %s", wid, version) buffer[index] = version index++ } else { log.Debug("[%d] Discover noncurrent latest version for key '%s'.", wid, *v.Key) } } discoveredVersions = append(discoveredVersions, buffer[0:index]...) if !*resp.IsTruncated { break } log.Info("[%d] Continue exploring path '%s'.", wid, path) if resp.NextVersionIdMarker != nil { log.Debug("[%d] NextVersionIdMarker = %+v", wid, resp.NextVersionIdMarker) } else { log.Debug("[%d] NextVersionIdMarker = nil", wid) } if resp.NextKeyMarker != nil { log.Debug("[%d] NextKeyMarker = %+v", wid, resp.NextKeyMarker) } else { log.Debug("[%d] NextKeyMarker = nil", wid) } params.VersionIdMarker = resp.NextVersionIdMarker params.KeyMarker = resp.NextKeyMarker } log.Info("[%d] Registering versions for path '%s'.", wid, path) versionsFunnel <- discoveredVersions log.Info("[%d] Done exploring path '%s'.", wid, path) doneSnapshotWorkers <- wid }
func downloadWorker() { s3Client := s3.New(nil) for work := range downloadWorkQueue { log.Debug("[%d] Download version, retry %d: %s", work.Wid, work.Retry, work.Version) getParams := &s3.GetObjectInput{ Bucket: aws.String(common.Cfg.BackupSet.SlaveBucket), // Required Key: aws.String(work.Version.Key), // Required // IfMatch: aws.String("IfMatch"), // IfModifiedSince: aws.Time(time.Now()), // IfNoneMatch: aws.String("IfNoneMatch"), // IfUnmodifiedSince: aws.Time(time.Now()), // Range: aws.String("Range"), // RequestPayer: aws.String("RequestPayer"), // ResponseCacheControl: aws.String("ResponseCacheControl"), // ResponseContentDisposition: aws.String("ResponseContentDisposition"), // ResponseContentEncoding: aws.String("ResponseContentEncoding"), // ResponseContentLanguage: aws.String("ResponseContentLanguage"), // ResponseContentType: aws.String("ResponseContentType"), // ResponseExpires: aws.Time(time.Now()), // SSECustomerAlgorithm: aws.String("SSECustomerAlgorithm"), // SSECustomerKey: aws.String("SSECustomerKey"), // SSECustomerKeyMD5: aws.String("SSECustomerKeyMD5"), VersionId: aws.String(work.Version.VersionId), } getResp, getErr := s3Client.GetObject(getParams) if getErr != nil { if awsErr, ok := getErr.(awserr.Error); ok { log.Error("[%d] Error code '%s', message '%s', origin '%s'", work.Wid, awsErr.Code(), awsErr.Message(), awsErr.OrigErr()) if reqErr, ok := getErr.(awserr.RequestFailure); ok { log.Error("[%d] Service error code '%s', message '%s', status code '%d', request id '%s'", work.Wid, reqErr.Code(), reqErr.Message(), reqErr.StatusCode(), reqErr.RequestID()) } } else { // This case should never be hit, The SDK should alwsy return an // error which satisfies the awserr.Error interface. log.Error("[%d] Non AWS error: %s", work.Wid, getErr.Error()) } work.Retry++ if work.Retry == common.MaxRetries { log.Fatal("[%d] Error downloading version, retry %d: %s", work.Wid, work.Retry, work.Version) } log.Error("[%d] Error downloading version, retry %d: %s", work.Wid, work.Retry, work.Version) downloadWorkQueue <- work continue } log.Debug("[%d] Read response: %s", work.Wid, work.Version) bytes, readErr := ioutil.ReadAll(getResp.Body) getResp.Body.Close() if readErr != nil { work.Retry++ if work.Retry == common.MaxRetries { log.Fatal("[%d] Could not read version %s, retry %d: %s", work.Wid, work.Version, work.Retry, readErr) } log.Error("[%d] Could not read version %s, retry %d: %s", work.Wid, work.Version, work.Retry, readErr) downloadWorkQueue <- work continue } log.Debug("[%d] Downloaded version: %s", work.Wid, work.Version) uploadWorkQueue <- UploadWork{Wid: work.Wid, Version: work.Version, Bytes: bytes, Retry: 0} } }
func uploadWorker() { s3Client := s3.New(nil) for work := range uploadWorkQueue { log.Debug("[%d] Upload version, retry %d: %s", work.Wid, work.Retry, work.Version) putParams := &s3.PutObjectInput{ Bucket: aws.String(common.Cfg.BackupSet.MasterBucket), // Required Key: aws.String(work.Version.Key), // Required // ACL: aws.String("ObjectCannedACL"), Body: bytes.NewReader(work.Bytes), // CacheControl: aws.String("CacheControl"), // ContentDisposition: aws.String("ContentDisposition"), // ContentEncoding: aws.String("ContentEncoding"), // ContentLanguage: aws.String("ContentLanguage"), // ContentLength: aws.Long(1), // ContentType: aws.String("ContentType"), // Expires: aws.Time(time.Now()), // GrantFullControl: aws.String("GrantFullControl"), // GrantRead: aws.String("GrantRead"), // GrantReadACP: aws.String("GrantReadACP"), // GrantWriteACP: aws.String("GrantWriteACP"), // Metadata: map[string]*string{ // "Key": aws.String("MetadataValue"), // Required // // More values... // }, // RequestPayer: aws.String("RequestPayer"), // SSECustomerAlgorithm: aws.String("SSECustomerAlgorithm"), // SSECustomerKey: aws.String("SSECustomerKey"), // SSECustomerKeyMD5: aws.String("SSECustomerKeyMD5"), // SSEKMSKeyID: aws.String("SSEKMSKeyId"), // ServerSideEncryption: aws.String("ServerSideEncryption"), // StorageClass: aws.String("StorageClass"), // WebsiteRedirectLocation: aws.String("WebsiteRedirectLocation"), } _, putErr := s3Client.PutObject(putParams) if putErr != nil { if awsErr, ok := putErr.(awserr.Error); ok { log.Error("[%d] Error code '%s', message '%s', origin '%s'", work.Wid, awsErr.Code(), awsErr.Message(), awsErr.OrigErr()) if reqErr, ok := putErr.(awserr.RequestFailure); ok { log.Error("[%d] Service error code '%s', message '%s', status code '%d', request id '%s'", work.Wid, reqErr.Code(), reqErr.Message(), reqErr.StatusCode(), reqErr.RequestID()) } } else { // This case should never be hit, The SDK should alwsy return an // error which satisfies the awserr.Error interface. log.Error("[%d] Non AWS error: %s", work.Wid, putErr.Error()) } work.Retry++ if work.Retry == common.MaxRetries { log.Fatal("[%d] Error uploading version, retry %d: %s", work.Wid, work.Retry, work.Version) } log.Error("[%d] Error uploading version, retry %d: %s", work.Wid, work.Retry, work.Version) uploadWorkQueue <- work continue } log.Info("[%d] Restored version: %s", work.Wid, work.Version) readyRestoreWorkers <- work.Wid } }