Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to zio-config 4.0.0-RC16 #519

Merged
merged 2 commits into from
Aug 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ val scala213Version = "2.13.11"
val scala3Version = "3.3.0"

val zioVersion = "2.0.15"
val zioConfigVersion = "3.0.7"
val zioConfigVersion = "4.0.0-RC16"
val zioLoggingVersion = "2.1.13"
val sttpVersion = "3.8.16"
val zioNioVersion = "2.0.1"
Expand Down Expand Up @@ -63,6 +63,8 @@ lazy val client = Project("zio-k8s-client", file("zio-k8s-client"))
"dev.zio" %% "zio" % zioVersion,
"dev.zio" %% "zio-streams" % zioVersion,
"dev.zio" %% "zio-config" % zioConfigVersion,
"dev.zio" %% "zio-config-derivation" % zioConfigVersion,
"dev.zio" %% "zio-config-magnolia" % zioConfigVersion,
"dev.zio" %% "zio-nio" % zioNioVersion,
"dev.zio" %% "zio-process" % "0.7.2",
"dev.zio" %% "zio-prelude" % zioPreludeVersion,
Expand All @@ -74,9 +76,9 @@ lazy val client = Project("zio-k8s-client", file("zio-k8s-client"))
"io.circe" %% "circe-parser" % "0.14.5",
"io.circe" %% "circe-yaml" % "0.14.2",
"org.bouncycastle" % "bcpkix-jdk18on" % "1.75",
"dev.zio" %% "zio-config-typesafe" % zioConfigVersion % Test,
"dev.zio" %% "zio-test" % zioVersion % Test,
"dev.zio" %% "zio-test-sbt" % zioVersion % Test,
"dev.zio" %% "zio-config-typesafe" % zioConfigVersion % Test,
"com.softwaremill.sttp.client3" %% "slf4j-backend" % sttpVersion % Optional,
"com.softwaremill.sttp.client3" %% "async-http-client-backend-zio" % sttpVersion % Optional
),
Expand Down
30 changes: 18 additions & 12 deletions docs/overview/gettingstarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,20 +127,26 @@ val cluster = config >>> k8sCluster
```scala mdoc:silent:reset
import com.coralogix.zio.k8s.client.config._
import com.coralogix.zio.k8s.client.config.httpclient._
import com.typesafe.config.ConfigFactory
import zio.config.ConfigDescriptor
import com.coralogix.zio.k8s.client.model._
import zio.config._
import zio.config.typesafe._
import zio._

case class Config(k8s: K8sClusterConfig)

// Loading config from HOCON
val configDesc = ConfigDescriptor.nested("k8s")(clusterConfigDescriptor).to[Config]
val config = TypesafeConfig.fromTypesafeConfig[Config](ZIO.attempt(ConfigFactory.load.resolve), configDesc)
// Define a custom configuration class for your application
case class MyConfig(k8s: K8sClusterConfig)
object MyConfig {
val configDescriptor: zio.Config[MyConfig] = clusterConfigDescriptor.map(k8s => MyConfig(k8s))
val live = ZLayer.fromZIO(ZIO.config(configDescriptor))
val k8s = ZLayer.fromFunction { cfg: MyConfig => cfg.k8s }
}

// K8s configuration and client layers
val client = config.project(_.k8s) >>> k8sSttpClient()
val cluster = config.project(_.k8s) >>> k8sCluster
// Set the config provider for the ZIO runtime to load your typesafe config. Typically this is done by overriding
// `ZIOAppDefault.bootstrap` in the Main class of your project.
val bootstrap: ZLayer[Any, Nothing, Unit] =
zio.Runtime.setConfigProvider(TypesafeConfigProvider.fromResourcePath())

//create zio-k8s layers for use in your application
val k8sLayers = MyConfig.live >>> MyConfig.k8s ++ k8sCluster ++ k8sSttpClient()
```

and place the configuration in `application.conf`, for example:
Expand All @@ -167,7 +173,7 @@ k8s {

## Clients

The above created `client` and `cluster` modules can be fed to any of the `zio-k8s` **client modules**
The above created `k8sLayers` can be fed to any of the `zio-k8s` **client modules**
to access _Kubernetes_ resources. This is explained in details in the [resources](resources.md) section.

The following example demonstrates how to gain access to the _Kubernetes pods and config maps_:
Expand All @@ -176,7 +182,7 @@ The following example demonstrates how to gain access to the _Kubernetes pods an
import com.coralogix.zio.k8s.client.v1.configmaps.ConfigMaps
import com.coralogix.zio.k8s.client.v1.pods.Pods

val k8s = (cluster ++ client) >>> (Pods.live ++ ConfigMaps.live)
val k8s = k8sLayers >>> (Pods.live ++ ConfigMaps.live)
```

## Notes
Expand Down
Original file line number Diff line number Diff line change
@@ -1,130 +1,81 @@
package com.coralogix.zio.k8s.client.config

import sttp.model.Uri
import zio.config.ConfigDescriptor._
import zio.config._
import zio.Config.boolean
import zio.nio.file.Path
import zio.{ Chunk, Config }

/** Defines ZIO Config descriptors for all the configuration data types of zio-k8s
*/
trait Descriptors {
private val uri: ConfigDescriptor[Uri] =
string.transformOrFail(
s => Uri.parse(s),
(uri: Uri) => Right(uri.toString)
)

private val path: ConfigDescriptor[Path] =
string.transform(
s => Path(s),
(path: Path) => path.toString()
)

private val keySourceFromFile: ConfigDescriptor[KeySource] =
nested("path")(path).transformOrFailRight(
(s: Path) => KeySource.FromFile(s),
{
case KeySource.FromFile(path) => Right(path)
case _ => Left("Not a KeySource.FromFile")
}
)

private val keySourceFromBase64: ConfigDescriptor[KeySource] =
nested("base64")(string).transformOrFailRight(
(s: String) => KeySource.FromBase64(s),
{
case KeySource.FromBase64(base64) => Right(base64)
case _ => Left("Not a KeySource.FromBase64")
}
)

private val keySourceFromString: ConfigDescriptor[KeySource] =
nested("value")(string).transformOrFailRight(
(s: String) => KeySource.FromString(s),
{
case KeySource.FromString(value) => Right(value)
case _ => Left("Not a KeySource.FromString")
}
)
private val uri: Config[Uri] =
Config.string.mapOrFail { s =>
Uri.parse(s).left.map(err => Config.Error.InvalidData(Chunk.empty, err))
}

private val keySource: ConfigDescriptor[KeySource] =
keySourceFromFile <> keySourceFromString <> keySourceFromBase64
private val path: Config[Path] = Config.string.map(Path(_))

private val serviceAccountToken: ConfigDescriptor[K8sAuthentication] =
nested("serviceAccountToken")(keySource).transformOrFailRight(
(s: KeySource) => K8sAuthentication.ServiceAccountToken(s),
{
case K8sAuthentication.ServiceAccountToken(token) => Right(token)
case _ => Left("Not a K8sAuthentication.ServiceAccountToken")
}
)

private val basicAuth: ConfigDescriptor[K8sAuthentication] =
nested("basicAuth")(
string("username") zip string("password")
).transformOrFailRight(
{ case (username, password) => K8sAuthentication.BasicAuth(username, password) },
{
case K8sAuthentication.BasicAuth(username, password) => Right((username, password))
case _ => Left("Not a K8sAuthentication.BasicAuth")
}
)

private val clientCertificates: ConfigDescriptor[K8sAuthentication] =
nested("clientCertificates")(
nested("certificate")(keySource) zip nested("key")(keySource) zip string(
"password"
).optional
).transformOrFailRight(
{ case (certificate, key, password) =>
K8sAuthentication.ClientCertificates(certificate, key, password)
},
{
case K8sAuthentication.ClientCertificates(certificate, key, password) =>
Right((certificate, key, password))
case _ => Left("Not a K8sAuthentication.ClientCertificates")
}
)

private val k8sAuthentication: ConfigDescriptor[K8sAuthentication] =
serviceAccountToken <> basicAuth <> clientCertificates

private val insecureServerCertificate: ConfigDescriptor[K8sServerCertificate] =
boolean("insecure").transformOrFail(
{
case true => Right(K8sServerCertificate.Insecure)
case false => Left("Use insecure: true or secure")
},
{
case K8sServerCertificate.Insecure => Right(true)
case _ => Left("Not a K8sServerCertificate.Insecure")
private val keySourceFromFile: Config[KeySource] =
path.nested("path").map(p => KeySource.FromFile(p))

private val keySourceFromBase64: Config[KeySource] =
Config.string.nested("base64").map(s => KeySource.FromBase64(s))

private val keySourceFromString: Config[KeySource] =
Config.string.nested("value").map(s => KeySource.FromString(s))

private val keySource: Config[KeySource] =
keySourceFromFile orElse keySourceFromString orElse keySourceFromBase64

private val serviceAccountToken: Config[K8sAuthentication] =
keySource.nested("serviceAccountToken").map(ks => K8sAuthentication.ServiceAccountToken(ks))

private val basicAuth: Config[K8sAuthentication] =
(Config.string("username") zip Config.string("password")).nested("basicAuth") map {
case (username, password) => K8sAuthentication.BasicAuth(username, password)
}

private val clientCertificates: Config[K8sAuthentication] =
(keySource.nested("certificate") zip
keySource.nested("key") zip
Config.string("password").optional)
.nested("clientCertificates")
.map { case (cert, key, password) =>
K8sAuthentication.ClientCertificates(cert, key, password)
}
)

private val secureServerCertificate: ConfigDescriptor[K8sServerCertificate] =
nested("secure")(
nested("certificate")(keySource) zip boolean("disableHostnameVerification")
).transformOrFailRight(
{ case (cert, disableHostnameVerification) =>
private val k8sAuthentication: Config[K8sAuthentication] =
serviceAccountToken orElse basicAuth orElse clientCertificates

private val insecureServerCertificate: Config[K8sServerCertificate] =
boolean("insecure").mapOrFail {
case true => Right(K8sServerCertificate.Insecure)
case false => Left(Config.Error.InvalidData(Chunk.empty, "Use insecure: true or secure"))
}

private val secureServerCertificate: Config[K8sServerCertificate] =
(keySource.nested("certificate") zip Config.boolean("disableHostnameVerification"))
.nested("secure")
.map { case (cert, disableHostnameVerification) =>
K8sServerCertificate.Secure(cert, disableHostnameVerification)
},
{
case K8sServerCertificate.Secure(cert, disableHostnameVerification) =>
Right((cert, disableHostnameVerification))
case K8sServerCertificate.Insecure => Left("Not a K8sServerCertificate.Secure")
}
)

private val serverCertificate: ConfigDescriptor[K8sServerCertificate] =
insecureServerCertificate <> secureServerCertificate
private val serverCertificate: Config[K8sServerCertificate] =
insecureServerCertificate orElse secureServerCertificate

private val clientConfig: ConfigDescriptor[K8sClientConfig] =
(boolean("debug") zip serverCertificate).to[K8sClientConfig]
private val clientConfig: Config[K8sClientConfig] =
(boolean("debug") zip serverCertificate).map { case (debug, cert) =>
K8sClientConfig(debug, cert)
}

/** ZIO Config descriptor for [[K8sClusterConfig]]
*/
val clusterConfigDescriptor: ConfigDescriptor[K8sClusterConfig] =
(nested("host")(uri) zip nested("authentication")(k8sAuthentication) zip nested("client")(
clientConfig
)).to[K8sClusterConfig]
val clusterConfigDescriptor: Config[K8sClusterConfig] =
(uri.nested("host") zip
k8sAuthentication.nested("authentication") zip
clientConfig.nested("client"))
.map { case (uri, auth, client) =>
K8sClusterConfig(uri, auth, client)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import io.circe.yaml.parser._

import zio.nio.file.Path
import zio.nio.file.Files
import zio.{ IO, Task, ZIO }
import zio.{ IO, ZIO }

import java.nio.charset.StandardCharsets

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import sttp.capabilities.zio.ZioStreams
import sttp.client3.SttpBackend
import sttp.client3.asynchttpclient.zio._
import sttp.client3.logging.LoggingBackend
import sttp.client3.logging.slf4j.{ Slf4jLogger, Slf4jLoggingBackend }
import sttp.client3.logging.slf4j.Slf4jLogger
import zio._

/** HTTP client implementation based on the async-http-client-zio backend
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import sttp.capabilities.zio.ZioStreams
import sttp.client3.SttpBackend
import sttp.client3.httpclient.zio._
import sttp.client3.logging.LoggingBackend
import sttp.client3.logging.slf4j.{ Slf4jLogger, Slf4jLoggingBackend }
import sttp.client3.logging.slf4j.Slf4jLogger
import zio._

import java.net.http.HttpClient
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import io.circe.generic.semiauto.deriveDecoder
import io.circe.{ parser, Decoder }
import sttp.client3.UriContext
import sttp.model.Uri
import zio.config._
import zio.nio.file.Path
import zio.process.Command
import zio.{ Layer, RIO, Scope, System, Task, ZIO, ZLayer }
Expand Down Expand Up @@ -180,16 +179,15 @@ package object config extends Descriptors {
}
}

/** Layer producing a [[com.coralogix.zio.k8s.client.model.K8sCluster]] from a provided
* K8sClusterConfig
/** Layer producing a [[com.coralogix.zio.k8s.client.model.K8sCluster]] from a K8sClusterConfig
*
* This can be used to either set up from a configuration source with zio-config or provide the
* hostname and token programmatically for the Kubernetes client.
*/
val k8sCluster: ZLayer[K8sClusterConfig, Throwable, K8sCluster] =
ZLayer.scoped {
for {
config <- getConfig[K8sClusterConfig]
config <- ZIO.service[K8sClusterConfig]
result <- config.authentication match {
case K8sAuthentication.ServiceAccountToken(tokenSource) =>
loadKeyString(tokenSource).flatMap { token =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import com.coralogix.zio.k8s.client.config.K8sAuthentication.ServiceAccountToken
import com.coralogix.zio.k8s.client.config.KeySource.FromString
import io.circe.yaml.parser.parse
import sttp.client3._
import zio.config._
import zio.config.typesafe.TypesafeConfig
import zio.config.typesafe.TypesafeConfigProvider
import zio.nio.file.{ Files, Path }
import zio.test.{ assertCompletes, assertZIO, Assertion, Spec, TestEnvironment, ZIOSpecDefault }
import zio.{ Chunk, ZIO }
Expand Down Expand Up @@ -81,7 +80,7 @@ object ConfigSpec extends ZIOSpecDefault {
val clientConfigSpec: Spec[zio.test.TestEnvironment, Any] = test("load client config") {
// Loading config from HOCON
val loadConfig = ZIO.scoped {
TypesafeConfig.fromHoconString[Config](example1, configDesc).build.map(_.get)
TypesafeConfigProvider.fromHoconString(example1).load(configDesc)
}

assertZIO(loadConfig)(
Expand Down Expand Up @@ -144,8 +143,7 @@ object ConfigSpec extends ZIOSpecDefault {

case class Config(k8s: K8sClusterConfig)

val configDesc: ConfigDescriptor[Config] =
ConfigDescriptor.nested("k8s")(clusterConfigDescriptor).to[Config]
val configDesc: zio.Config[Config] = clusterConfigDescriptor.nested("k8s").map(Config)

val example1: String =
"""k8s {
Expand Down
Loading