Skip to content

Commit

Permalink
Merge pull request #168 from hagay3/feature/namespacedAPI
Browse files Browse the repository at this point in the history
Feature/namespaced api
hagay3 authored Jun 30, 2022

Verified

This commit was signed with the committer’s verified signature.
scala-steward Scala Steward
2 parents 3348fba + e06d895 commit afefb0f
Showing 24 changed files with 625 additions and 331 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -143,6 +143,7 @@ jobs:
minikube version: v1.25.2
kubernetes version: v1.19.6
github token: ${{ secrets.GITHUB_TOKEN }}
start args: '--extra-config=apiserver.disable-admission-plugins=ServiceAccount --extra-config=apiserver.enable-admission-plugins=NamespaceLifecycle'

- run: 'sbt ++${{ matrix.scala }} "it:testOnly "'

@@ -165,6 +166,7 @@ jobs:
minikube version: v1.25.2
kubernetes version: v1.20.11
github token: ${{ secrets.GITHUB_TOKEN }}
start args: '--extra-config=apiserver.disable-admission-plugins=ServiceAccount --extra-config=apiserver.enable-admission-plugins=NamespaceLifecycle'

- run: 'sbt ++${{ matrix.scala }} "it:testOnly "'

@@ -187,6 +189,7 @@ jobs:
minikube version: v1.25.2
kubernetes version: v1.21.5
github token: ${{ secrets.GITHUB_TOKEN }}
start args: '--extra-config=apiserver.disable-admission-plugins=ServiceAccount --extra-config=apiserver.enable-admission-plugins=NamespaceLifecycle'

- run: 'sbt ++${{ matrix.scala }} "it:testOnly "'

@@ -209,6 +212,7 @@ jobs:
minikube version: v1.25.2
kubernetes version: v1.22.9
github token: ${{ secrets.GITHUB_TOKEN }}
start args: '--extra-config=apiserver.disable-admission-plugins=ServiceAccount --extra-config=apiserver.enable-admission-plugins=NamespaceLifecycle'

- run: 'sbt ++${{ matrix.scala }} "it:testOnly * -- -l CustomResourceTag"'

@@ -231,6 +235,7 @@ jobs:
minikube version: v1.25.2
kubernetes version: v1.23.6
github token: ${{ secrets.GITHUB_TOKEN }}
start args: '--extra-config=apiserver.disable-admission-plugins=ServiceAccount --extra-config=apiserver.enable-admission-plugins=NamespaceLifecycle'

- run: 'sbt ++${{ matrix.scala }} "it:testOnly * -- -l CustomResourceTag"'

@@ -253,5 +258,6 @@ jobs:
minikube version: v1.25.2
kubernetes version: v1.24.1
github token: ${{ secrets.GITHUB_TOKEN }}
start args: '--extra-config=apiserver.disable-admission-plugins=ServiceAccount --extra-config=apiserver.enable-admission-plugins=NamespaceLifecycle'

- run: 'sbt ++${{ matrix.scala }} "it:testOnly * -- -l CustomResourceTag"'
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -67,7 +67,7 @@ Provides you with a configured client on startup. It is handy to use this for qu
> Just handy shortcut to import skuber inside ammonite-repl:
```scala
import $ivy.`io.github.hagay3::skuber:2.7.5`, skuber._, skuber.json.format._
import $ivy.`io.github.hagay3::skuber:2.7.6`, skuber._, skuber.json.format._
```

### Interactive with sbt
@@ -120,7 +120,7 @@ To get minikube follow the instructions [here](https://github.com/kubernetes/min
You can use the latest release (for 2.12 or 2.13) by adding to your build:
```sbt
libraryDependencies += "io.github.hagay3" %% "skuber" % "2.7.5"
libraryDependencies += "io.github.hagay3" %% "skuber" % "2.7.6"
```
Meanwhile users of skuber v1 can continue to use the final v1.x release, which is available only on Scala 2.11:
5 changes: 3 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -109,8 +109,9 @@ def workflowJobMinikube(jobName: String, k8sServerVersion: String, excludedTests
params = Map(
"minikubeversion" -> "v1.25.2",
"kubernetesversion" -> k8sServerVersion,
"githubtoken" -> "${{ secrets.GITHUB_TOKEN }}"),
env = Map("SBT_OPTS" -> "-XX:+CMSClassUnloadingEnabled -Xmx8G -Xms6G")
"githubtoken" -> "${{ secrets.GITHUB_TOKEN }}",
"startargs" -> "--extra-config=apiserver.disable-admission-plugins=ServiceAccount --extra-config=apiserver.enable-admission-plugins=NamespaceLifecycle"),
env = Map("SBT_OPTS" -> "-XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=2G -Xmx8G -Xms6G")
),
WorkflowStep.Sbt(List(finalSbtCommand))
)
77 changes: 51 additions & 26 deletions client/src/it/scala/skuber/CustomResourceSpec.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package skuber

import java.util.UUID.randomUUID
import akka.stream._
import akka.stream.scaladsl._
import skuber.apiextensions.CustomResourceDefinition
@@ -11,7 +12,9 @@ import scala.concurrent.duration._
import scala.language.postfixOps
import org.scalatest.Tag
import skuber.FutureUtil.FutureOps

import skuber.json.format.namespaceFormat
import skuber.Namespace.namespaceDef
import skuber.apps.v1.Deployment
/**
* This tests making requests on custom resources based on a very simple custom resource type (TestResource) defined
* here. (A TestResource consists of a desired replica count (spec) and corresponding actual replicas count (status))
@@ -45,19 +48,26 @@ class CustomResourceSpec extends K8SFixture with Eventually with Matchers with F
type TestResource = CustomResource[TestResource.Spec, TestResource.Status]
type TestResourceList = ListResource[TestResource]

val namespace1: String = randomUUID.toString

override def beforeAll(): Unit = {
val k8s = k8sInit(config)
k8s.create(TestResource.crd).withTimeout().futureValue
k8s.create(TestResource.crd).valueT
}

override def afterAll(): Unit = {
val k8s = k8sInit(config)

val results = k8s.delete[CustomResourceDefinition](TestResource.crd.name).withTimeout().recover { case _ => () }
val results2 = k8s.delete[Namespace](namespace1).withTimeout().recover { case _ => () }

results.futureValue

results.onComplete { _ =>
k8s.close
system.terminate().recover { case _ => () }.withTimeout().futureValue
results2.onComplete { _ =>
k8s.close
system.terminate().recover { case _ => () }.valueT
}
}

}
@@ -114,67 +124,67 @@ class CustomResourceSpec extends K8SFixture with Eventually with Matchers with F

override implicit val patienceConfig: PatienceConfig = PatienceConfig(10.second)

def createNamedTestResource(k8s: FixtureParam, name: String, replicas: Int): CustomResource[TestResource.Spec, TestResource.Status] = {
def createNamedTestResource(k8s: FixtureParam, name: String, replicas: Int, namespace: Option[String] = None): CustomResource[TestResource.Spec, TestResource.Status] = {
val testSpec = TestResource.Spec(replicas)
val testResource1 = TestResource(name, testSpec)
k8s.create(testResource1).withTimeout().futureValue
k8s.create(testResource1, namespace).valueT
}

it should "get a crd" taggedAs (CustomResourceTag) in { k8s =>
val getResource = k8s.get[CustomResourceDefinition](TestResource.crd.name).withTimeout().futureValue
it should "get a crd" taggedAs CustomResourceTag in { k8s =>
val getResource = k8s.get[CustomResourceDefinition](TestResource.crd.name).valueT
assert(getResource.name == TestResource.crd.name)
}


it should "create a new custom resource defined by the crd" taggedAs (CustomResourceTag) in { k8s =>
it should "create a new custom resource defined by the crd" taggedAs CustomResourceTag in { k8s =>
val testResourceName1: String = java.util.UUID.randomUUID().toString
val testResourceCreated = createNamedTestResource(k8s = k8s, name = testResourceName1, replicas = 1)

val expectedSpec = TestResource.Spec(1)
assert(testResourceCreated.name == testResourceName1)
assert(testResourceCreated.spec == expectedSpec)

val resourceGet = k8s.get[TestResource](testResourceName1).withTimeout().futureValue
val resourceGet = k8s.get[TestResource](testResourceName1).valueT

assert(resourceGet.name == testResourceName1)
assert(resourceGet.spec.desiredReplicas == 1)
assert(resourceGet.status.isEmpty)

}

it should "scale the desired replicas on the spec of the custom resource" taggedAs (CustomResourceTag) in { k8s =>
it should "scale the desired replicas on the spec of the custom resource" taggedAs CustomResourceTag in { k8s =>
val modifiedDesiredReplicas = 2
val testResourceName1: String = java.util.UUID.randomUUID().toString
createNamedTestResource(k8s = k8s, name = testResourceName1, replicas = 1)

val currentScale = k8s.getScale[TestResource](testResourceName1).withTimeout().futureValue
val currentScale = k8s.getScale[TestResource](testResourceName1).valueT

k8s.updateScale[TestResource](testResourceName1, currentScale.withSpecReplicas(modifiedDesiredReplicas)).withTimeout().futureValue
val updated = k8s.get[TestResource](testResourceName1).withTimeout().futureValue
k8s.updateScale[TestResource](testResourceName1, currentScale.withSpecReplicas(modifiedDesiredReplicas)).valueT
val updated = k8s.get[TestResource](testResourceName1).valueT
assert(updated.spec.desiredReplicas == modifiedDesiredReplicas)
}

it should "update the status on the custom resource with a modified actual replicas count" taggedAs (CustomResourceTag) in { k8s =>
it should "update the status on the custom resource with a modified actual replicas count" taggedAs CustomResourceTag in { k8s =>
val specReplicas = 1
val testResourceName1: String = java.util.UUID.randomUUID().toString
createNamedTestResource(k8s = k8s, name = testResourceName1, replicas = specReplicas)

val modifiedActualReplicas = 3
val status = TestResource.Status(modifiedActualReplicas)

val testResource = k8s.get[TestResource](testResourceName1).withTimeout().futureValue
val updated = k8s.updateStatus(testResource.withStatus(status)).withTimeout().futureValue
val testResource = k8s.get[TestResource](testResourceName1).valueT
val updated = k8s.updateStatus(testResource.withStatus(status)).valueT
updated.status shouldBe Some(status)

val scale = k8s.getScale[TestResource](testResourceName1).withTimeout().futureValue
val scale = k8s.getScale[TestResource](testResourceName1).valueT
scale.spec.replicas shouldBe Some(specReplicas)
scale.status.get.replicas shouldBe modifiedActualReplicas
}

it should "delete the custom resource" taggedAs (CustomResourceTag) in { k8s =>
it should "delete the custom resource" taggedAs CustomResourceTag in { k8s =>
val testResourceName1: String = java.util.UUID.randomUUID().toString
createNamedTestResource(k8s = k8s, name = testResourceName1, replicas = 1)
k8s.delete[TestResource](testResourceName1).withTimeout().futureValue
k8s.delete[TestResource](testResourceName1).valueT

whenReady(
k8s.get[TestResource](testResourceName1).withTimeout().failed
@@ -187,7 +197,23 @@ class CustomResourceSpec extends K8SFixture with Eventually with Matchers with F
}
}

it should "watch the custom resources" taggedAs (CustomResourceTag) in { k8s =>
it should "getStatus on deployment - specific namespace" taggedAs CustomResourceTag in { k8s =>
createNamespace(namespace1, k8s)

val testName = randomUUID().toString
val actualReplicas = 1
val desiredReplicas = 7
val dToCreate = new TestResource(metadata = ObjectMeta(name = testName), status = None, kind = "SkuberTest", apiVersion = "test.skuber.io/v1alpha1", spec = TestResource.Spec(desiredReplicas))
k8s.create(dToCreate, Some(namespace1)).valueT
val dGet = k8s.get[TestResource](testName, Some(namespace1)).valueT
k8s.updateStatus(dGet.withStatus(TestResource.Status(actualReplicas)), Some(namespace1)).valueT

val actualStatus = k8s.getStatus[TestResource](testName, Some(namespace1)).valueT
actualStatus.status.map(_.actualReplicas) shouldBe Some(actualReplicas)

}

it should "watch the custom resources" taggedAs CustomResourceTag in { k8s =>
import skuber.api.client.{EventType, WatchEvent}
import scala.collection.mutable.ListBuffer

@@ -200,20 +226,20 @@ class CustomResourceSpec extends K8SFixture with Eventually with Matchers with F
trackedEvents += event
}

def getCurrentResourceVersion: String = k8s.list[TestResourceList].withTimeout().futureValue.resourceVersion
def getCurrentResourceVersion: String = k8s.list[TestResourceList].valueT.resourceVersion


def watchAndTrackEvents(sinceVersion: String) = {
val crEventSource = k8s.watchAll[TestResource](sinceResourceVersion = Some(sinceVersion)).withTimeout().futureValue
val crEventSource = k8s.watchAll[TestResource](sinceResourceVersion = Some(sinceVersion)).valueT
crEventSource
.viaMat(KillSwitches.single)(Keep.right)
.toMat(trackEvents)(Keep.both).run()

}

def createTestResource = k8s.create(testResource).withTimeout().futureValue
def createTestResource = k8s.create(testResource).valueT

def deleteTestResource(resource: String) = k8s.delete[TestResource](resource).withTimeout().futureValue
def deleteTestResource(resource: String) = k8s.delete[TestResource](resource).valueT

val killSwitch: UniqueKillSwitch = {
val (kill, _) = watchAndTrackEvents(getCurrentResourceVersion)
@@ -234,5 +260,4 @@ class CustomResourceSpec extends K8SFixture with Eventually with Matchers with F
killSwitch.shutdown()
assert(true)
}

}
Loading

0 comments on commit afefb0f

Please sign in to comment.