Skip to content
Snippets Groups Projects
Commit 0c976926 authored by Sören Henning's avatar Sören Henning
Browse files

Merge branch '140-add-kdoc' into 'theodolite-kotlin'

Add Kdoc

See merge request !112
parents 7d4443e7 91f2ad4c
No related branches found
No related tags found
4 merge requests!159Re-implementation of Theodolite with Kotlin/Quarkus,!157Update Graal Image in CI pipeline,!112Add Kdoc,!83WIP: Re-implementation of Theodolite with Kotlin/Quarkus
Pipeline #2756 passed
Showing
with 214 additions and 23 deletions
......@@ -21,9 +21,17 @@ private const val RESYNC_PERIOD = 10 * 60 * 1000.toLong()
private const val GROUP = "theodolite.com"
private val logger = KotlinLogging.logger {}
/**
* Implementation of the Operator pattern for K8s.
*
* **See Also:** [Kubernetes Operator Pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/)
*/
class TheodoliteOperator {
private val namespace = System.getenv("NAMESPACE") ?: DEFAULT_NAMESPACE
/**
* Start the operator.
*/
fun start() {
logger.info { "Using $namespace as namespace." }
val client = DefaultKubernetesClient().inNamespace(namespace)
......
......
......@@ -2,8 +2,25 @@ package theodolite.k8s
import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext
/**
* Factory for CustomResourceDefinitionContext
*
* @see CustomResourceDefinitionContext
*/
class K8sContextFactory {
/**
* Create a CustomResourceDefinitionContext.
*
* @param api The K8s API version
* @param scope The scope of the CRD
* @param group The group of the CRD
* @param plural The plural name (kind) of the CRD
*
* @return a new CustomResourceDefinitionContext
*
* @see CustomResourceDefinitionContext
*/
fun create(api: String, scope: String, group: String, plural: String ) : CustomResourceDefinitionContext {
return CustomResourceDefinitionContext.Builder()
.withVersion(api)
......
......
......@@ -7,8 +7,17 @@ import io.fabric8.kubernetes.api.model.apps.Deployment
import io.fabric8.kubernetes.api.model.apps.StatefulSet
import io.fabric8.kubernetes.client.NamespacedKubernetesClient
/**
* This class is used to deploy or remove different Kubernetes resources.
* Supports: Deployments, Services, ConfigMaps, StatefulSets, and CustomResources.
* @param client KubernetesClient used to deploy or remove.
*/
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 ->
......@@ -24,6 +33,10 @@ class K8sManager(private val client: NamespacedKubernetesClient) {
}
}
/**
* Removes different k8s resources using the client.
* @throws IllegalArgumentException if KubernetesResource not supported.
*/
fun remove(resource: KubernetesResource) {
when (resource) {
is Deployment ->
......
......
......@@ -10,12 +10,17 @@ import theodolite.util.YamlParser
private val logger = KotlinLogging.logger {}
/**
* Used to load different Kubernetes resources.
* Supports: Deployments, Services, ConfigMaps, and CustomResources.
* @param client KubernetesClient used to deploy or remove.
*/
class K8sResourceLoader(private val client: NamespacedKubernetesClient) {
/**
* Parses a Service from a service yaml
* @param path of the yaml file
* @return service from fabric8
* @return Service from fabric8
*/
private fun loadService(path: String): Service {
return loadGenericResource(path) { x: String -> client.services().load(x).get() }
......@@ -24,7 +29,7 @@ class K8sResourceLoader(private val client: NamespacedKubernetesClient) {
/**
* Parses a CustomResource from a yaml
* @param path of the yaml file
* @return customResource from fabric8
* @return CustomResource from fabric8
*/
private fun loadServiceMonitor(path: String): ServiceMonitorWrapper {
return loadGenericResource(path) { x: String -> ServiceMonitorWrapper(YamlParser().parse(path, HashMap<String, String>()::class.java)!!) }
......@@ -51,7 +56,8 @@ class K8sResourceLoader(private val client: NamespacedKubernetesClient) {
/**
* Generic helper function to load a resource.
* @param path of the resource
* @param f function that shall be applied to the resource.
* @param f function that is applied to the resource.
* @throws IllegalArgumentException If the resource could not be loaded.
*/
private fun <T> loadGenericResource(path: String, f: (String) -> T): T {
var resource: T? = null
......@@ -69,6 +75,14 @@ class K8sResourceLoader(private val client: NamespacedKubernetesClient) {
return resource
}
/**
* Factory function used to load different k8s resources from a path.
* Supported kinds are: Deployments, Services, ServiceMonitors, ConfigMaps and CustomResources.
* Uses CustomResource as default if Kind is not supported.
* @param kind of the resource. CustomResource as default.
* @param path of the resource to be loaded.
* @throws Exception if the resource could not be loaded.
*/
fun loadK8sResource(kind: String, path: String): KubernetesResource {
return when (kind) {
"Deployment" -> loadDeployment(path)
......
......
......@@ -3,19 +3,19 @@ package theodolite.k8s
import mu.KotlinLogging
import org.apache.kafka.clients.admin.AdminClient
import org.apache.kafka.clients.admin.NewTopic
import java.util.*
private val logger = KotlinLogging.logger {}
/**
* Manages the topics related tasks
* @param kafkaConfig Kafka Configuration as HashMap
* @constructor Creates a KafkaAdminClient
*/
class TopicManager(private val kafkaConfig: HashMap<String, Any>) {
/**
* Creates topics.
* @param newTopics List of all Topic which should be created
* @param newTopics List of all Topic that should be created
*/
fun createTopics(newTopics: Collection<NewTopic>) {
var kafkaAdmin: AdminClient = AdminClient.create(this.kafkaConfig)
......@@ -30,10 +30,9 @@ class TopicManager(private val kafkaConfig: HashMap<String, Any>) {
kafkaAdmin.close()
}
/**
* Removes topics.
* @param topics
* @param topics List of names with the topics to remove.
*/
fun removeTopics(topics: List<String>) {
var kafkaAdmin: AdminClient = AdminClient.create(this.kafkaConfig)
......
......
......@@ -2,6 +2,23 @@ package theodolite.patcher
import io.fabric8.kubernetes.api.model.KubernetesResource
/**
* A Patcher is able to modify values of a Kubernetes resource, see [Patcher].
*
* An AbstractPatcher is created with up to three parameters.
*
* @param k8sResource The Kubernetes resource to be patched.
* @param container *(optional)* The name of the container to be patched
* @param variableName *(optional)* The variable name to be patched
*
*
* **For example** to patch the load dimension of a workload generator, the Patcher should be created as follow:
*
* k8sResource: `uc-1-workload-generator.yaml`
* container: `workload`
* variableName: `NUM_SENSORS`
*
*/
abstract class AbstractPatcher(
k8sResource: KubernetesResource,
container: String? = null,
......
......
......@@ -6,6 +6,13 @@ import io.fabric8.kubernetes.api.model.EnvVarSource
import io.fabric8.kubernetes.api.model.KubernetesResource
import io.fabric8.kubernetes.api.model.apps.Deployment
/**
* The EnvVarPatcher allows to modify the value of an environment variable
*
* @property k8sResource Kubernetes resource to be patched.
* @property container Container to be patched.
* @property variableName Name of the environment variable to be patched.
*/
class EnvVarPatcher(
private val k8sResource: KubernetesResource,
private val container: String,
......
......
......@@ -4,6 +4,12 @@ import io.fabric8.kubernetes.api.model.KubernetesResource
import io.fabric8.kubernetes.api.model.apps.Deployment
import io.fabric8.kubernetes.api.model.apps.StatefulSet
/**
* The Image patcher allows to change the image of a container.
*
* @param k8sResource Kubernetes resource to be patched.
* @param container Container to be patched.
*/
class ImagePatcher(private val k8sResource: KubernetesResource, private val container: String) :
AbstractPatcher(k8sResource, container) {
......
......
......@@ -3,6 +3,12 @@ package theodolite.patcher
import io.fabric8.kubernetes.api.model.KubernetesResource
import io.fabric8.kubernetes.api.model.apps.Deployment
/**
* The Node selector patcher make it possible to set the NodeSelector of a Kubernetes deployment.
*
* @param k8sResource Kubernetes resource to be patched.
* @param variableName The `label-key` of the node for which the `label-value` is to be patched.
*/
class NodeSelectorPatcher(private val k8sResource: KubernetesResource, private val variableName: String) :
AbstractPatcher(k8sResource, variableName) {
override fun <String> patch(value: String) {
......
......
......@@ -2,7 +2,19 @@ package theodolite.patcher
import io.quarkus.runtime.annotations.RegisterForReflection
/**
* A patcher can be used to modify values of Kubernetes resource.
*
* @constructor Create empty Patcher
*/
@RegisterForReflection
interface Patcher {
/**
* The patch method modifies a value in the definition of a
* Kubernetes resource.
*
* @param T The type of value
* @param value The value to be used.
*/
fun <T> patch(value: T)
}
......@@ -3,7 +3,23 @@ package theodolite.patcher
import theodolite.util.PatcherDefinition
import theodolite.util.TypeName
/**
* The PatcherDefinition Factory creates a [PatcherDefinition]s.
*
* @constructor Create empty Patcher definition factory.
*/
class PatcherDefinitionFactory {
/**
* Creates a list of PatcherDefinitions
*
* @param requiredType indicates the required PatcherDefinitions
* (for example `NumSensors`)
* @param patcherTypes list of TypeNames. A TypeName contains a type
* (for example `NumSensors`) and a list of
* PatcherDefinitions, which are related to this type.
* @return A list of PatcherDefinitions which corresponds to the
* value of the requiredType.
*/
fun createPatcherDefinition(requiredType: String, patcherTypes: List<TypeName>): List<PatcherDefinition> {
return patcherTypes
.filter { type -> type.typeName == requiredType }
......
......
......@@ -3,9 +3,29 @@ package theodolite.patcher
import io.fabric8.kubernetes.api.model.KubernetesResource
import theodolite.util.PatcherDefinition
/**
* The Patcher factory creates [Patcher]s
*
* @constructor Creates an empty PatcherFactory.
*/
class PatcherFactory {
fun createPatcher(patcherDefinition: PatcherDefinition,
k8sResources: List<Pair<String, KubernetesResource>>) : Patcher {
/**
* Create patcher based on the given [PatcherDefinition] and
* the list of KubernetesResources.
*
* @param patcherDefinition The [PatcherDefinition] for which are
* [Patcher] should be created.
* @param k8sResources List of all available Kubernetes resources.
* This is a list of pairs<String, KubernetesResource>:
* The frist corresponds to the filename where the resource is defined.
* The second corresponds to the concrete [KubernetesResource] that should be patched.
* @return The created [Patcher].
* @throws IllegalArgumentException if no patcher can be created.
*/
fun createPatcher(
patcherDefinition: PatcherDefinition,
k8sResources: List<Pair<String, KubernetesResource>>
): Patcher {
val resource =
k8sResources.filter { it.first == patcherDefinition.resource }.map { resource -> resource.second }[0]
return when (patcherDefinition.type) {
......
......
......@@ -3,6 +3,11 @@ package theodolite.patcher
import io.fabric8.kubernetes.api.model.KubernetesResource
import io.fabric8.kubernetes.api.model.apps.Deployment
/**
* The Replica [Patcher] modifies the number of replicas for the given Kubernetes deployment.
*
* @param k8sResource Kubernetes resource to be patched.
*/
class ReplicaPatcher(private val k8sResource: KubernetesResource) : AbstractPatcher(k8sResource) {
override fun <String> patch(value: String) {
if (k8sResource is Deployment) {
......
......
......@@ -8,6 +8,13 @@ import io.fabric8.kubernetes.api.model.apps.Deployment
import io.fabric8.kubernetes.api.model.apps.StatefulSet
import java.lang.IllegalArgumentException
/**
* The Resource limit [Patcher] set resource limits for deployments and statefulSets.
*
* @param k8sResource Kubernetes resource to be patched.
* @param container Container to be patched.
* @param limitedResource The resource to be limited (e.g. **cpu or memory**)
*/
class ResourceLimitPatcher(
private val k8sResource: KubernetesResource,
private val container: String,
......
......
......@@ -8,6 +8,13 @@ import io.fabric8.kubernetes.api.model.apps.Deployment
import io.fabric8.kubernetes.api.model.apps.StatefulSet
import java.lang.IllegalArgumentException
/**
* The Resource request [Patcher] set resource limits for deployments and statefulSets.
*
* @param k8sResource Kubernetes resource to be patched.
* @param container Container to be patched.
* @param requestedResource The resource to be requested (e.g. **cpu or memory**)
*/
class ResourceRequestPatcher(
private val k8sResource: KubernetesResource,
private val container: String,
......
......
......@@ -3,6 +3,11 @@ package theodolite.patcher
import io.fabric8.kubernetes.api.model.KubernetesResource
import io.fabric8.kubernetes.api.model.apps.Deployment
/**
* The Scheduler name [Patcher] make it possible to set the scheduler which should be used to deploy the given deployment.
*
* @param k8sResource Kubernetes resource to be patched.
*/
class SchedulerNamePatcher(private val k8sResource: KubernetesResource): Patcher {
override fun <String> patch(value: String) {
if (k8sResource is Deployment) {
......
......
......@@ -8,8 +8,20 @@ import theodolite.strategies.searchstrategy.LinearSearch
import theodolite.strategies.searchstrategy.SearchStrategy
import theodolite.util.Results
/**
* Factory for creating [SearchStrategy] and [RestrictionStrategy] Strategies.
*/
class StrategyFactory {
/**
* Create a [SearchStrategy].
*
* @param executor The [theodolite.execution.BenchmarkExecutor] that executes individual experiments.
* @param searchStrategyString Specifies the [SearchStrategy]. Must either be the string 'LinearSearch',
* or 'BinarySearch'.
*
* @throws IllegalArgumentException if the [SearchStrategy] was not one of the allowed options.
*/
fun createSearchStrategy(executor: BenchmarkExecutor, searchStrategyString: String): SearchStrategy {
return when (searchStrategyString) {
"LinearSearch" -> LinearSearch(executor)
......@@ -18,6 +30,16 @@ class StrategyFactory {
}
}
/**
* Create a [RestrictionStrategy].
*
* @param results The [Results] saves the state of the Theodolite benchmark run.
* @param restrictionStrings Specifies the list of [RestrictionStrategy] that are used to restrict the amount
* of [theodolite.util.Resource] for a fixed LoadDimension. Must equal the string
* 'LowerBound'.
*
* @throws IllegalArgumentException if param searchStrategyString was not one of the allowed options.
*/
fun createRestrictionStrategy(results: Results, restrictionStrings: List<String>): Set<RestrictionStrategy> {
return restrictionStrings
.map { restriction ->
......
......
......@@ -8,10 +8,10 @@ import theodolite.util.Results
* The Lower Bound Restriction sets the lower bound of the resources to be examined to the value
* needed to successfully execute the next smaller load.
*
* @param results Result object used as a basis to restrict the resources.
* @param results [Result] object used as a basis to restrict the resources.
*/
class LowerBoundRestriction(results: Results) : RestrictionStrategy(results) {
override fun next(load: LoadDimension, resources: List<Resource>): List<Resource> {
override fun apply(load: LoadDimension, resources: List<Resource>): List<Resource> {
val maxLoad: LoadDimension? = this.results.getMaxBenchmarkedLoad(load)
var lowerBound: Resource? = this.results.getMinRequiredInstances(maxLoad)
if (lowerBound == null) {
......
......
......@@ -6,18 +6,20 @@ import theodolite.util.Resource
import theodolite.util.Results
/**
* A "Restriction Strategy" restricts a list of resources based on the current
* A 'Restriction Strategy' restricts a list of resources based on the current
* results of all previously performed benchmarks.
*
* @param results the [Results] object
*/
@RegisterForReflection
abstract class RestrictionStrategy(val results: Results) {
/**
* Next Restrict the given resource list for the given load based on the result object.
* Apply the restriction of the given resource list for the given load based on the results object.
*
* @param load Load dimension for which a subset of resources are required.
* @param resources List of resources to be restricted.
* @param load [LoadDimension] for which a subset of resources are required.
* @param resources List of [Resource]s to be restricted.
* @return Returns a list containing only elements that have not been filtered out by the
* restriction (possibly empty).
*/
abstract fun next(load: LoadDimension, resources: List<Resource>): List<Resource>
abstract fun apply(load: LoadDimension, resources: List<Resource>): List<Resource>
}
......@@ -5,7 +5,7 @@ import theodolite.util.LoadDimension
import theodolite.util.Resource
/**
* Search for the smallest suitable resource with binary search.
* Binary-search-like implementation for determining the smallest suitable number of instances.
*
* @param benchmarkExecutor Benchmark executor which runs the individual benchmarks.
*/
......@@ -18,6 +18,14 @@ class BinarySearch(benchmarkExecutor: BenchmarkExecutor) : SearchStrategy(benchm
return resources[result]
}
/**
* Apply binary search.
*
* @param load the load dimension to perform experiments for
* @param resources the list in which binary search is performed
* @param lower lower bound for binary search (inclusive)
* @param upper upper bound for binary search (inclusive)
*/
private fun binarySearch(load: LoadDimension, resources: List<Resource>, lower: Int, upper: Int): Int {
if (lower > upper) {
throw IllegalArgumentException()
......
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment