app/services/FileMove/CopyProxyFiles.scala (91 lines of code) (raw):

package services.FileMove import com.amazonaws.services.s3.AmazonS3 import com.theguardian.multimedia.archivehunter.common.clientManagers.S3ClientManager import com.theguardian.multimedia.archivehunter.common.{DocId, ProxyLocation} import play.api.Configuration import software.amazon.awssdk.regions.Region import software.amazon.awssdk.services.s3.S3Client import software.amazon.awssdk.services.s3.model.{CopyObjectRequest, DeleteObjectRequest, HeadObjectRequest} import scala.util.{Failure, Success, Try} /** * this actor copies a file to the requested destination bucket and updates the internal state with the new file ID. * when rolling back, it checks that the source file still exists and if so deletes the one it copied earlier. */ class CopyProxyFiles (s3ClientManager:S3ClientManager, config:Configuration) extends GenericMoveActor with DocId { import GenericMoveActor._ import com.theguardian.multimedia.archivehunter.common.cmn_helpers.S3ClientExtensions._ def deleteCopiedProxies(currentState:FileMoveTransientData, proxyList:Seq[ProxyLocation])(implicit s3Client:S3Client) = proxyList.foreach(loc=> { val deletion = for { exists <- s3Client.doesObjectExist(currentState.destProxyBucket, loc.bucketPath) result <- if (exists) { Try { s3Client.deleteObject(DeleteObjectRequest.builder().bucket(currentState.destProxyBucket).key(loc.bucketPath).build()) }.map(Some.apply) } else { Success(None) } } yield result deletion match { case Success(_)=> logger.info(s"Deleted copied proxy at ${currentState.destProxyBucket}:${loc.bucketPath}") case Failure(err)=> logger.error("Could not delete copied proxy: ", err) } }) override def receive: Receive = { case PerformStep(currentState)=> implicit val s3Client = s3ClientManager.getS3Client(region=Some(Region.of(currentState.destRegion)),profileName=config.getOptional[String]("externalData.awsProfile")) currentState.sourceFileProxies match { case Some(proxyList) => try { val updatedProxyList = proxyList.map(loc=> loc.copy(fileId=currentState.destFileId.get, proxyId=makeDocId(currentState.destProxyBucket, loc.bucketPath), bucketName = currentState.destProxyBucket, region = Some(currentState.destRegion) ) ) proxyList.map(proxy => { logger.debug(s"Copying from ${proxy.bucketName}:${proxy.bucketPath} to ${currentState.destProxyBucket}:${proxy.bucketPath}") //does the proxy still exist? val copyResult = for { meta <- Try { s3Client.headObject(HeadObjectRequest.builder().bucket(proxy.bucketName).key(proxy.bucketPath).build()) } copyResult <- Try { logger.info(s"Proxy ${proxy.bucketName}/${proxy.bucketPath} exists with size ${meta.contentLength()} and eTag ${meta.eTag()}") s3Client.copyObject(CopyObjectRequest.builder() .sourceBucket(proxy.bucketName) .sourceKey(proxy.bucketPath) .destinationBucket(currentState.destProxyBucket) .destinationKey(proxy.bucketPath) .build() ) } } yield copyResult copyResult match { case Success(result)=>Some(result) case Failure(err)=> logger.warn(s"Could not find proxy ${proxy.bucketName}/${proxy.bucketPath}: ${err.getMessage}. Assuming that it does not exist any more.") None } }) sender() ! StepSucceeded(currentState.copy(destFileProxy = Some(updatedProxyList))) } catch { case err:Throwable=> logger.error(s"Can't copy proxies ", err) currentState.sourceFileProxies match { case Some(proxyList)=>deleteCopiedProxies(currentState, proxyList) case None=> //don't need to do anything } sender() ! StepFailed(currentState, err.toString) } case None=> sender() ! StepFailed(currentState, "No source proxy list available") } case RollbackStep(currentState)=> implicit val s3Client = s3ClientManager.getS3Client(region=Some(Region.of(currentState.destRegion)),profileName=config.getOptional[String]("externalData.awsProfile")) currentState.sourceFileProxies match { case Some(proxyList) => logger.info(s"Rolling back proxy copy for $proxyList") deleteCopiedProxies(currentState, proxyList) sender() ! StepSucceeded(currentState) case None=> sender() ! StepFailed(currentState, "Can't rollback proxy copy as there were no proxies copied") } } }