Hello scala community,
I'm using ciris-kubernetes to inject credentials and certificates from kubernetes secrets. While it works like a charm, I am wondering what would be the best way to unit test or write an intergration test for this functionality?
More specifically, if one of the primary objectives of configuration as code is to write tests for it, shouldn't that aspect be given more importance?
Here's my case class for holding configuration
import java.io.File
import ciris.Secret
final case class SAMLCertificateConfiguration(publicKey: Secret[File], privateKey: Secret[File], password: Secret[String])
and the configuration loader
object ConfigurationLoader {
private[this] val log = LoggerFactory getLogger this.getClass
private def createConfigurationUsingSecret(namespace: NonEmptyString, secretName: NonEmptyString): IO[SAMLCertificateConfiguration] = {
def getKubernetesSecret: IO[SecretInNamespace[IO]] =
for {
apiClient <- defaultApiClient[IO]
} yield secretInNamespace[IO](namespace, apiClient)
def createCertificateConfigurationWith(secret: SecretInNamespace[IO]): IO[SAMLCertificateConfiguration] =
loadConfig(secret[Secret[File]](secretName.value, "public.crt"), secret[Secret[File]](secretName.value, "private.pfx"), secret[Secret[String]](secretName.value, "password")) {
(publicKey, privateKey, privateKeyPassword) =>
SAMLCertificateConfiguration(publicKey, privateKey, privateKeyPassword)
}.orRaiseThrowable
for {
secret <- getKubernetesSecret
samlCertificateConfiguration <- createCertificateConfigurationWith(secret)
} yield samlCertificateConfiguration
}
def loadSAMLCertificateConfiguration: IO[SAMLCertificateConfiguration] =
loadConfig(
envF[IO, ApplicationEnvironment]("APPLICATION_ENVIRONMENT")
.orElse(propF[IO, ApplicationEnvironment]("application.environment"))
.orNone,
envF[IO, NonEmptyString]("KUBERNETES_NAMESPACE")
.orElse(propF[IO, NonEmptyString]("kubernetes.namespace"))
.orValue("default"),
envF[IO, NonEmptyString]("KUBERNETES_SECRET_NAME")
.orElse(propF[IO, NonEmptyString]("kubernetes.secret.name"))
.orValue("saml-pfx")
) { (environment, namespace, secret) =>
import pp.util.configuration.ApplicationEnvironment._
log.info(s"Environment: $environment, Kubernetes namespace: $namespace, Kubernetes secret's name: $secret")
environment match {
case Some(Test) | Some(Production) =>
createConfigurationUsingSecret(namespace, secret).unsafeRunSync()
case _ =>
createSAMLCertificateConfigurationUsingHOCON
}
}.orRaiseThrowable
}
import enumeratum.{Enum, EnumEntry}
import scala.collection.immutable
sealed abstract class ApplicationEnvironment extends EnumEntry
object ApplicationEnvironment extends Enum[ApplicationEnvironment] {
case object Development extends ApplicationEnvironment
case object Test extends ApplicationEnvironment
case object Production extends ApplicationEnvironment
def values: immutable.IndexedSeq[ApplicationEnvironment] = findValues
}
import ciris._
import lt.dvim.ciris.Hocon._
import com.typesafe.config.ConfigFactory
object DefaultConfigurationLoader {
def createSAMLCertificateConfigurationUsingHOCON: SAMLCertificateConfiguration = {
def defaultConfiguration = {
val fallbackConfiguration = ConfigFactory.parseString("""
|saml.certificate {
| privateKey = "/tmp/certificates/private.pfx"
| publicKey = "/tmp/certificates/public.crt"
| password = ""
|}
""".stripMargin)
ConfigFactory.load().withFallback(fallbackConfiguration)
}
def hocon = hoconAt(defaultConfiguration)("saml.certificate")
loadConfig(hocon[String]("publicKey"), hocon[String]("privateKey"), hocon[String]("password"))((publicKeyPath, privateKeyPath, privateKeyPassword) => {
import java.nio.file.Paths
import ciris.Secret
SAMLCertificateConfiguration(Secret(Paths.get(publicKeyPath).toFile), Secret(Paths.get(privateKeyPath).toFile), Secret(privateKeyPassword))
}).orThrow()
}
}
Should one use io.fabric8.kubernetes.client.server.mock.KubernetesServer as shown over here and implicitly inject kubernetes java client into the configuration loader, so that it can be mocked in tests?