Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • she/theodolite
1 result
Show changes
Showing
with 268 additions and 503 deletions
......@@ -7,7 +7,6 @@ import io.fabric8.kubernetes.client.dsl.ExecListener
import io.fabric8.kubernetes.client.dsl.ExecWatch
import io.fabric8.kubernetes.client.utils.Serialization
import mu.KotlinLogging
import okhttp3.Response
import theodolite.util.ActionCommandFailedException
import theodolite.util.Configuration
import java.io.ByteArrayOutputStream
......@@ -145,10 +144,8 @@ class ActionCommand(val client: NamespacedKubernetesClient) {
}
private class ActionCommandListener(val execLatch: CountDownLatch) : ExecListener {
override fun onOpen(response: Response) {
}
override fun onFailure(throwable: Throwable, response: Response) {
override fun onFailure(throwable: Throwable, response: ExecListener.Response) {
execLatch.countDown()
throw ActionCommandFailedException("Some error encountered while executing action, caused ${throwable.message})")
}
......
package theodolite.benchmark
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import io.fabric8.kubernetes.api.model.HasMetadata
import io.fabric8.kubernetes.api.model.KubernetesResource
import io.fabric8.kubernetes.client.KubernetesClientException
import io.fabric8.kubernetes.client.NamespacedKubernetesClient
import io.quarkus.runtime.annotations.RegisterForReflection
import theodolite.k8s.resourceLoader.K8sResourceLoaderFromString
import theodolite.util.DeploymentFailedException
import theodolite.util.YamlParserFromString
import java.lang.IllegalArgumentException
@RegisterForReflection
@JsonDeserialize
class ConfigMapResourceSet : ResourceSet, KubernetesResource {
lateinit var name: String
lateinit var files: List<String> // load all files, iff files is not set
var files: List<String>? = null // load all files, iff files is not set
override fun getResourceSet(client: NamespacedKubernetesClient): Collection<Pair<String, KubernetesResource>> {
val loader = K8sResourceLoaderFromString(client)
override fun getResourceSet(client: NamespacedKubernetesClient): Collection<Pair<String, HasMetadata>> {
var resources: Map<String, String>
try {
......@@ -31,9 +29,9 @@ class ConfigMapResourceSet : ResourceSet, KubernetesResource {
throw DeploymentFailedException("Cannot find or read ConfigMap with name '$name'.", e)
}
if (::files.isInitialized) {
val filteredResources = resources.filter { files.contains(it.key) }
if (filteredResources.size != files.size) {
files?.run {
val filteredResources = resources.filter { this.contains(it.key) }
if (filteredResources.size != this.size) {
throw DeploymentFailedException("Could not find all specified Kubernetes manifests files")
}
resources = filteredResources
......@@ -43,14 +41,8 @@ class ConfigMapResourceSet : ResourceSet, KubernetesResource {
resources
.map {
Pair(
getKind(resource = it.value),
it
)
}
.map {
Pair(
it.second.key,
loader.loadK8sResource(it.first, it.second.value)
it.key, // filename
client.resource(it.value).get()
)
}
} catch (e: IllegalArgumentException) {
......@@ -59,11 +51,4 @@ class ConfigMapResourceSet : ResourceSet, KubernetesResource {
}
private fun getKind(resource: String): String {
val parser = YamlParserFromString()
val resourceAsMap = parser.parse(resource, HashMap<String, String>()::class.java)
return resourceAsMap?.get("kind")
?: throw DeploymentFailedException("Could not find field kind of Kubernetes resource: ${resourceAsMap?.get("name")}")
}
}
\ No newline at end of file
package theodolite.benchmark
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import io.fabric8.kubernetes.api.model.HasMetadata
import io.fabric8.kubernetes.api.model.KubernetesResource
import io.fabric8.kubernetes.client.NamespacedKubernetesClient
import io.quarkus.runtime.annotations.RegisterForReflection
import theodolite.k8s.resourceLoader.K8sResourceLoaderFromFile
import theodolite.util.DeploymentFailedException
import theodolite.util.YamlParserFromFile
import java.io.File
import java.io.BufferedReader
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.lang.IllegalArgumentException
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
import java.nio.file.Path
import java.nio.file.Paths
import java.util.stream.Collectors
import kotlin.io.path.listDirectoryEntries
@RegisterForReflection
@JsonDeserialize
class FileSystemResourceSet: ResourceSet, KubernetesResource {
lateinit var path: String
lateinit var files: List<String>
override fun getResourceSet(client: NamespacedKubernetesClient): Collection<Pair<String, KubernetesResource>> {
//if files is set ...
if(::files.isInitialized){
return files
.map { loadSingleResource(resourceURL = it, client = client) }
}
return try {
File(path)
.list() !!
.filter { it.endsWith(".yaml") || it.endsWith(".yml") }
.map {
loadSingleResource(resourceURL = it, client = client)
}
} catch (e: NullPointerException) {
var files: List<String>? = null
override fun getResourceSet(client: NamespacedKubernetesClient): Collection<Pair<String, HasMetadata>> {
// if files is set ...
return files?.run {
return this
.map { Paths.get(path, it) }
.map { loadSingleResource(resource = it, client = client) }
} ?:
try {
Paths.get(path)
.listDirectoryEntries()
.filter { it.toString().endsWith(".yaml") || it.toString().endsWith(".yml") }
.map { loadSingleResource(resource = it, client = client) }
} catch (e: java.nio.file.NoSuchFileException) { // not to be confused with Kotlin exception
throw DeploymentFailedException("Could not load files located in $path", e)
}
}
private fun loadSingleResource(resourceURL: String, client: NamespacedKubernetesClient): Pair<String, KubernetesResource> {
val parser = YamlParserFromFile()
val loader = K8sResourceLoaderFromFile(client)
val resourcePath = "$path/$resourceURL"
lateinit var kind: String
try {
kind = parser.parse(resourcePath, HashMap<String, String>()::class.java)?.get("kind")!!
} catch (e: NullPointerException) {
throw DeploymentFailedException("Can not get Kind from resource $resourcePath", e)
} catch (e: FileNotFoundException){
throw DeploymentFailedException("File $resourcePath not found", e)
}
private fun loadSingleResource(resource: Path, client: NamespacedKubernetesClient): Pair<String, HasMetadata> {
return try {
val k8sResource = loader.loadK8sResource(kind, resourcePath)
Pair(resourceURL, k8sResource)
val stream = FileInputStream(resource.toFile())
val text = BufferedReader(
InputStreamReader(stream, StandardCharsets.UTF_8)
).lines().collect(Collectors.joining("\n"))
val k8sResource = client.resource(text).get()
Pair(resource.last().toString(), k8sResource)
} catch (e: FileNotFoundException){
throw DeploymentFailedException("File $resource not found.", e)
} catch (e: IllegalArgumentException) {
throw DeploymentFailedException("Could not load resource: $resourcePath", e)
throw DeploymentFailedException("Could not load resource: $resource.", e)
}
}
}
\ No newline at end of file
package theodolite.benchmark
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import io.fabric8.kubernetes.api.model.HasMetadata
import io.fabric8.kubernetes.api.model.KubernetesResource
import io.fabric8.kubernetes.client.DefaultKubernetesClient
import io.fabric8.kubernetes.client.NamespacedKubernetesClient
import io.quarkus.runtime.annotations.RegisterForReflection
import mu.KotlinLogging
import theodolite.k8s.K8sManager
import theodolite.k8s.resourceLoader.K8sResourceLoader
import theodolite.patcher.PatcherFactory
import theodolite.util.*
......@@ -53,22 +53,27 @@ class KubernetesBenchmark : KubernetesResource, Benchmark {
* It first loads them via the [YamlParserFromFile] to check for their concrete type and afterwards initializes them using
* the [K8sResourceLoader]
*/
fun loadKubernetesResources(resourceSet: List<ResourceSets>): Collection<Pair<String, KubernetesResource>> {
@Deprecated("Use `loadResourceSet` from `ResourceSets`")
fun loadKubernetesResources(resourceSet: List<ResourceSets>): Collection<Pair<String, HasMetadata>> {
return loadResources(resourceSet)
}
private fun loadResources(resourceSet: List<ResourceSets>): Collection<Pair<String, HasMetadata>> {
return resourceSet.flatMap { it.loadResourceSet(this.client) }
}
override fun setupInfrastructure() {
this.infrastructure.beforeActions.forEach { it.exec(client = client) }
val kubernetesManager = K8sManager(this.client)
loadKubernetesResources(this.infrastructure.resources)
.map{it.second}
loadResources(this.infrastructure.resources)
.map { it.second }
.forEach { kubernetesManager.deploy(it) }
}
override fun teardownInfrastructure() {
val kubernetesManager = K8sManager(this.client)
loadKubernetesResources(this.infrastructure.resources)
.map{it.second}
loadResources(this.infrastructure.resources)
.map { it.second }
.forEach { kubernetesManager.remove(it) }
this.infrastructure.afterActions.forEach { it.exec(client = client) }
}
......@@ -91,8 +96,8 @@ class KubernetesBenchmark : KubernetesResource, Benchmark {
): BenchmarkDeployment {
logger.info { "Using $namespace as namespace." }
val appResources = loadKubernetesResources(this.sut.resources)
val loadGenResources = loadKubernetesResources(this.loadGenerator.resources)
val appResources = loadResources(this.sut.resources)
val loadGenResources = loadResources(this.loadGenerator.resources)
val patcherFactory = PatcherFactory()
......@@ -122,7 +127,7 @@ class KubernetesBenchmark : KubernetesResource, Benchmark {
loadGenResources = loadGenResources.map { it.second },
loadGenerationDelay = loadGenerationDelay,
afterTeardownDelay = afterTeardownDelay,
kafkaConfig = if (kafkaConfig != null) hashMapOf("bootstrap.servers" to kafkaConfig.bootstrapServer) else mapOf(),
kafkaConfig = if (kafkaConfig != null) mapOf("bootstrap.servers" to kafkaConfig.bootstrapServer) else mapOf(),
topics = kafkaConfig?.topics ?: listOf(),
client = this.client
)
......
package theodolite.benchmark
import io.fabric8.kubernetes.api.model.HasMetadata
import io.fabric8.kubernetes.api.model.KubernetesResource
import io.fabric8.kubernetes.client.NamespacedKubernetesClient
import io.quarkus.runtime.annotations.RegisterForReflection
......@@ -27,8 +28,8 @@ class KubernetesBenchmarkDeployment(
private val sutAfterActions: List<Action>,
private val loadGenBeforeActions: List<Action>,
private val loadGenAfterActions: List<Action>,
val appResources: List<KubernetesResource>,
val loadGenResources: List<KubernetesResource>,
val appResources: List<HasMetadata>,
val loadGenResources: List<HasMetadata>,
private val loadGenerationDelay: Long,
private val afterTeardownDelay: Long,
private val kafkaConfig: Map<String, Any>,
......
......@@ -3,6 +3,7 @@ package theodolite.benchmark
import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import io.fabric8.kubernetes.api.model.HasMetadata
import io.fabric8.kubernetes.api.model.KubernetesResource
import io.fabric8.kubernetes.client.NamespacedKubernetesClient
import io.quarkus.runtime.annotations.RegisterForReflection
......@@ -19,13 +20,14 @@ class ResourceSets: KubernetesResource {
@JsonInclude(JsonInclude.Include.NON_NULL)
var fileSystem: FileSystemResourceSet? = null
fun loadResourceSet(client: NamespacedKubernetesClient): Collection<Pair<String, KubernetesResource>> {
fun loadResourceSet(client: NamespacedKubernetesClient): Collection<Pair<String, HasMetadata>> {
// TODO Find out whether field access (::configMap) is really what we want to do here (see #362)
return if (::configMap != null) {
configMap?.getResourceSet(client= client) !!
configMap?.getResourceSet(client= client) !!
} else if (::fileSystem != null) {
fileSystem?.getResourceSet(client= client ) !!
fileSystem?.getResourceSet(client= client ) !!
} else {
throw DeploymentFailedException("could not load resourceSet.")
throw DeploymentFailedException("Could not load resourceSet.")
}
}
}
\ No newline at end of file
......@@ -25,7 +25,12 @@ abstract class AbstractStateHandler<S : HasMetadata>(
val resource = this.crdClient.withName(resourceName).get()
if (resource != null) {
val resourcePatched = setter(resource)
this.crdClient.patchStatus(resourcePatched)
// TODO replace with this.crdClient.replaceStatus(resourcePatched) with upcoming fabric8 release (> 5.12.1)
// find out the difference between patchStatus and replaceStatus
// see also https://github.com/fabric8io/kubernetes-client/pull/3798
if (resourcePatched != null) {
this.crdClient.withName(resourcePatched.metadata.name).patchStatus(resourcePatched)
}
}
} catch (e: KubernetesClientException) {
logger.warn(e) { "Status cannot be set for resource $resourceName." }
......
......@@ -130,8 +130,7 @@ class TheodoliteController(
.list()
.items
.map {
it.spec.name = it.metadata.name
it
it.apply { it.spec.name = it.metadata.name }
}
}
......
package theodolite.k8s
import io.fabric8.kubernetes.api.model.KubernetesResource
import io.fabric8.kubernetes.client.NamespacedKubernetesClient
import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext
import mu.KotlinLogging
private val logger = KotlinLogging.logger {}
class CustomResourceWrapper(
val crAsMap: Map<String, String>,
private val context: CustomResourceDefinitionContext
) : KubernetesResource {
/**
* Deploy a service monitor
*
* @param client a namespaced Kubernetes client which are used to deploy the CR object.
*
* @throws java.io.IOException if the resource could not be deployed.
*/
fun deploy(client: NamespacedKubernetesClient) {
client.customResource(this.context)
.createOrReplace(client.configuration.namespace, this.crAsMap as Map<String, Any>)
}
/**
* Delete a service monitor
*
* @param client a namespaced Kubernetes client which are used to delete the CR object.
*/
fun delete(client: NamespacedKubernetesClient) {
try {
client.customResource(this.context)
.delete(client.configuration.namespace, this.getName())
} catch (e: Exception) {
logger.warn { "Could not delete custom resource" }
}
}
/**
* @throws NullPointerException if name or metadata is null
*/
fun getName(): String {
val metadataAsMap = this.crAsMap["metadata"]!! as Map<String, String>
return metadataAsMap["name"]!!
}
}
......@@ -7,6 +7,7 @@ import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext
*
* @see CustomResourceDefinitionContext
*/
@Deprecated("Use `CustomResourceDefinitionContext.Builder` instead.")
class K8sContextFactory {
/**
......
package theodolite.k8s
import io.fabric8.kubernetes.api.model.ConfigMap
import io.fabric8.kubernetes.api.model.HasMetadata
import io.fabric8.kubernetes.api.model.KubernetesResource
import io.fabric8.kubernetes.api.model.Service
import io.fabric8.kubernetes.api.model.apps.Deployment
......@@ -21,49 +22,31 @@ class K8sManager(private val client: NamespacedKubernetesClient) {
* Deploys different k8s resources using the client.
* @throws IllegalArgumentException if KubernetesResource not supported.
*/
fun deploy(resource: KubernetesResource) {
when (resource) {
is Deployment ->
this.client.apps().deployments().createOrReplace(resource)
is Service ->
this.client.services().createOrReplace(resource)
is ConfigMap ->
this.client.configMaps().createOrReplace(resource)
is StatefulSet ->
this.client.apps().statefulSets().createOrReplace(resource)
is CustomResourceWrapper -> resource.deploy(client)
else -> throw IllegalArgumentException("Unknown Kubernetes resource.")
}
fun deploy(resource: HasMetadata) {
client.resource(resource).createOrReplace()
}
/**
* Removes different k8s resources using the client.
* @throws IllegalArgumentException if KubernetesResource not supported.
*/
fun remove(resource: KubernetesResource) {
fun remove(resource: HasMetadata) {
client.resource(resource).delete()
when (resource) {
is Deployment -> {
this.client.apps().deployments().delete(resource)
ResourceByLabelHandler(client = client)
.blockUntilPodsDeleted(
matchLabels = resource.spec.selector.matchLabels
)
logger.info { "Deployment '${resource.metadata.name}' deleted." }
}
is Service ->
this.client.services().delete(resource)
is ConfigMap ->
this.client.configMaps().delete(resource)
is StatefulSet -> {
this.client.apps().statefulSets().delete(resource)
ResourceByLabelHandler(client = client)
.blockUntilPodsDeleted(
matchLabels = resource.spec.selector.matchLabels
)
logger.info { "StatefulSet '$resource.metadata.name' deleted." }
}
is CustomResourceWrapper -> resource.delete(client)
else -> throw IllegalArgumentException("Unknown Kubernetes resource.")
}
}
}
package theodolite.k8s.resourceLoader
import io.fabric8.kubernetes.api.model.KubernetesResource
import mu.KotlinLogging
import theodolite.k8s.K8sContextFactory
private val logger = KotlinLogging.logger {}
abstract class AbstractK8sLoader: K8sResourceLoader {
fun loadK8sResource(kind: String, resourceString: String): KubernetesResource {
return when (kind.replaceFirst(kind[0],kind[0].uppercaseChar())) {
"Deployment" -> loadDeployment(resourceString)
"Service" -> loadService(resourceString)
"ServiceMonitor" -> loadServiceMonitor(resourceString)
"PodMonitor" -> loadPodMonitor(resourceString)
"ConfigMap" -> loadConfigmap(resourceString)
"StatefulSet" -> loadStatefulSet(resourceString)
"Execution" -> loadExecution(resourceString)
"Benchmark" -> loadBenchmark(resourceString)
else -> {
logger.error { "Error during loading of unspecified resource Kind '$kind'." }
throw IllegalArgumentException("error while loading resource with kind: $kind")
}
}
}
fun <T : KubernetesResource> loadGenericResource(resourceString: String, f: (String) -> T): T {
var resource: T? = null
try {
resource = f(resourceString)
} catch (e: Exception) {
logger.warn { e }
}
if (resource == null) {
throw IllegalArgumentException("The Resource: $resourceString could not be loaded")
}
return resource
}
override fun loadServiceMonitor(resource: String): KubernetesResource {
val context = K8sContextFactory().create(
api = "v1",
scope = "Namespaced",
group = "monitoring.coreos.com",
plural = "servicemonitors"
)
return loadCustomResourceWrapper(resource, context)
}
override fun loadPodMonitor(resource: String): KubernetesResource {
val context = K8sContextFactory().create(
api = "v1",
scope = "Namespaced",
group = "monitoring.coreos.com",
plural = "podmonitors"
)
return loadCustomResourceWrapper(resource, context)
}
override fun loadExecution(resource: String): KubernetesResource {
val context = K8sContextFactory().create(
api = "v1",
scope = "Namespaced",
group = "theodolite.com",
plural = "executions"
)
return loadCustomResourceWrapper(resource, context)
}
override fun loadBenchmark(resource: String): KubernetesResource {
val context = K8sContextFactory().create(
api = "v1",
scope = "Namespaced",
group = "theodolite.com",
plural = "benchmarks"
)
return loadCustomResourceWrapper(resource, context)
}
}
\ No newline at end of file
package theodolite.k8s.resourceLoader
import io.fabric8.kubernetes.api.model.KubernetesResource
import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext
interface K8sResourceLoader {
fun loadDeployment(resource: String): KubernetesResource
fun loadService(resource: String): KubernetesResource
fun loadStatefulSet(resource: String): KubernetesResource
fun loadExecution(resource: String): KubernetesResource
fun loadBenchmark(resource: String): KubernetesResource
fun loadConfigmap(resource: String): KubernetesResource
fun loadServiceMonitor(resource: String): KubernetesResource
fun loadPodMonitor(resource: String): KubernetesResource
fun loadCustomResourceWrapper(resource: String, context: CustomResourceDefinitionContext): KubernetesResource
}
\ No newline at end of file
package theodolite.k8s.resourceLoader
import io.fabric8.kubernetes.api.model.ConfigMap
import io.fabric8.kubernetes.api.model.KubernetesResource
import io.fabric8.kubernetes.api.model.Service
import io.fabric8.kubernetes.api.model.apps.Deployment
import io.fabric8.kubernetes.client.NamespacedKubernetesClient
import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext
import theodolite.k8s.CustomResourceWrapper
import theodolite.util.YamlParserFromFile
/**
* Used to load different Kubernetes resources.
* Supports: Deployments, Services, ConfigMaps, and CustomResources.
* @param client KubernetesClient used to deploy or remove.
*/
class K8sResourceLoaderFromFile(private val client: NamespacedKubernetesClient): AbstractK8sLoader(),
K8sResourceLoader {
/**
* Parses a Service from a service yaml
* @param resource of the yaml file
* @return Service from fabric8
*/
override fun loadService(resource: String): Service {
return loadGenericResource(resource) { x: String -> client.services().load(x).get() }
}
/**
* Parses a CustomResource from a yaml
* @param path of the yaml file
* @param context specific crd context for this custom resource
* @return CustomResourceWrapper from fabric8
*/
override fun loadCustomResourceWrapper(resource: String, context: CustomResourceDefinitionContext): CustomResourceWrapper {
return loadGenericResource(resource) {
CustomResourceWrapper(
YamlParserFromFile().parse(
resource,
HashMap<String, String>()::class.java
)!!,
context
)
}
}
/**
* Parses a Deployment from a Deployment yaml
* @param resource of the yaml file
* @return Deployment from fabric8
*/
override fun loadDeployment(resource: String): Deployment {
return loadGenericResource(resource) { x: String -> client.apps().deployments().load(x).get() }
}
/**
* Parses a ConfigMap from a ConfigMap yaml
* @param resource of the yaml file
* @return ConfigMap from fabric8
*/
override fun loadConfigmap(resource: String): ConfigMap {
return loadGenericResource(resource) { x: String -> client.configMaps().load(x).get() }
}
/**
* Parses a StatefulSet from a StatefulSet yaml
* @param resource of the yaml file
* @return StatefulSet from fabric8
*/
override fun loadStatefulSet(resource: String): KubernetesResource {
return loadGenericResource(resource) { x: String -> client.apps().statefulSets().load(x).get() }
}
}
package theodolite.k8s.resourceLoader
import io.fabric8.kubernetes.api.model.ConfigMap
import io.fabric8.kubernetes.api.model.KubernetesResource
import io.fabric8.kubernetes.api.model.Service
import io.fabric8.kubernetes.api.model.apps.Deployment
import io.fabric8.kubernetes.api.model.apps.StatefulSet
import io.fabric8.kubernetes.client.NamespacedKubernetesClient
import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext
import theodolite.k8s.CustomResourceWrapper
import theodolite.util.YamlParserFromString
import java.io.ByteArrayInputStream
import java.io.InputStream
class K8sResourceLoaderFromString(private val client: NamespacedKubernetesClient): AbstractK8sLoader(),
K8sResourceLoader {
override fun loadService(resource: String): Service {
return loadAnyResource(resource) { stream -> client.services().load(stream).get() }
}
override fun loadDeployment(resource: String): Deployment {
return loadAnyResource(resource) { stream -> client.apps().deployments().load(stream).get() }
}
override fun loadConfigmap(resource: String): ConfigMap {
return loadAnyResource(resource) { stream -> client.configMaps().load(stream).get() }
}
override fun loadStatefulSet(resource: String): StatefulSet {
return loadAnyResource(resource) { stream -> client.apps().statefulSets().load(stream).get() }
}
private fun <T : KubernetesResource> loadAnyResource(resource: String, f: (InputStream) -> T): T {
return loadGenericResource(resource) { f(ByteArrayInputStream(it.encodeToByteArray())) }
}
/**
* Parses a CustomResource from a yaml
* @param resource of the yaml file
* @param context specific crd context for this custom resource
* @return CustomResourceWrapper from fabric8
*/
override fun loadCustomResourceWrapper(resource: String, context: CustomResourceDefinitionContext): CustomResourceWrapper {
return loadGenericResource(resource) {
CustomResourceWrapper(
YamlParserFromString().parse(
resource,
HashMap<String, String>()::class.java
)!!,
context
)
}
}
}
\ No newline at end of file
......@@ -21,5 +21,5 @@ class PatcherDefinition {
lateinit var resource: String
@JsonSerialize
lateinit var properties: MutableMap<String, String>
lateinit var properties: Map<String, String>
}
......@@ -9,6 +9,7 @@ import java.io.InputStream
/**
* The YamlParser parses a YAML file
*/
@Deprecated("Use Jackson ObjectMapper instead")
class YamlParserFromFile : Parser {
override fun <T> parse(path: String, E: Class<T>): T? {
val input: InputStream = FileInputStream(File(path))
......
......@@ -6,6 +6,7 @@ import org.yaml.snakeyaml.constructor.Constructor
/**
* The YamlParser parses a YAML string
*/
@Deprecated("Use Jackson ObjectMapper instead")
class YamlParserFromString : Parser {
override fun <T> parse(fileString: String, E: Class<T>): T? {
val parser = Yaml(Constructor(E))
......
import io.fabric8.kubernetes.api.model.APIResourceListBuilder
import io.fabric8.kubernetes.client.dsl.base.ResourceDefinitionContext
import io.fabric8.kubernetes.client.server.mock.KubernetesServer
fun KubernetesServer.registerResource(context: ResourceDefinitionContext) {
val apiResourceList = APIResourceListBuilder()
.addNewResource()
.withName(context.plural)
.withKind(context.kind)
.withNamespaced(context.isNamespaceScoped)
.endResource()
.build()
this
.expect()
.get()
.withPath("/apis/${context.group}/${context.version}")
.andReturn(200, apiResourceList)
.always()
}
\ No newline at end of file
package theodolite.benchmark
import com.google.gson.Gson
import com.fasterxml.jackson.databind.ObjectMapper
import io.fabric8.kubernetes.api.model.*
import io.fabric8.kubernetes.api.model.apps.Deployment
import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder
import io.fabric8.kubernetes.api.model.apps.StatefulSet
import io.fabric8.kubernetes.api.model.apps.StatefulSetBuilder
import io.fabric8.kubernetes.client.dsl.MixedOperation
import io.fabric8.kubernetes.client.dsl.Resource
import io.fabric8.kubernetes.client.dsl.base.ResourceDefinitionContext
import io.fabric8.kubernetes.client.server.mock.KubernetesServer
import io.quarkus.test.junit.QuarkusTest
import io.quarkus.test.kubernetes.client.KubernetesTestServer
import io.quarkus.test.kubernetes.client.WithKubernetesTestServer
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import theodolite.k8s.CustomResourceWrapper
import theodolite.k8s.resourceLoader.K8sResourceLoaderFromFile
import org.mockito.kotlin.mock
import registerResource
import theodolite.TestBenchmark
import theodolite.execution.operator.BenchmarkCRDummy
import theodolite.execution.operator.ExecutionClient
import theodolite.execution.operator.ExecutionEventHandler
import theodolite.execution.operator.ExecutionStateHandler
import theodolite.model.crd.BenchmarkCRD
import theodolite.model.crd.ExecutionCRD
import theodolite.util.DeploymentFailedException
import java.io.FileInputStream
private const val testResourcePath = "./src/test/resources/k8s-resource-files/"
// TODO move somewhere else
typealias BenchmarkClient = MixedOperation<BenchmarkCRD, KubernetesResourceList<BenchmarkCRD>, Resource<BenchmarkCRD>>
@QuarkusTest
class ConfigMapResourceSetTest {
private val server = KubernetesServer(false, true)
@WithKubernetesTestServer
internal class ConfigMapResourceSetTest {
@KubernetesTestServer
private lateinit var server: KubernetesServer
private val objectMapper: ObjectMapper = ObjectMapper()
private lateinit var executionClient: ExecutionClient
private lateinit var benchmarkClient: BenchmarkClient
@BeforeEach
fun setUp() {
server.before()
this.server.client
.apiextensions().v1()
.customResourceDefinitions()
.load(FileInputStream("crd/crd-execution.yaml"))
.create()
this.server.client
.apiextensions().v1()
.customResourceDefinitions()
.load(FileInputStream("crd/crd-benchmark.yaml"))
.create()
this.executionClient = this.server.client.resources(ExecutionCRD::class.java)
this.benchmarkClient = this.server.client.resources(BenchmarkCRD::class.java)
}
@AfterEach
......@@ -34,184 +69,196 @@ class ConfigMapResourceSetTest {
server.after()
}
fun deployAndGetResource(resource: String): Collection<Pair<String, KubernetesResource>> {
val configMap1 = ConfigMapBuilder()
private fun deployAndGetResource(vararg resources: HasMetadata): ConfigMapResourceSet {
val configMap = ConfigMapBuilder()
.withNewMetadata().withName("test-configmap").endMetadata()
.addToData("test-resource.yaml",resource)
.let {
resources.foldIndexed(it) {
i, b, r -> b.addToData("resource_$i.yaml", objectMapper.writeValueAsString(r))
}
}
.build()
server.client.configMaps().createOrReplace(configMap1)
server.client.configMaps().createOrReplace(configMap)
val resourceSet = ConfigMapResourceSet()
resourceSet.name = "test-configmap"
return resourceSet.getResourceSet(server.client)
return resourceSet
}
@Test
fun testLoadDeployment() {
val resourceBuilder = DeploymentBuilder()
resourceBuilder.withNewSpec().endSpec()
resourceBuilder.withNewMetadata().endMetadata()
val resource = resourceBuilder.build()
resource.metadata.name = "test-deployment"
val resource = DeploymentBuilder()
.withNewSpec()
.endSpec()
.withNewMetadata()
.withName("test-deployment")
.endMetadata()
.build()
val createdResource = deployAndGetResource(resource = Gson().toJson(resource))
val createdResource = deployAndGetResource(resource).getResourceSet(server.client)
assertEquals(1, createdResource.size)
assertTrue(createdResource.toMutableSet().first().second is Deployment)
assertTrue(createdResource.toMutableSet().first().second.toString().contains(other = resource.metadata.name))
assertTrue(createdResource.toList().first().second is Deployment)
assertTrue(createdResource.toList().first().second.toString().contains(other = resource.metadata.name))
}
@Test
fun testLoadStateFulSet() {
val resourceBuilder = StatefulSetBuilder()
resourceBuilder.withNewSpec().endSpec()
resourceBuilder.withNewMetadata().endMetadata()
val resource = resourceBuilder.build()
resource.metadata.name = "test-resource"
val resource = StatefulSetBuilder()
.withNewSpec()
.endSpec()
.withNewMetadata()
.withName("test-sts")
.endMetadata()
.build()
val createdResource = deployAndGetResource(resource = Gson().toJson(resource))
val createdResource = deployAndGetResource(resource).getResourceSet(server.client)
assertEquals(1, createdResource.size)
assertTrue(createdResource.toMutableSet().first().second is StatefulSet)
assertTrue(createdResource.toMutableSet().first().second.toString().contains(other = resource.metadata.name))
assertTrue(createdResource.toList().first().second is StatefulSet)
assertTrue(createdResource.toList().first().second.toString().contains(other = resource.metadata.name))
}
@Test
fun testLoadService() {
val resourceBuilder = ServiceBuilder()
resourceBuilder.withNewSpec().endSpec()
resourceBuilder.withNewMetadata().endMetadata()
val resource = resourceBuilder.build()
resource.metadata.name = "test-resource"
val resource = ServiceBuilder()
.withNewSpec()
.endSpec()
.withNewMetadata()
.withName("test-service")
.endMetadata()
.build()
val createdResource = deployAndGetResource(resource = Gson().toJson(resource))
val createdResource = deployAndGetResource(resource).getResourceSet(server.client)
assertEquals(1, createdResource.size)
assertTrue(createdResource.toMutableSet().first().second is Service)
assertTrue(createdResource.toMutableSet().first().second.toString().contains(other = resource.metadata.name))
assertTrue(createdResource.toList().first().second is Service)
assertTrue(createdResource.toList().first().second.toString().contains(other = resource.metadata.name))
}
@Test
fun testLoadConfigMap() {
val resourceBuilder = ConfigMapBuilder()
resourceBuilder.withNewMetadata().endMetadata()
val resource = resourceBuilder.build()
resource.metadata.name = "test-resource"
val resource = ConfigMapBuilder()
.withNewMetadata()
.withName("test-configmap")
.endMetadata()
.build()
val createdResource = deployAndGetResource(resource = Gson().toJson(resource))
val createdResource = deployAndGetResource(resource).getResourceSet(server.client)
assertEquals(1, createdResource.size)
assertTrue(createdResource.toMutableSet().first().second is ConfigMap)
assertTrue(createdResource.toMutableSet().first().second.toString().contains(other = resource.metadata.name))
assertTrue(createdResource.toList().first().second is ConfigMap)
assertTrue(createdResource.toList().first().second.toString().contains(other = resource.metadata.name))
}
@Test
fun testLoadExecution() {
val loader = K8sResourceLoaderFromFile(server.client)
val resource = loader.loadK8sResource("Execution", testResourcePath + "test-execution.yaml") as CustomResourceWrapper
val createdResource = deployAndGetResource(resource = Gson().toJson(resource.crAsMap))
val stream = javaClass.getResourceAsStream("/k8s-resource-files/test-execution.yaml")
val execution = this.executionClient.load(stream).get()
val createdResource = deployAndGetResource(execution).getResourceSet(server.client)
assertEquals(1, createdResource.size)
assertTrue(createdResource.toMutableSet().first().second is CustomResourceWrapper)
val loadedResource = createdResource.toList().first().second
assertTrue(loadedResource is ExecutionCRD)
assertEquals("example-execution", loadedResource.metadata.name)
val loadedResource = createdResource.toMutableSet().first().second
if (loadedResource is CustomResourceWrapper){
assertTrue(loadedResource.getName() == "example-execution")
}
}
@Test
fun testLoadBenchmark() {
val loader = K8sResourceLoaderFromFile(server.client)
val resource = loader.loadK8sResource("Benchmark", testResourcePath + "test-benchmark.yaml") as CustomResourceWrapper
val createdResource = deployAndGetResource(resource = Gson().toJson(resource.crAsMap))
val benchmark = BenchmarkCRDummy("example-benchmark").getCR()
val createdResource = deployAndGetResource(benchmark).getResourceSet(server.client)
assertEquals(1, createdResource.size)
assertTrue(createdResource.toMutableSet().first().second is CustomResourceWrapper)
val loadedResource = createdResource.toMutableSet().first().second
if (loadedResource is CustomResourceWrapper){
assertTrue(loadedResource.getName() == "example-benchmark")
}
val loadedResource = createdResource.toList().first().second
assertTrue(loadedResource is BenchmarkCRD)
assertEquals("example-benchmark", loadedResource.metadata.name)
}
@Test
fun testLoadServiceMonitor() {
val loader = K8sResourceLoaderFromFile(server.client)
val resource = loader.loadK8sResource("ServiceMonitor", testResourcePath + "test-service-monitor.yaml") as CustomResourceWrapper
val createdResource = deployAndGetResource(resource = Gson().toJson(resource.crAsMap))
val serviceMonitorContext = ResourceDefinitionContext.Builder()
.withGroup("monitoring.coreos.com")
.withKind("ServiceMonitor")
.withPlural("servicemonitors")
.withNamespaced(true)
.withVersion("v1")
.build()
server.registerResource(serviceMonitorContext)
assertEquals(1, createdResource.size)
assertTrue(createdResource.toMutableSet().first().second is CustomResourceWrapper)
val stream = javaClass.getResourceAsStream("/k8s-resource-files/test-service-monitor.yaml")
val serviceMonitor = server.client.load(stream).get()[0]
val createdResource = deployAndGetResource(serviceMonitor).getResourceSet(server.client)
val loadedResource = createdResource.toMutableSet().first().second
if (loadedResource is CustomResourceWrapper){
assertTrue(loadedResource.getName() == "test-service-monitor")
}
assertEquals(1, createdResource.size)
val loadedResource = createdResource.toList().first().second
assertTrue(loadedResource is GenericKubernetesResource)
assertEquals("ServiceMonitor", loadedResource.kind)
assertEquals("test-service-monitor", loadedResource.metadata.name)
}
@Test
fun testMultipleFiles(){
val resourceBuilder = DeploymentBuilder()
resourceBuilder.withNewSpec().endSpec()
resourceBuilder.withNewMetadata().endMetadata()
val resource = resourceBuilder.build()
resource.metadata.name = "test-deployment"
val resourceBuilder1 = ConfigMapBuilder()
resourceBuilder1.withNewMetadata().endMetadata()
val resource1 = resourceBuilder1.build()
resource1.metadata.name = "test-configmap"
val configMap1 = ConfigMapBuilder()
.withNewMetadata().withName("test-configmap").endMetadata()
.addToData("test-deployment.yaml",Gson().toJson(resource))
.addToData("test-configmap.yaml",Gson().toJson(resource1))
val deployment = DeploymentBuilder()
.withNewSpec()
.endSpec()
.withNewMetadata()
.withName("test-deployment")
.endMetadata()
.build()
val configMap = ConfigMapBuilder()
.withNewMetadata()
.withName("test-configmap")
.endMetadata()
.build()
server.client.configMaps().createOrReplace(configMap1)
val resourceSet = ConfigMapResourceSet()
resourceSet.name = "test-configmap"
val createdResourcesSet = resourceSet.getResourceSet(server.client)
val createdResourceSet = deployAndGetResource(deployment, configMap).getResourceSet(server.client)
assertEquals(2,createdResourcesSet.size )
assert(createdResourcesSet.toMutableList()[0].second is Deployment)
assert(createdResourcesSet.toMutableList()[1].second is ConfigMap)
assertEquals(2, createdResourceSet.size )
assert(createdResourceSet.toList()[0].second is Deployment)
assert(createdResourceSet.toList()[1].second is ConfigMap)
}
@Test
fun testFileIsSet(){
val resourceBuilder = DeploymentBuilder()
resourceBuilder.withNewSpec().endSpec()
resourceBuilder.withNewMetadata().endMetadata()
val resource = resourceBuilder.build()
resource.metadata.name = "test-deployment"
val resourceBuilder1 = ConfigMapBuilder()
resourceBuilder1.withNewMetadata().endMetadata()
val resource1 = resourceBuilder1.build()
resource1.metadata.name = "test-configmap"
val configMap1 = ConfigMapBuilder()
.withNewMetadata().withName("test-configmap").endMetadata()
.addToData("test-deployment.yaml",Gson().toJson(resource))
.addToData("test-configmap.yaml",Gson().toJson(resource1))
fun testFilesRestricted() {
val deployment = DeploymentBuilder()
.withNewSpec()
.endSpec()
.withNewMetadata()
.withName("test-deployment")
.endMetadata()
.build()
val configMap = ConfigMapBuilder()
.withNewMetadata()
.withName("test-configmap")
.endMetadata()
.build()
server.client.configMaps().createOrReplace(configMap1)
val resourceSet = ConfigMapResourceSet()
resourceSet.name = "test-configmap"
resourceSet.files = listOf("test-deployment.yaml")
val createdResourceSet = deployAndGetResource(deployment, configMap)
val allResources = createdResourceSet.getResourceSet(server.client)
assertEquals(2, allResources.size)
createdResourceSet.files = listOf(allResources.first().first) // only select first file from ConfigMa
val resources = createdResourceSet.getResourceSet(server.client)
assertEquals(1, resources.size)
assertTrue(resources.toList().first().second is Deployment)
}
val createdResourcesSet = resourceSet.getResourceSet(server.client)
@Test
fun testFileNotExist() {
val resource = DeploymentBuilder()
.withNewSpec()
.endSpec()
.withNewMetadata()
.withName("test-deployment")
.endMetadata()
.build()
assertEquals(1, createdResourcesSet.size )
assert(createdResourcesSet.toMutableSet().first().second is Deployment)
val resourceSet = deployAndGetResource(resource)
resourceSet.files = listOf("non-existing-file.yaml")
assertThrows<DeploymentFailedException> {
resourceSet.getResourceSet(server.client)
}
}
@Test
fun testConfigMapNotExist() {
val resourceSet = ConfigMapResourceSet()
......