Skip to content

Commit

Permalink
Providers (#225)
Browse files Browse the repository at this point in the history
* move credentials providers inside zio.s3.providers object

* sbt build file remove deprecated warning
  • Loading branch information
regis-leray authored May 20, 2021
1 parent 7a27e4c commit 141d4e4
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 190 deletions.
14 changes: 7 additions & 7 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ inThisBuild(
Developer("regis-leray", "Regis Leray", "[email protected]", url("https://github.com/regis-leray"))
),
Test / fork := true,
parallelExecution in Test := false,
(Test / parallelExecution) := false,
pgpPassphrase := sys.env.get("PGP_PASSWORD").map(_.toArray),
pgpPublicRing := file("/tmp/public.asc"),
pgpSecretRing := file("/tmp/secret.asc"),
Expand Down Expand Up @@ -46,18 +46,18 @@ lazy val `zio-s3` = project
lazy val docs = project
.in(file("zio-s3-docs"))
.settings(
skip.in(publish) := true,
skip / publish := true,
moduleName := "zio-s3-docs",
scalacOptions -= "-Yno-imports",
scalacOptions -= "-Xfatal-warnings",
libraryDependencies ++= Seq(
"dev.zio" %% "zio" % zioVersion
),
unidocProjectFilter in (ScalaUnidoc, unidoc) := inProjects(`zio-s3`),
target in (ScalaUnidoc, unidoc) := (baseDirectory in LocalRootProject).value / "website" / "static" / "api",
cleanFiles += (target in (ScalaUnidoc, unidoc)).value,
docusaurusCreateSite := docusaurusCreateSite.dependsOn(unidoc in Compile).value,
docusaurusPublishGhpages := docusaurusPublishGhpages.dependsOn(unidoc in Compile).value
(ScalaUnidoc / unidoc / unidocProjectFilter) := inProjects(`zio-s3`),
ScalaUnidoc / unidoc / target := (LocalRootProject / baseDirectory).value / "website" / "static" / "api",
cleanFiles += (ScalaUnidoc / unidoc / target).value,
docusaurusCreateSite := docusaurusCreateSite.dependsOn(Compile / unidoc).value,
docusaurusPublishGhpages := docusaurusPublishGhpages.dependsOn(Compile / unidoc).value
)
.dependsOn(`zio-s3`)
.enablePlugins(MdocPlugin, DocusaurusPlugin, ScalaUnidocPlugin)
9 changes: 5 additions & 4 deletions docs/quickstart/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,24 +55,25 @@ import zio.blocking._
import software.amazon.awssdk.regions.Region
import software.amazon.awssdk.services.s3.model.S3Exception
import zio.s3._
import zio.s3.providers._

// build S3 Layer from basic credentials
val s3: Layer[S3Exception, S3] =
live(Region.AF_SOUTH_1, AwsBasicCredentials.create("key", "secret"))

// build S3 Layer from System properties or Environment variables
val s3: Layer[S3Exception, S3] =
liveM(Region.AF_SOUTH_1, CredentialsProviders.system <> S3Credentials.env)
liveM(Region.AF_SOUTH_1, system <> env)

// build S3 Layer from Instance profile credentials
val s3: Layer[S3Exception, S3] =
liveM(Region.AF_SOUTH_1, CredentialsProviders.instanceProfile)
liveM(Region.AF_SOUTH_1, instanceProfile)

// build S3 Layer from web identity token credentials with STS. awssdk sts module required to be on classpath
val s3: Layer[S3Exception, S3] = liveM(Region.AF_SOUTH_1, CredentialsProviders.webIdentity)
val s3: Layer[S3Exception, S3] = liveM(Region.AF_SOUTH_1, webIdentity)

// build S3 Layer from default available credentials providers
val s3: Layer[S3Exception, S3] = liveM(Region.AF_SOUTH_1, CredentialsProviders.default)
val s3: Layer[S3Exception, S3] = liveM(Region.AF_SOUTH_1, default)

// use custom logic to fetch aws credentials
val zcredentials: ZIO[R, S3Exception, AwsCredentials] = ??? // specific implementation to fetch credentials
Expand Down
5 changes: 3 additions & 2 deletions src/main/scala/zio/s3/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import software.amazon.awssdk.services.s3.model.S3Exception
import zio.blocking.Blocking
import zio.nio.core.file.{ Path => ZPath }
import zio.s3.S3Bucket.S3BucketListing
import zio.s3.providers.const
import zio.stream.{ Stream, ZStream, ZTransducer }

package object s3 {
Expand Down Expand Up @@ -201,7 +202,7 @@ package object s3 {
}

def live(region: Region, credentials: AwsCredentials, uriEndpoint: Option[URI] = None): Layer[S3Exception, S3] =
liveM(region, CredentialsProviders.const(credentials.accessKeyId, credentials.secretAccessKey), uriEndpoint)
liveM(region, const(credentials.accessKeyId, credentials.secretAccessKey), uriEndpoint)

def liveM[R](
region: Region,
Expand All @@ -220,7 +221,7 @@ package object s3 {
val live: ZLayer[Settings, ConnectionError, S3] = ZLayer.fromFunctionManaged(s =>
Live.connect(
s.get.s3Region,
CredentialsProviders.const(s.get.credentials.accessKeyId, s.get.credentials.secretAccessKey),
const(s.get.credentials.accessKeyId, s.get.credentials.secretAccessKey),
None
)
)
Expand Down
88 changes: 88 additions & 0 deletions src/main/scala/zio/s3/providers.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package zio.s3

import software.amazon.awssdk.auth.credentials.{
AwsBasicCredentials,
AwsCredentialsProvider,
ContainerCredentialsProvider,
DefaultCredentialsProvider,
EnvironmentVariableCredentialsProvider,
InstanceProfileCredentialsProvider,
ProfileCredentialsProvider,
SystemPropertyCredentialsProvider,
WebIdentityTokenFileCredentialsProvider
}
import zio.blocking.{ Blocking, effectBlocking }
import zio.{ IO, Managed, UManaged, ZIO, ZManaged }

object providers {

def const(accessKeyId: String, secretAccessKey: String): UManaged[AwsCredentialsProvider] =
ZManaged.succeedNow[AwsCredentialsProvider](() => AwsBasicCredentials.create(accessKeyId, secretAccessKey))

val system: Managed[InvalidCredentials, SystemPropertyCredentialsProvider] =
ZManaged
.succeed(SystemPropertyCredentialsProvider.create())
.tapM(c => ZIO(c.resolveCredentials()))
.mapError(err => InvalidCredentials(err.getMessage))

val env: Managed[InvalidCredentials, EnvironmentVariableCredentialsProvider] =
ZManaged
.succeed(EnvironmentVariableCredentialsProvider.create())
.tapM(c =>
ZIO
.effect(c.resolveCredentials())
.mapError(err => InvalidCredentials(err.getMessage))
)

val profile: ZManaged[Blocking, InvalidCredentials, ProfileCredentialsProvider] =
ZManaged
.fromAutoCloseable(IO.succeed(ProfileCredentialsProvider.create()))
.tapM(c =>
effectBlocking(c.resolveCredentials())
.mapError(err => InvalidCredentials(err.getMessage))
)

val container: ZManaged[Blocking, InvalidCredentials, ContainerCredentialsProvider] =
ZManaged
.fromAutoCloseable(
IO.succeed(
ContainerCredentialsProvider
.builder()
.build()
)
)
.tapM(c => effectBlocking(c.resolveCredentials()))
.mapError(err => InvalidCredentials(err.getMessage))

val instanceProfile: ZManaged[Blocking, InvalidCredentials, InstanceProfileCredentialsProvider] =
ZManaged
.fromAutoCloseable(
IO.succeed(
InstanceProfileCredentialsProvider
.create()
)
)
.tapM(c => effectBlocking(c.resolveCredentials()))
.mapError(err => InvalidCredentials(err.getMessage))

/**
* Use of this layer requires the awssdk sts module to be on the classpath,
* by default zio-s3 required this library
*/
val webIdentity: ZManaged[Blocking, InvalidCredentials, WebIdentityTokenFileCredentialsProvider] =
ZManaged
.succeed(
WebIdentityTokenFileCredentialsProvider
.create()
)
.tapM(c => effectBlocking(c.resolveCredentials()))
.mapError(err => InvalidCredentials(err.getMessage))

/**
* Use default chaining strategy to fetch credentials
*/
val default: ZManaged[Blocking, InvalidCredentials, AwsCredentialsProvider] =
ZManaged.fromAutoCloseable(
IO.succeed(DefaultCredentialsProvider.create())
)
}
76 changes: 1 addition & 75 deletions src/main/scala/zio/s3/settings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,81 +18,7 @@ package zio.s3

import software.amazon.awssdk.auth.credentials._
import software.amazon.awssdk.regions.Region
import zio.blocking.{ Blocking, effectBlocking }
import zio.{ IO, Managed, UManaged, ZIO, ZManaged }

object CredentialsProviders {

def const(accessKeyId: String, secretAccessKey: String): UManaged[AwsCredentialsProvider] =
ZManaged.succeedNow[AwsCredentialsProvider](() => AwsBasicCredentials.create(accessKeyId, secretAccessKey))

val system: Managed[InvalidCredentials, SystemPropertyCredentialsProvider] =
ZManaged
.succeed(SystemPropertyCredentialsProvider.create())
.tapM(c => ZIO(c.resolveCredentials()))
.mapError(err => InvalidCredentials(err.getMessage))

val env: Managed[InvalidCredentials, EnvironmentVariableCredentialsProvider] =
ZManaged
.succeed(EnvironmentVariableCredentialsProvider.create())
.tapM(c =>
ZIO
.effect(c.resolveCredentials())
.mapError(err => InvalidCredentials(err.getMessage))
)

val profile: ZManaged[Blocking, InvalidCredentials, ProfileCredentialsProvider] =
ZManaged
.fromAutoCloseable(IO.succeed(ProfileCredentialsProvider.create()))
.tapM(c =>
effectBlocking(c.resolveCredentials())
.mapError(err => InvalidCredentials(err.getMessage))
)

val container: ZManaged[Blocking, InvalidCredentials, ContainerCredentialsProvider] =
ZManaged
.fromAutoCloseable(
IO.succeed(
ContainerCredentialsProvider
.builder()
.build()
)
)
.tapM(c => effectBlocking(c.resolveCredentials()))
.mapError(err => InvalidCredentials(err.getMessage))

val instanceProfile: ZManaged[Blocking, InvalidCredentials, InstanceProfileCredentialsProvider] =
ZManaged
.fromAutoCloseable(
IO.succeed(
InstanceProfileCredentialsProvider
.create()
)
)
.tapM(c => effectBlocking(c.resolveCredentials()))
.mapError(err => InvalidCredentials(err.getMessage))

/**
* Use of this layer requires the awssdk sts module to be on the classpath,
* by default zio-s3 required this library
*/
val webIdentity: ZManaged[Blocking, InvalidCredentials, WebIdentityTokenFileCredentialsProvider] =
ZManaged
.succeed(
WebIdentityTokenFileCredentialsProvider
.create()
)
.tapM(c => effectBlocking(c.resolveCredentials()))
.mapError(err => InvalidCredentials(err.getMessage))

/**
* Use default chaining strategy to fetch credentials
*/
val default: ZManaged[Blocking, InvalidCredentials, AwsCredentialsProvider] =
ZManaged.fromAutoCloseable(
IO.succeed(DefaultCredentialsProvider.create())
)
}
import zio.{ IO, ZIO }

final case class S3Region(region: Region)

Expand Down
2 changes: 1 addition & 1 deletion src/test/scala/zio/s3/S3LayerTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ object S3LayerSpec extends DefaultRunnableSpec {
testM("using ZManaged[R, E, A] in liveM compiles") {
assertM(
typeCheck(
"""liveM(Region.CA_CENTRAL_1, CredentialsProviders.default, Some(URI.create("http://localhost:9000")))"""
"""liveM(Region.CA_CENTRAL_1, providers.default, Some(URI.create("http://localhost:9000")))"""
)
)(isRight)
}
Expand Down
93 changes: 93 additions & 0 deletions src/test/scala/zio/s3/S3ProvidersTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package zio.s3

import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
import software.amazon.awssdk.regions.Region
import zio.s3.providers._
import zio.test.Assertion._
import zio.test.TestAspect._
import zio.test._
import zio.{ UIO, ZIO }

object S3ProvidersTest extends DefaultRunnableSpec {

def setProps(props: (String, String)*): UIO[Unit] =
UIO {
props.foreach {
case (k, v) =>
System.setProperty(k, v)
}
}

def unsetProps(keys: String*): UIO[Unit] =
UIO {
keys.foreach(System.clearProperty)
}

def spec =
suite("Providers")(
testM("cred with const") {
assertM(const("k", "v").useNow.map(_.resolveCredentials()))(
equalTo(AwsBasicCredentials.create("k", "v"))
)
},
testM("cred with default fallback const") {
assertM(
(env <> const("k", "v")).useNow.map(_.resolveCredentials())
)(equalTo(AwsBasicCredentials.create("k", "v")))
},
testM("cred in system properties") {
for {
cred <- system.use(p => ZIO(p.resolveCredentials()))
} yield assert(cred)(equalTo(AwsBasicCredentials.create("k1", "s1")))
} @@ flaky @@ around_(
setProps(("aws.accessKeyId", "k1"), ("aws.secretAccessKey", "s1")),
unsetProps("aws.accessKeyId", "aws.secretAccessKey")
),
testM("no cred in system properties") {
for {
failure <- system.useNow.flip.map(_.getMessage)
} yield assert(failure)(isNonEmptyString)
} @@ around_(
unsetProps("aws.accessKeyId", "aws.secretAccessKey"),
UIO.unit
),
testM("no cred in environment properties") {
for {
failure <- env.useNow.flip.map(_.getMessage)
} yield assert(failure)(isNonEmptyString)
},
testM("no cred in profile") {
for {
failure <- profile.useNow.flip.map(_.getMessage)
} yield assert(failure)(isNonEmptyString)
},
testM("no cred in container") {
for {
failure <- container.useNow.flip.map(_.getMessage)
} yield assert(failure)(isNonEmptyString)
},
testM("no cred in instance profile credentials") {
for {
failure <- instanceProfile.useNow.flip.map(_.getMessage)
} yield assert(failure)(isNonEmptyString)
},
testM("no cred in webidentity credentials") {
for {
failure <- webIdentity.useNow.flip.map(_.getMessage)
} yield assert(failure)(isNonEmptyString)
},
testM("settings from invalid creds") {
for {
failure <- settings(
Region.AF_SOUTH_1,
system.useNow.map(_.resolveCredentials())
).build.useNow.flip
} yield assert(failure.getMessage)(isNonEmptyString)
},
testM("no cred when chain all providers") {
for {
failure <- default.use(c => ZIO.effect(c.resolveCredentials())).flip.map(_.getMessage)
} yield assert(failure)(isNonEmptyString)
}
) @@ sequential
}
Loading

0 comments on commit 141d4e4

Please sign in to comment.