From 970a738ece9cded0da2cb505afe8a8ae9f748b91 Mon Sep 17 00:00:00 2001 From: Tim Cuthbertson Date: Sat, 25 Nov 2023 10:24:04 +1100 Subject: [PATCH] Bugfix: Refresh before token expires, not after (#254) Co-authored-by: Iurii Malchenko --- build.sc | 2 +- .../client/util/cache/AuthorizationCache.scala | 5 +++-- .../client/cache/AuthorizationCacheTest.scala | 16 ++++++++++++++++ project/Dependencies.sc | 2 ++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/build.sc b/build.sc index 2ec4e96f..c87ead84 100644 --- a/build.sc +++ b/build.sc @@ -35,7 +35,7 @@ class KubernetesClientModule(val crossScalaVersion: String) ) override def ivyDeps = - super.ivyDeps() ++ http4s ++ circe ++ circeYaml ++ bouncycastle ++ collectionCompat ++ logging + super.ivyDeps() ++ http4s ++ circe ++ circeYaml ++ bouncycastle ++ collectionCompat ++ logging ++ java8compat override def scalacPluginIvyDeps = super.scalacPluginIvyDeps() ++ (if (isScala3(scalaVersion())) Agg.empty else Agg(ivy"org.typelevel:::kind-projector:0.13.2")) diff --git a/kubernetes-client/src/com/goyeau/kubernetes/client/util/cache/AuthorizationCache.scala b/kubernetes-client/src/com/goyeau/kubernetes/client/util/cache/AuthorizationCache.scala index dc560606..c69c4f01 100644 --- a/kubernetes-client/src/com/goyeau/kubernetes/client/util/cache/AuthorizationCache.scala +++ b/kubernetes-client/src/com/goyeau/kubernetes/client/util/cache/AuthorizationCache.scala @@ -5,6 +5,7 @@ import cats.effect.Async import cats.syntax.all.* import org.http4s.headers.Authorization import org.typelevel.log4cats.Logger +import scala.compat.java8.DurationConverters.* import scala.concurrent.duration.* @@ -38,8 +39,8 @@ object AuthorizationCache { case Some(cached) => F.realTimeInstant .flatMap { now => - val shouldRenew = - cached.expirationTimestamp.exists(_.isBefore(now.minusSeconds(refreshBeforeExpiration.toSeconds))) + val minExpiry = now.plus(refreshBeforeExpiration.toJava) + val shouldRenew = cached.expirationTimestamp.exists(_.isBefore(minExpiry)) if (shouldRenew) getAndCacheToken.flatMap { case Some(token) => token.pure[F] diff --git a/kubernetes-client/test/src/com/goyeau/kubernetes/client/cache/AuthorizationCacheTest.scala b/kubernetes-client/test/src/com/goyeau/kubernetes/client/cache/AuthorizationCacheTest.scala index f9d2f668..24941c7b 100644 --- a/kubernetes-client/test/src/com/goyeau/kubernetes/client/cache/AuthorizationCacheTest.scala +++ b/kubernetes-client/test/src/com/goyeau/kubernetes/client/cache/AuthorizationCacheTest.scala @@ -10,6 +10,7 @@ import org.http4s.{AuthScheme, Credentials} import org.http4s.headers.Authorization import cats.effect.unsafe.implicits.global import java.time.Instant +import scala.concurrent.duration.* class AuthorizationCacheTest extends FunSuite { @@ -73,6 +74,21 @@ class AuthorizationCacheTest extends FunSuite { io.unsafeRunSync() } + test(s"retrieve the token when it's going to expire within refreshBeforeExpiration") { + val io = for { + counter <- IO.ref(1) + auth = mkAuthorization( + expirationTimestamp = IO.realTimeInstant.map(_.plusSeconds(40).some), + token = counter.getAndUpdate(_ + 1).map(i => s"test-token-$i") + ) + cache <- AuthorizationCache[IO](retrieve = auth, refreshBeforeExpiration = 1.minute) + obtained <- (1 to 5).toList.traverse(i => cache.get.product(i.pure)) + } yield obtained.foreach { case (obtained, i) => + assertEquals(obtained, Authorization(Credentials.Token(AuthScheme.Bearer, s"test-token-$i"))) + } + io.unsafeRunSync() + } + test(s"fail if cannot retrieve the token when it's expired") { val io = for { counter <- IO.ref(1) diff --git a/project/Dependencies.sc b/project/Dependencies.sc index 56185957..0074bb29 100644 --- a/project/Dependencies.sc +++ b/project/Dependencies.sc @@ -31,5 +31,7 @@ object Dependencies { lazy val logback = Agg(ivy"ch.qos.logback:logback-classic:1.4.11") + lazy val java8compat = Agg(ivy"org.scala-lang.modules::scala-java8-compat:1.0.2") + lazy val tests = Agg(ivy"org.scalameta::munit:0.7.29") }