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

tweaking tests #257

Merged
merged 4 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.goyeau.kubernetes.client

import cats.syntax.all.*
import io.k8s.api.core.v1.{Container, PodSpec, ResourceRequirements}
import io.k8s.apimachinery.pkg.api.resource.Quantity

object TestPodSpec {

val alpine: PodSpec = alpine(None)

def alpine(command: Seq[String]): PodSpec = alpine(command.some)

private def alpine(command: Option[Seq[String]]): PodSpec = PodSpec(
containers = Seq(
Container(
name = "test",
image = "alpine".some,
command = command,
imagePullPolicy = "IfNotPresent".some,
resources = ResourceRequirements(
requests = Map(
"memory" -> Quantity("10Mi")
).some,
limits = Map(
"memory" -> Quantity("10Mi")
).some
).some
)
),
terminationGracePeriodSeconds = 0L.some
)

}
38 changes: 27 additions & 11 deletions kubernetes-client/test/src/com/goyeau/kubernetes/client/Utils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,37 @@ object Utils {
def retry[F[_], Result](
f: F[Result],
initialDelay: FiniteDuration = 1.second,
maxRetries: Int = 50,
actionClue: Option[String] = None
maxRetries: Int = 10,
actionClue: Option[String] = None,
firstRun: Boolean = true
)(implicit
temporal: Temporal[F],
F: ApplicativeError[F, Throwable],
D: Defer[F],
log: Logger[F]
): F[Result] =
f.handleErrorWith { exception =>
if (maxRetries > 0)
log.info(
s"Retrying in $initialDelay${actionClue.map(c => s", action: $c").getOrElse("")}. Retries left: $maxRetries"
) *>
temporal.sleep(initialDelay) *>
D.defer(retry(f, initialDelay, maxRetries - 1, actionClue))
else F.raiseError(exception)
}
f
.flatTap { _ =>
F.whenA(!firstRun)(log.info(s"Succeeded after retrying${actionClue.map(c => s", action: $c").getOrElse("")}"))
}
.handleErrorWith { exception =>
val firstLine = exception.getMessage.takeWhile(_ != '\n')
val message =
if (firstLine.contains(".scala"))
firstLine.split('/').lastOption.getOrElse(firstLine)
else
firstLine

if (maxRetries > 0)
log.info(
s"$message. Retrying in $initialDelay${actionClue.map(c => s", action: $c").getOrElse("")}. Retries left: $maxRetries"
) *>
temporal.sleep(initialDelay) *>
D.defer(retry(f, initialDelay, maxRetries - 1, actionClue, firstRun = false))
else
log.info(
s"Giving up ${actionClue.map(c => s", action: $c").getOrElse("")}. No retries left"
) *>
F.raiseError(exception)
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.goyeau.kubernetes.client.api

import cats.syntax.all.*
import cats.effect.{Async, IO}
import com.goyeau.kubernetes.client.operation.*
import com.goyeau.kubernetes.client.KubernetesClient
import com.goyeau.kubernetes.client.{KubernetesClient, TestPodSpec}
import org.typelevel.log4cats.Logger
import org.typelevel.log4cats.slf4j.Slf4jLogger
import io.k8s.api.batch.v1.{CronJob, CronJobList, CronJobSpec, JobSpec, JobTemplateSpec}
Expand Down Expand Up @@ -40,12 +41,7 @@ class CronJobsApiTest
JobSpec(
template = PodTemplateSpec(
metadata = Option(ObjectMeta(name = Option(resourceName))),
spec = Option(
PodSpec(
containers = Seq(Container("test", image = Option("docker"))),
restartPolicy = Option("Never")
)
)
spec = TestPodSpec.alpine.copy(restartPolicy = "Never".some).some
)
)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.goyeau.kubernetes.client.api

import cats.effect.{Async, IO}
import com.goyeau.kubernetes.client.operation._
import com.goyeau.kubernetes.client.{IntValue, KubernetesClient, StringValue}
import com.goyeau.kubernetes.client.operation.*
import com.goyeau.kubernetes.client.{IntValue, KubernetesClient, StringValue, TestPodSpec}
import org.typelevel.log4cats.Logger
import org.typelevel.log4cats.slf4j.Slf4jLogger
import io.k8s.api.apps.v1._
import io.k8s.api.core.v1._
import io.k8s.api.apps.v1.*
import io.k8s.api.core.v1.*
import io.k8s.apimachinery.pkg.apis.meta.v1.{LabelSelector, ObjectMeta}
import munit.FunSuite

Expand Down Expand Up @@ -40,7 +40,7 @@ class DeploymentsApiTest
selector = LabelSelector(matchLabels = label),
template = PodTemplateSpec(
metadata = Option(ObjectMeta(name = Option(resourceName), labels = label)),
spec = Option(PodSpec(containers = Seq(Container("test", image = Option("docker")))))
spec = Option(TestPodSpec.alpine)
)
)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.goyeau.kubernetes.client.api

import cats.syntax.all.*
import cats.effect.{Async, IO}
import com.goyeau.kubernetes.client.operation._
import com.goyeau.kubernetes.client.KubernetesClient
import com.goyeau.kubernetes.client.operation.*
import com.goyeau.kubernetes.client.{KubernetesClient, TestPodSpec}
import org.typelevel.log4cats.Logger
import org.typelevel.log4cats.slf4j.Slf4jLogger
import io.k8s.api.batch.v1.{Job, JobList, JobSpec}
import io.k8s.api.core.v1._
import io.k8s.api.core.v1.*
import io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta
import munit.FunSuite

Expand Down Expand Up @@ -37,7 +38,7 @@ class JobsApiTest
template = PodTemplateSpec(
metadata = Option(ObjectMeta(name = Option(resourceName))),
spec = Option(
PodSpec(containers = Seq(Container("test", image = Option("docker"))), restartPolicy = Option("Never"))
TestPodSpec.alpine.copy(restartPolicy = "Never".some)
)
)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.goyeau.kubernetes.client.api

import cats.syntax.all.*
import cats.effect.unsafe.implicits.global
import cats.effect.{Async, IO}
import com.goyeau.kubernetes.client.KubernetesClient
import com.goyeau.kubernetes.client.{KubernetesClient, TestPodSpec}
import com.goyeau.kubernetes.client.Utils.retry
import com.goyeau.kubernetes.client.api.ExecStream.{StdErr, StdOut}
import com.goyeau.kubernetes.client.operation.*
Expand All @@ -15,7 +16,8 @@ import munit.FunSuite
import org.http4s.Status
import org.typelevel.log4cats.Logger
import org.typelevel.log4cats.slf4j.Slf4jLogger
import java.nio.file.{Files => JFiles}
import io.circe.syntax.*
import java.nio.file.Files as JFiles
import scala.util.Random

class PodsApiTest
Expand All @@ -40,7 +42,7 @@ class PodsApiTest
override def sampleResource(resourceName: String, labels: Map[String, String]): Pod =
Pod(
metadata = Option(ObjectMeta(name = Option(resourceName), labels = Option(labels))),
spec = Option(PodSpec(containers = Seq(Container("test", image = Option("docker")))))
spec = Option(TestPodSpec.alpine)
)

private val activeDeadlineSeconds = Option(5L)
Expand All @@ -61,38 +63,20 @@ class PodsApiTest

def testPod(podName: String, labels: Map[String, String] = Map.empty): Pod = Pod(
metadata = Option(ObjectMeta(name = Option(podName), labels = Option(labels))),
spec = Option(
PodSpec(
containers = Seq(
Container(
"test",
image = Option("docker"),
command = Option(Seq("sh", "-c", "tail -f /dev/null"))
)
)
)
)
spec = TestPodSpec.alpine(command = Seq("sh", "-c", "sleep 120")).some
)

def testPodWithLogs(podName: String, labels: Map[String, String] = Map.empty): Pod = Pod(
metadata = Option(ObjectMeta(name = Option(podName), labels = Option(labels))),
spec = Option(
PodSpec(
containers = Seq(
Container(
"test",
image = Option("docker"),
command = Option(
Seq(
"sh",
"-c",
"echo line 1; sleep 1; echo line 2; sleep 2; echo line 3; echo line 4; echo line 5; echo line 6"
)
)
)
spec = TestPodSpec
.alpine(command =
Seq(
"sh",
"-c",
"echo line 1; sleep 1; echo line 2; sleep 2; echo line 3; echo line 4; echo line 5; echo line 6"
)
)
)
.some
)

private val successStatus = Some(Right(v1.Status(status = Some("Success"), metadata = Some(ListMeta()))))
Expand Down Expand Up @@ -303,9 +287,14 @@ class PodsApiTest
statusCount = pod.status.flatMap(_.conditions.map(_.length)).getOrElse(0)
_ <-
if (notStarted || statusCount != podStatusCount)
IO.raiseError(new RuntimeException("Pod is not started"))
IO.raiseError(
new RuntimeException(
s"Pod is not started: ${pod.status.flatMap(_.conditions).toSeq.flatten.map(_.asJson.noSpaces).mkString(", ")}"
)
)
else IO.unit
} yield pod,
maxRetries = 100,
actionClue = Some(s"Waiting for pod $name to be ready")
)
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.goyeau.kubernetes.client.api

import cats.effect.{Async, IO}
import com.goyeau.kubernetes.client.KubernetesClient
import com.goyeau.kubernetes.client.{KubernetesClient, TestPodSpec}
import com.goyeau.kubernetes.client.operation.*
import org.typelevel.log4cats.Logger
import org.typelevel.log4cats.slf4j.Slf4jLogger
Expand Down Expand Up @@ -41,7 +41,7 @@ class ReplicaSetsApiTest
template = Option(
PodTemplateSpec(
metadata = Option(ObjectMeta(name = Option(resourceName), labels = label)),
spec = Option(PodSpec(containers = Seq(Container("test", image = Option("docker")))))
spec = Option(TestPodSpec.alpine)
)
)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.goyeau.kubernetes.client.api

import cats.effect.{Async, IO}
import com.goyeau.kubernetes.client.KubernetesClient
import com.goyeau.kubernetes.client.{KubernetesClient, TestPodSpec}
import com.goyeau.kubernetes.client.operation.*
import org.typelevel.log4cats.Logger
import org.typelevel.log4cats.slf4j.Slf4jLogger
Expand Down Expand Up @@ -41,7 +41,7 @@ class StatefulSetsApiTest
selector = LabelSelector(matchLabels = label),
template = PodTemplateSpec(
metadata = Option(ObjectMeta(name = Option(resourceName), labels = label)),
spec = Option(PodSpec(containers = Seq(Container("test", image = Option("docker")))))
spec = Option(TestPodSpec.alpine)
)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@ trait CreatableTests[F[_], Resource <: { def metadata: Option[ObjectMeta] }]
): F[Resource] = {
val resource = sampleResource(resourceName, labels)
for {
_ <- namespacedApi(namespaceName).create(resource)
status <- namespacedApi(namespaceName).create(resource)
_ <- logger.info(s"Created '$resourceName' in $namespaceName namespace: $status")
_ <- F.delay(assertEquals(status.isSuccess, true, s"$status should be successful"))
resource <- retry(
getChecked(namespaceName, resourceName),
actionClue = Some(s"Creating '$resourceName' in $namespaceName namespace")
actionClue = Some(s"Getting after create '$resourceName' in $namespaceName namespace"),
maxRetries = 2
)
} yield resource
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,25 +40,29 @@ trait MinikubeClientProvider[F[_]] {
}.void

private def deleteNamespace(namespace: String) = kubernetesClient.use { client =>
client.namespaces.deleteTerminated(
client.namespaces.delete(
namespace,
DeleteOptions(gracePeriodSeconds = Some(0L)).some
DeleteOptions(gracePeriodSeconds = 0L.some, propagationPolicy = "Background".some).some
)
}.void

protected def createNamespaces() = {
val ns = defaultNamespace +: extraNamespace.toList
println(s"Creating namespaces: $ns")
ns.foreach(name => unsafeRunSync(createNamespace(name)))
protected def createNamespaces(): Unit = {
val ns = defaultNamespace +: extraNamespace
unsafeRunSync(
logger.info(s"Creating namespaces: $ns") *>
ns.traverse_(name => createNamespace(name))
)
}

override def beforeAll(): Unit =
createNamespaces()

override def afterAll(): Unit = {
val ns = defaultNamespace +: extraNamespace.toList
println(s"Deleting namespaces: $ns")
ns.foreach(name => unsafeRunSync(deleteNamespace(name)))
val ns = defaultNamespace +: extraNamespace
unsafeRunSync(
logger.info(s"Deleting namespaces: $ns") *>
ns.traverse_(name => deleteNamespace(name))
)
}

def usingMinikube[T](body: KubernetesClient[F] => F[T]): T =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,11 @@ trait WatchableTests[F[_], Resource <: { def metadata: Option[ObjectMeta] }]
val expected = Set[EventType](EventType.MODIFIED, EventType.DELETED)

for {
_ <- retry(createIfMissing(defaultNamespace, name), actionClue = Some(s"createIfMissing ${defaultNamespace}/${name}"))
resource <- getChecked(defaultNamespace, name)
_ <- retry(
createIfMissing(defaultNamespace, name),
actionClue = Some(s"createIfMissing $defaultNamespace/$name")
)
resource <- getChecked(defaultNamespace, name)
resourceVersion = resource.metadata.flatMap(_.resourceVersion).get
_ <- (
watchEvents(Map(defaultNamespace -> expected), name, Some(defaultNamespace), Some(resourceVersion)),
Expand Down