11package com .avast .clients .storage .gcs
22
33import better .files .File
4- import cats .data .EitherT
54import cats .effect .implicits .catsEffectSyntaxBracket
65import cats .effect .{Blocker , ContextShift , Resource , Sync }
76import cats .syntax .all ._
@@ -10,7 +9,7 @@ import com.avast.clients.storage.{ConfigurationException, GetResult, HeadResult,
109import com .avast .scala .hashes .Sha256
1110import com .google .auth .oauth2 .ServiceAccountCredentials
1211import com .google .cloud .ServiceOptions
13- import com .google .cloud .storage .{Blob , Bucket , Storage , StorageOptions , StorageException => GcStorageException }
12+ import com .google .cloud .storage .{Blob , BlobId , Storage , StorageOptions , StorageException => GcStorageException }
1413import com .typesafe .config .{Config , ConfigFactory }
1514import com .typesafe .scalalogging .StrictLogging
1615import pureconfig .error .ConfigReaderException
@@ -23,7 +22,9 @@ import java.nio.charset.StandardCharsets
2322import java .nio .file .StandardOpenOption
2423import java .security .{DigestOutputStream , MessageDigest }
2524
26- class GcsStorageBackend [F [_]: Sync : ContextShift ](bucket : Bucket )(blocker : Blocker ) extends StorageBackend [F ] with StrictLogging {
25+ class GcsStorageBackend [F [_]: Sync : ContextShift ](storageClient : Storage , bucketName : String )(blocker : Blocker )
26+ extends StorageBackend [F ]
27+ with StrictLogging {
2728 private val FileStreamOpenOptions = Seq (StandardOpenOption .WRITE , StandardOpenOption .CREATE , StandardOpenOption .TRUNCATE_EXISTING )
2829
2930 override def head (sha256 : Sha256 ): F [Either [StorageException , HeadResult ]] = {
@@ -74,7 +75,7 @@ class GcsStorageBackend[F[_]: Sync: ContextShift](bucket: Bucket)(blocker: Block
7475 for {
7576 objectPath <- Sync [F ].delay(composeBlobPath(sha256))
7677 result <- blocker.delay {
77- Option (bucket .get(objectPath))
78+ Option (storageClient .get(BlobId .of(bucketName, objectPath) ))
7879 }
7980 } yield result
8081 }
@@ -117,34 +118,28 @@ object GcsStorageBackend {
117118 private val DefaultConfig = ConfigFactory .defaultReference().getConfig(" gcsBackendDefaults" )
118119
119120 def fromConfig [F [_]: Sync : ContextShift ](config : Config ,
120- blocker : Blocker ): EitherT [F , ConfigurationException , Resource [F , GcsStorageBackend [F ]]] = {
121-
122- def composeConfig : EitherT [F , ConfigurationException , GcsBackendConfiguration ] = EitherT {
123- Sync [F ].delay {
124- pureconfig.ConfigSource
125- .fromConfig(config.withFallback(DefaultConfig ))
126- .load[GcsBackendConfiguration ]
127- .leftMap { failures =>
128- ConfigurationException (" Could not load config" , new ConfigReaderException [GcsBackendConfiguration ](failures))
129- }
130- }
121+ blocker : Blocker ): Either [ConfigurationException , Resource [F , GcsStorageBackend [F ]]] = {
122+
123+ def composeConfig : Either [ConfigurationException , GcsBackendConfiguration ] = {
124+ pureconfig.ConfigSource
125+ .fromConfig(config.withFallback(DefaultConfig ))
126+ .load[GcsBackendConfiguration ]
127+ .leftMap { failures =>
128+ ConfigurationException (" Could not load config" , new ConfigReaderException [GcsBackendConfiguration ](failures))
129+ }
131130 }
132131
133- {
134- for {
135- conf <- composeConfig
136- storageClient <- prepareStorageClient(conf, blocker)
137- bucket <- getBucket(conf, storageClient, blocker)
138- } yield (storageClient, bucket)
139- }.map {
140- case (storage, bucket) =>
141- Resource
142- .fromAutoCloseable {
143- Sync [F ].pure(storage)
144- }
145- .map { _ =>
146- new GcsStorageBackend [F ](bucket)(blocker)
147- }
132+ for {
133+ conf <- composeConfig
134+ storageClient <- prepareStorageClient(conf, blocker)
135+ } yield {
136+ Resource
137+ .fromAutoCloseable {
138+ Sync [F ].pure(storageClient)
139+ }
140+ .map { storageClient =>
141+ new GcsStorageBackend [F ](storageClient, conf.bucketName)(blocker)
142+ }
148143 }
149144 }
150145
@@ -154,66 +149,36 @@ object GcsStorageBackend {
154149 }
155150
156151 def prepareStorageClient [F [_]: Sync : ContextShift ](conf : GcsBackendConfiguration ,
157- blocker : Blocker ): EitherT [F , ConfigurationException , Storage ] = {
158- EitherT {
159- blocker.delay {
160- Either
161- .catchNonFatal {
162- val credentialsFileContent = conf.credentialsFile
163- .map { credentialsFilePath =>
164- new FileInputStream (credentialsFilePath)
165- }
166- .orElse {
167- sys.env.get(" GOOGLE_APPLICATION_CREDENTIALS_RAW" ).map { credentialFileRaw =>
168- new ByteArrayInputStream (credentialFileRaw.getBytes(StandardCharsets .UTF_8 ))
169- }
170- }
171-
172- val builder = credentialsFileContent match {
173- case Some (inputStream) =>
174- StorageOptions .newBuilder
175- .setCredentials(ServiceAccountCredentials .fromStream(inputStream))
176- case None =>
177- StorageOptions .getDefaultInstance.toBuilder
178- }
179-
180- builder
181- .setProjectId(conf.projectId)
182- .setRetrySettings(ServiceOptions .getNoRetrySettings)
183-
184- builder.build.getService
152+ blocker : Blocker ): Either [ConfigurationException , Storage ] = {
153+ Either
154+ .catchNonFatal {
155+ val credentialsFileContent = conf.credentialsFile
156+ .map { credentialsFilePath =>
157+ new FileInputStream (credentialsFilePath)
185158 }
186- .leftMap { e =>
187- ConfigurationException (" Could not create GCS client" , e)
188- }
189- }
190- }
191- }
192-
193- def getBucket [F [_]: Sync : ContextShift ](conf : GcsBackendConfiguration ,
194- storageClient : Storage ,
195- blocker : Blocker ): EitherT [F , ConfigurationException , Bucket ] = {
196- EitherT {
197- blocker
198- .delay {
199- Either
200- .catchNonFatal {
201- Option (storageClient.get(conf.bucketName, Storage .BucketGetOption .userProject(conf.projectId)))
159+ .orElse {
160+ sys.env.get(" GOOGLE_APPLICATION_CREDENTIALS_RAW" ).map { credentialFileRaw =>
161+ new ByteArrayInputStream (credentialFileRaw.getBytes(StandardCharsets .UTF_8 ))
202162 }
203- }
204- .map {
205- _.leftMap { e =>
206- ConfigurationException (s " Attempt to get bucket ${conf.bucketName} failed " , e)
207- }.flatMap {
208- case Some (bucket) =>
209- Right (bucket)
210- case None =>
211- Left {
212- ConfigurationException (s " Bucket ${conf.bucketName} does not exist " )
213- }
214163 }
164+
165+ val builder = credentialsFileContent match {
166+ case Some (inputStream) =>
167+ StorageOptions .newBuilder
168+ .setCredentials(ServiceAccountCredentials .fromStream(inputStream))
169+ case None =>
170+ StorageOptions .getDefaultInstance.toBuilder
215171 }
216- }
172+
173+ builder
174+ .setProjectId(conf.projectId)
175+ .setRetrySettings(ServiceOptions .getNoRetrySettings)
176+
177+ builder.build.getService
178+ }
179+ .leftMap { e =>
180+ ConfigurationException (" Could not create GCS client" , e)
181+ }
217182 }
218183}
219184
0 commit comments