diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9b10ffeabbc08a1f25a88d2b351f3e8dd6309443..4a412569fbd477f02a0b67b83e8814ab98b34031 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -51,7 +51,7 @@ build-benchmarks: - "theodolite-benchmarks/build/libs/*.jar" - "theodolite-benchmarks/*/build/libs/*.jar" - "theodolite-benchmarks/*/build/distributions/*.tar" - expire_in: 1 day + expire_in: 6 hours test-benchmarks: stage: test @@ -234,7 +234,7 @@ build-theodolite-jvm: paths: - "theodolite-quarkus/build/lib/*" - "theodolite-quarkus/build/*-runner.jar" - expire_in: 1 day + expire_in: 6 hours build-theodolite-native: stage: build @@ -245,7 +245,7 @@ build-theodolite-native: artifacts: paths: - "theodolite-quarkus/build/*-runner" - expire_in: 1 day + expire_in: 6 hours test-theodolite: stage: test @@ -303,6 +303,32 @@ deploy-theodolite: allow_failure: true +# Theodolite SLO Checker: Lag Trend + +deploy-slo-checker-lag-trend: + stage: deploy + extends: + - .dind + script: + - DOCKER_TAG_NAME=$(echo $CI_COMMIT_REF_SLUG- | sed 's/^master-$//') + - docker build --pull -t theodolite-slo-checker-lag-trend slope-evaluator + - "[ ! $CI_COMMIT_TAG ] && docker tag theodolite-slo-checker-lag-trend $CR_HOST/$CR_ORG/theodolite-slo-checker-lag-trend:${DOCKER_TAG_NAME}latest" + - "[ $CI_COMMIT_TAG ] && docker tag theodolite-slo-checker-lag-trend $CR_HOST/$CR_ORG/theodolite-slo-checker-lag-trend:$CI_COMMIT_TAG" + - echo $CR_PW | docker login $CR_HOST -u $CR_USER --password-stdin + - docker push $CR_HOST/$CR_ORG/theodolite-slo-checker-lag-trend + - docker logout + rules: + - if: "$CR_HOST && $CR_ORG && $CR_USER && $CR_PW && $CI_COMMIT_TAG" + when: always + - changes: + - slope-evaluator/**/* + if: "$CR_HOST && $CR_ORG && $CR_USER && $CR_PW" + when: always + - if: "$CR_HOST && $CR_ORG && $CR_USER && $CR_PW" + when: manual + allow_failure: true + + # Theodolite Random Scheduler deploy-random-scheduler: diff --git a/execution/helm/README.md b/execution/helm/README.md index 4cacd06c8181970e78cb4f62e93b77fa169fcdfa..c545804aaec8eb8ed91054f1f7ee97dd293816a4 100644 --- a/execution/helm/README.md +++ b/execution/helm/README.md @@ -6,11 +6,9 @@ Install the chart via: ```sh helm dependencies update . -helm install my-confluent . +helm install theodolite . ``` -**Please note: Theodolite currently uses hard-coded URLs, to connect to Kafka and Zookeeper. For that reason, the name of this chart must be `my-confluent`.** We will change this behavior soon. - This chart installs requirements to execute benchmarks with Theodolite. Dependencies and subcharts: @@ -27,7 +25,7 @@ Dependencies and subcharts: Test the installation: ```sh -helm test <release-name> +helm test theodolite ``` Our test files are located [here](templates/../../theodolite-chart/templates/tests). Many subcharts have their own tests, these are also executed and are placed in the respective /templates folders. @@ -44,6 +42,31 @@ In development environments Kubernetes resources are often low. To reduce resour helm install theodolite . -f preconfigs/one-broker-values.yaml ``` +## Uninstall this Chart + +To uninstall/delete the `theodolite` deployment: + +```sh +helm delete theodolite +``` + +This command does not remove the CRDs which are created by this chart. Remove them manually with: + +```sh +# CRDs from Theodolite +kubectl delete crd executions.theodolite.com +kubectl delete crd benchmarks.theodolite.com +# CRDs from Prometheus operator (see https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack#uninstall-chart) +kubectl delete crd alertmanagerconfigs.monitoring.coreos.com +kubectl delete crd alertmanagers.monitoring.coreos.com +kubectl delete crd podmonitors.monitoring.coreos.com +kubectl delete crd probes.monitoring.coreos.com +kubectl delete crd prometheuses.monitoring.coreos.com +kubectl delete crd prometheusrules.monitoring.coreos.com +kubectl delete crd servicemonitors.monitoring.coreos.com +kubectl delete crd thanosrulers.monitoring.coreos.com +``` + ## Development **Hints**: diff --git a/execution/helm/preconfigs/one-broker-values.yaml b/execution/helm/preconfigs/one-broker-values.yaml index fdbc3207ee37f49cf176645851d91e62ba354d28..c53c1f1eb8bc7a17f192d70a6f10f8cacc09c98f 100644 --- a/execution/helm/preconfigs/one-broker-values.yaml +++ b/execution/helm/preconfigs/one-broker-values.yaml @@ -9,7 +9,7 @@ cp-helm-charts: ## Kafka ## ------------------------------------------------------ cp-kafka: - brokers: 1 # deauflt: 10 + brokers: 1 # default: 10 configurationOverrides: offsets.topic.replication.factor: "1" \ No newline at end of file diff --git a/execution/helm/templates/theodolite/crd-benchmark.yaml b/execution/helm/templates/theodolite/crd-benchmark.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9d7468b490fa2f2a6cf829bdcafab8c4bd6fc5bf --- /dev/null +++ b/execution/helm/templates/theodolite/crd-benchmark.yaml @@ -0,0 +1,15 @@ +{{- if .Values.benchmarkCRD.create -}} +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: benchmarks.theodolite.com +spec: + group: theodolite.com + version: v1alpha1 + names: + kind: benchmark + plural: benchmarks + scope: Namespaced + subresources: + status: {} +{{- end }} \ No newline at end of file diff --git a/execution/helm/templates/theodolite/crd-execution.yaml b/execution/helm/templates/theodolite/crd-execution.yaml new file mode 100644 index 0000000000000000000000000000000000000000..73b58397b8c1fc15ffef5da74e8f1dbdabaa3a30 --- /dev/null +++ b/execution/helm/templates/theodolite/crd-execution.yaml @@ -0,0 +1,15 @@ +{{- if .Values.executionCRD.create -}} +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: executions.theodolite.com +spec: + group: theodolite.com + version: v1alpha1 + names: + kind: execution + plural: executions + scope: Namespaced + subresources: + status: {} +{{- end }} \ No newline at end of file diff --git a/execution/helm/templates/theodolite/role-binding.yaml b/execution/helm/templates/theodolite/role-binding.yaml new file mode 100644 index 0000000000000000000000000000000000000000..93d8c34e7bc544c3b0c231e986bc58c792cce38e --- /dev/null +++ b/execution/helm/templates/theodolite/role-binding.yaml @@ -0,0 +1,15 @@ +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: RoleBinding +metadata: + name: {{ include "theodolite.fullname" . }} + labels: + app: {{ include "theodolite.name" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "theodolite.fullname" . }} +subjects: +- kind: ServiceAccount + name: {{ include "theodolite.serviceAccountName" . }} +{{- end }} \ No newline at end of file diff --git a/execution/helm/templates/theodolite/role.yaml b/execution/helm/templates/theodolite/role.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ee699a5b861c64e355ca2ba44330a7d600756b77 --- /dev/null +++ b/execution/helm/templates/theodolite/role.yaml @@ -0,0 +1,60 @@ +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "theodolite.fullname" . }} +rules: + - apiGroups: + - apps + resources: + - deployments + verbs: + - delete + - list + - get + - create + - update + - apiGroups: + - "" + resources: + - services + - pods + - configmaps + verbs: + - update + - delete + - list + - get + - create + - apiGroups: + - "" + resources: + - pods/exec + verbs: + - create + - get + - apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - update + - delete + - list + - create + {{- if .Values.operator.enabled }} + - apiGroups: + - theodolite.com + resources: + - executions + - benchmarks + verbs: + - delete + - list + - get + - create + - watch + - update + - patch + {{- end }} +{{- end }} \ No newline at end of file diff --git a/execution/helm/templates/theodolite/serviceaccount.yaml b/execution/helm/templates/theodolite/serviceaccount.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4585b8ce413bf3d36cb986163788c353f2a4a2de --- /dev/null +++ b/execution/helm/templates/theodolite/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "theodolite.serviceAccountName" . }} + labels: + {{- include "theodolite.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/execution/helm/templates/theodolite/thedolite-operator.yaml b/execution/helm/templates/theodolite/thedolite-operator.yaml new file mode 100644 index 0000000000000000000000000000000000000000..3af9cfffd71059993688d5be77ae110a2e3cdc2d --- /dev/null +++ b/execution/helm/templates/theodolite/thedolite-operator.yaml @@ -0,0 +1,41 @@ +{{- if .Values.operator.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "theodolite.fullname" . }}-operator +spec: + selector: + matchLabels: + app: {{ include "theodolite.fullname" . }} + replicas: 1 + template: + metadata: + labels: + app: {{ include "theodolite.fullname" . }} + spec: + terminationGracePeriodSeconds: 0 + serviceAccountName: {{ include "theodolite.serviceAccountName" . }} + containers: + - name: theodolite + image: ghcr.io/cau-se/theodolite:theodolite-kotlin-latest + env: + - name: NAMESPACE + value: {{ .Release.Namespace }} + - name: MODE + value: operator + - name: THEODOLITE_APP_RESOURCES + value: "./benchmark-resources" + volumeMounts: + - name: benchmark-resources + mountPath: /work/benchmark-resources + - name: lag-analysis + image: ghcr.io/cau-se/theodolite-slo-checker-lag-trend:theodolite-kotlin-latest + ports: + - containerPort: 80 + name: analysis + volumes: + - name: benchmark-resources + configMap: + name: benchmark-resources + optional: true +{{- end }} diff --git a/execution/helm/values.yaml b/execution/helm/values.yaml index d35912de6ffc72fff27f5d389c94761bc80eecd1..67dab74c3931ce13a1ab0f7504a946a208b4dfb8 100644 --- a/execution/helm/values.yaml +++ b/execution/helm/values.yaml @@ -24,6 +24,9 @@ grafana: # Administrator credentials when not using an existing secret (see below) adminUser: admin adminPassword: admin + grafana.ini: + users: + default_theme: light ## Sidecars that collect the configmaps with specified label and stores the included files them into the respective folders ## Requires at least Grafana 5 to work and can't be used together with parameters dashboardProviders, datasources and dashboards sidecar: @@ -145,8 +148,8 @@ kafka-lag-exporter: enabled: true nodeSelector: {} clusters: - - name: "my-confluent-cp-kafka" - bootstrapBrokers: "my-confluent-cp-kafka:9092" + - name: "theodolite-cp-kafka" + bootstrapBrokers: "theodolite-cp-kafka:9092" ## The interval between refreshing metrics pollIntervalSeconds: 15 @@ -226,4 +229,22 @@ prometheus: clusterRole: enabled: true clusterRoleBinding: - enabled: true \ No newline at end of file + enabled: true + +### +# Theodolite Operator +### +operator: + enabled: true + +executionCRD: + create: true + +benchmarkCRD: + create: true + +serviceAccount: + create: true + +rbac: + create: true diff --git a/execution/infrastructure/kubernetes/rbac/role.yaml b/execution/infrastructure/kubernetes/rbac/role.yaml index 84ba14a8bc7a6eceb8a20596ede057ca2271b967..a21fd554f0f56b3955e9a9b6cf4bf95442b5d7af 100644 --- a/execution/infrastructure/kubernetes/rbac/role.yaml +++ b/execution/infrastructure/kubernetes/rbac/role.yaml @@ -38,4 +38,18 @@ rules: verbs: - delete - list - - create \ No newline at end of file + - create + - apiGroups: + - theodolite.com + resources: + - executions + - benchmarks + verbs: + - delete + - list + - get + - create + - watch + - update + - patch + diff --git a/execution/uc-application/aggregation-deployment.yaml b/execution/uc-application/aggregation-deployment.yaml index 07732ca1dd1e6b2b06f098dfb10a53d38e8d5cae..1d3ebdb20dd06433e97e112edef76d7deac17395 100644 --- a/execution/uc-application/aggregation-deployment.yaml +++ b/execution/uc-application/aggregation-deployment.yaml @@ -21,9 +21,9 @@ spec: name: jmx env: - name: KAFKA_BOOTSTRAP_SERVERS - value: "my-confluent-cp-kafka:9092" + value: "theodolite-cp-kafka:9092" - name: SCHEMA_REGISTRY_URL - value: "http://my-confluent-cp-schema-registry:8081" + value: "http://theodolite-cp-schema-registry:8081" - name: JAVA_OPTS value: "-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=5555" - name: COMMIT_INTERVAL_MS # Set as default for the applications diff --git a/execution/uc-workload-generator/workloadGenerator.yaml b/execution/uc-workload-generator/workloadGenerator.yaml index 146e285f66d4c0e1a88d613e4ac2d5571234fad6..8f7cc3a8df20752eccb321242bb774c18f4e1d0a 100644 --- a/execution/uc-workload-generator/workloadGenerator.yaml +++ b/execution/uc-workload-generator/workloadGenerator.yaml @@ -32,6 +32,6 @@ spec: - name: KUBERNETES_DNS_NAME value: "titan-ccp-load-generator.$(KUBERNETES_NAMESPACE).svc.cluster.local" - name: KAFKA_BOOTSTRAP_SERVERS - value: "my-confluent-cp-kafka:9092" + value: "theodolite-cp-kafka:9092" - name: SCHEMA_REGISTRY_URL - value: "http://my-confluent-cp-schema-registry:8081" + value: "http://theodolite-cp-schema-registry:8081" diff --git a/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.flink.gradle b/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.flink.gradle index f9c1d98d9f88a95bdc3fa25e7c1bec2f3c9bddb4..d6d5217667a73a1529d73ac59260bcf47d8cf2e1 100644 --- a/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.flink.gradle +++ b/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.flink.gradle @@ -6,6 +6,23 @@ plugins { applicationDefaultJvmArgs = ["-Dlog4j.configuration=log4j.properties"] + +run.classpath = sourceSets.main.runtimeClasspath + +jar { + manifest { + attributes 'Built-By': System.getProperty('user.name'), + 'Build-Jdk': System.getProperty('java.version') + } +} + +shadowJar { + configurations = [project.configurations.compile] + zip64 true +} + +tasks.distZip.enabled = false + ext { flinkVersion = '1.12.2' scalaBinaryVersion = '2.12' @@ -48,17 +65,3 @@ dependencies { // Use JUnit test framework testImplementation 'junit:junit:4.12' } - -run.classpath = sourceSets.main.runtimeClasspath - -jar { - manifest { - attributes 'Built-By': System.getProperty('user.name'), - 'Build-Jdk': System.getProperty('java.version') - } -} - -shadowJar { - configurations = [project.configurations.compile] - zip64 true -} diff --git a/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.kstreams.gradle b/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.kstreams.gradle index c6779fbc4348a8d665776e68688858ab3d2f4146..eece7b835ae9d6f39283ea371ce8b0b8194cdaa0 100644 --- a/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.kstreams.gradle +++ b/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.kstreams.gradle @@ -6,6 +6,8 @@ plugins { id 'application' } +tasks.distZip.enabled = false + repositories { jcenter() maven { diff --git a/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.load-generator.gradle b/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.load-generator.gradle index 13b7ea191d11c942cd0ca58b882ffda7bc7912be..c6c2b6057cf35c32faa4d67b6ea6dba9e5c13beb 100644 --- a/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.load-generator.gradle +++ b/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.load-generator.gradle @@ -6,6 +6,8 @@ plugins { id 'application' } +tasks.distZip.enabled = false + repositories { jcenter() maven { diff --git a/theodolite-quarkus/config/aggregation-service.yaml b/theodolite-quarkus/config/aggregation-service.yaml index 916dd6677d60370b1d62e5d7e708c3ee966bda23..85432d04f225c30469f3232153ef6bd72bd02bdf 100644 --- a/theodolite-quarkus/config/aggregation-service.yaml +++ b/theodolite-quarkus/config/aggregation-service.yaml @@ -14,4 +14,4 @@ spec: targetPort: 80 protocol: TCP - name: metrics - port: 9980 + port: 5556 diff --git a/theodolite-quarkus/config/example-execution-yaml-resource.yaml b/theodolite-quarkus/config/example-execution-yaml-resource.yaml index 80e4575d653bff8d67d8f66cf4ae9806a1fe102e..23c1587ec1e5c2a88fcf69d7127edbcc1ffdb00f 100644 --- a/theodolite-quarkus/config/example-execution-yaml-resource.yaml +++ b/theodolite-quarkus/config/example-execution-yaml-resource.yaml @@ -2,36 +2,34 @@ name: example-execution benchmark: "uc1-kstreams" load: loadType: "NumSensors" - loadValues: - - 50000 + loadValues: [25000, 50000, 75000, 100000, 125000, 150000] resources: resourceType: "Instances" - resourceValues: - - 1 + resourceValues: [1, 2, 3, 4, 5] slos: - sloType: "lag trend" - threshold: 1000 + threshold: 2000 prometheusUrl: "http://prometheus-operated:9090" externalSloUrl: "http://localhost:80/evaluate-slope" offset: 0 - warmup: 0 + warmup: 60 # in seconds execution: strategy: "LinearSearch" - duration: 60 + duration: 300 # in seconds repetitions: 1 restrictions: - "LowerBound" -configOverrides: - - patcher: - type: "NodeSelectorPatcher" - resource: "uc1-load-generator-deployment.yaml" - variableName: "env" - value: "prod" - - patcher: - type: "NodeSelectorPatcher" - resource: "uc1-kstreams-deployment.yaml" - variableName: "env" - value: "prod" +configOverrides: [] +# - patcher: +# type: "NodeSelectorPatcher" +# resource: "uc1-load-generator-deployment.yaml" +# variableName: "env" +# value: "prod" +# - patcher: +# type: "NodeSelectorPatcher" +# resource: "uc1-kstreams-deployment.yaml" +# variableName: "env" +# value: "prod" # - patcher: # type: "ResourceLimitPatcher" # resource: "uc1-kstreams-deployment.yaml" diff --git a/theodolite-quarkus/config/example-operator-benchmark.yaml b/theodolite-quarkus/config/example-operator-benchmark.yaml new file mode 100644 index 0000000000000000000000000000000000000000..419042fdd0b1e58fed4d402b4bb329d54602d23f --- /dev/null +++ b/theodolite-quarkus/config/example-operator-benchmark.yaml @@ -0,0 +1,31 @@ +apiVersion: theodolite.com/v1alpha1 +kind: benchmark +metadata: + name: uc1-kstreams +#name: "uc1-kstreams" +appResource: + - "uc1-kstreams-deployment.yaml" + - "aggregation-service.yaml" + - "jmx-configmap.yaml" + - "uc1-service-monitor.yaml" +loadGenResource: + - "uc1-load-generator-deployment.yaml" + - "uc1-load-generator-service.yaml" +resourceTypes: + - typeName: "Instances" + patchers: + - type: "ReplicaPatcher" + resource: "uc1-kstreams-deployment.yaml" +loadTypes: + - typeName: "NumSensors" + patchers: + - type: "EnvVarPatcher" + resource: "uc1-load-generator-deployment.yaml" + container: "workload-generator" + variableName: "NUM_SENSORS" +kafkaConfig: + bootstrapServer: "theodolite-cp-kafka:9092" + topics: + - name: "input" + numPartitions: 40 + replicationFactor: 1 \ No newline at end of file diff --git a/theodolite-quarkus/config/example-operator-execution.yaml b/theodolite-quarkus/config/example-operator-execution.yaml new file mode 100644 index 0000000000000000000000000000000000000000..3df1a723dd771453ab1b267335176e4ae74c3ed5 --- /dev/null +++ b/theodolite-quarkus/config/example-operator-execution.yaml @@ -0,0 +1,52 @@ +apiVersion: theodolite.com/v1alpha1 +kind: execution +metadata: + name: example-execution +#name: example-execution +benchmark: "uc1-kstreams" +load: + loadType: "NumSensors" + loadValues: [25000, 50000, 75000, 100000, 125000, 150000] +resources: + resourceType: "Instances" + resourceValues: [1, 2, 3, 4, 5] +slos: + - sloType: "lag trend" + threshold: 2000 + prometheusUrl: "http://prometheus-operated:9090" + externalSloUrl: "http://localhost:80/evaluate-slope" + offset: 0 + warmup: 60 # in seconds +execution: + strategy: "LinearSearch" + duration: 300 # in seconds + repetitions: 1 + restrictions: + - "LowerBound" +configOverrides: [] +# - patcher: +# type: "NodeSelectorPatcher" +# resource: "uc1-load-generator-deployment.yaml" +# variableName: "env" +# value: "prod" +# - patcher: +# type: "NodeSelectorPatcher" +# resource: "uc1-kstreams-deployment.yaml" +# variableName: "env" +# value: "prod" +# - patcher: +# type: "ResourceLimitPatcher" +# resource: "uc1-kstreams-deployment.yaml" +# container: "uc-application" +# variableName: "cpu" +# value: "1000m" +# - patcher: +# type: "ResourceLimitPatcher" +# resource: "uc1-kstreams-deployment.yaml" +# container: "uc-application" +# variableName: "memory" +# value: "2Gi" +# - patcher: +# type: "SchedulerNamePatcher" +# resource: "uc1-kstreams-deployment.yaml" +# value: "random-scheduler" diff --git a/theodolite-quarkus/config/thedolite-operator.yaml b/theodolite-quarkus/config/thedolite-operator.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1e0e60248c2474cc8493179c003b806030f79f8c --- /dev/null +++ b/theodolite-quarkus/config/thedolite-operator.yaml @@ -0,0 +1,24 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: theodolite +spec: + selector: + matchLabels: + app: theodolite + replicas: 1 + template: + metadata: + labels: + app: theodolite + spec: + terminationGracePeriodSeconds: 0 + serviceAccountName: theodolite + containers: + - name: thedolite + image: ghcr.io/cau-se/theodolite:theodolite-kotlin-latest + env: + - name: KUBECONFIG + value: "~/.kube/config" + - name: NAMESPACE + value: "default" \ No newline at end of file diff --git a/theodolite-quarkus/config/uc1-kstreams-deployment.yaml b/theodolite-quarkus/config/uc1-kstreams-deployment.yaml index 85fe243714f9523e760ad827ef697b3dcefdc8e3..171c3446db2719ee91bd8954233015316851fcf9 100644 --- a/theodolite-quarkus/config/uc1-kstreams-deployment.yaml +++ b/theodolite-quarkus/config/uc1-kstreams-deployment.yaml @@ -21,9 +21,9 @@ spec: name: jmx env: - name: KAFKA_BOOTSTRAP_SERVERS - value: "my-confluent-cp-kafka:9092" + value: "theodolite-cp-kafka:9092" - name: SCHEMA_REGISTRY_URL - value: "http://my-confluent-cp-schema-registry:8081" + value: "http://theodolite-cp-schema-registry:8081" - name: JAVA_OPTS value: "-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=5555" - name: COMMIT_INTERVAL_MS # Set as default for the applications diff --git a/theodolite-quarkus/config/uc1-load-generator-deployment.yaml b/theodolite-quarkus/config/uc1-load-generator-deployment.yaml index e49c22d6a478fbd7f3f5987222c76ca73a661cb0..374dd60113e133ef0a793149e3786efb38973287 100644 --- a/theodolite-quarkus/config/uc1-load-generator-deployment.yaml +++ b/theodolite-quarkus/config/uc1-load-generator-deployment.yaml @@ -31,6 +31,6 @@ spec: - name: KUBERNETES_DNS_NAME value: "titan-ccp-load-generator.$(KUBERNETES_NAMESPACE).svc.cluster.local" - name: KAFKA_BOOTSTRAP_SERVERS - value: "my-confluent-cp-kafka:9092" + value: "theodolite-cp-kafka:9092" - name: SCHEMA_REGISTRY_URL - value: "http://my-confluent-cp-schema-registry:8081" + value: "http://theodolite-cp-schema-registry:8081" diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt index a2d649ab38f4c0fda31d7c99d687cc253efe1ea8..36fd00795e3f075ca1fbd171fa59339df8151f85 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt @@ -11,6 +11,7 @@ import kotlin.properties.Delegates @JsonDeserialize @RegisterForReflection class BenchmarkExecution : CustomResource(), Namespaced { + var executionId: Int = 0 lateinit var name: String lateinit var benchmark: String lateinit var load: LoadDefinition diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt index 4f630ff6b5da77d4933c77e8741926ac0c37cdf1..a348deb9ff2acf592458635712b99c8d23fb0577 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt @@ -21,19 +21,11 @@ class KubernetesBenchmark : Benchmark, CustomResource(), Namespaced { lateinit var resourceTypes: List<TypeName> lateinit var loadTypes: List<TypeName> lateinit var kafkaConfig: KafkaConfig - lateinit var namespace: String - lateinit var path: String + private val namespace = System.getenv("NAMESPACE") ?: DEFAULT_NAMESPACE + var path = System.getenv("THEODOLITE_APP_RESOURCES") ?: "./config" private fun loadKubernetesResources(resources: List<String>): List<Pair<String, KubernetesResource>> { val parser = YamlParser() - - namespace = System.getenv("NAMESPACE") ?: DEFAULT_NAMESPACE - logger.info { "Using $namespace as namespace." } - - path = System.getenv("THEODOLITE_APP_RESOURCES") ?: "./config" - logger.info { "Using $path as path for resources." } - - val loader = K8sResourceLoader(DefaultKubernetesClient().inNamespace(namespace)) return resources .map { resource -> @@ -49,6 +41,9 @@ class KubernetesBenchmark : Benchmark, CustomResource(), Namespaced { res: Resource, configurationOverrides: List<ConfigurationOverride?> ): BenchmarkDeployment { + logger.info { "Using $namespace as namespace." } + logger.info { "Using $path as resource path." } + val resources = loadKubernetesResources(this.appResource + this.loadGenResource) val patcherFactory = PatcherFactory() @@ -66,12 +61,12 @@ class KubernetesBenchmark : Benchmark, CustomResource(), Namespaced { patcherFactory.createPatcher(it.patcher, resources).patch(override.value) } } - return KubernetesBenchmarkDeployment( namespace = namespace, resources = resources.map { r -> r.second }, kafkaConfig = hashMapOf("bootstrap.servers" to kafkaConfig.bootstrapServer), - topics = kafkaConfig.getKafkaTopics() + topics = kafkaConfig.getKafkaTopics(), + client = DefaultKubernetesClient().inNamespace(namespace) ) } } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkDeployment.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkDeployment.kt index 238cb17071c5b48fc883808b4334b79dada7ee32..8e4fce681147ffe7c80b37bab1d0dbb094b4036e 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkDeployment.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkDeployment.kt @@ -1,7 +1,7 @@ package theodolite.benchmark 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 org.apache.kafka.clients.admin.NewTopic import theodolite.k8s.K8sManager @@ -12,13 +12,12 @@ class KubernetesBenchmarkDeployment( val namespace: String, val resources: List<KubernetesResource>, private val kafkaConfig: HashMap<String, Any>, - private val topics: Collection<NewTopic> + private val topics: Collection<NewTopic>, + private val client: NamespacedKubernetesClient ) : BenchmarkDeployment { private val kafkaController = TopicManager(this.kafkaConfig) - private val kubernetesManager = K8sManager(DefaultKubernetesClient().inNamespace(namespace)) - + private val kubernetesManager = K8sManager(client) private val LABEL = "app.kubernetes.io/name=kafka-lag-exporter" - private val client = DefaultKubernetesClient().inNamespace(namespace) override fun setup() { kafkaController.createTopics(this.topics) diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt index ce63f23a31840b8e47cb15b71a026d35e438e953..0b6c681c9123adace08bc60946a067442ca2baa1 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt @@ -9,7 +9,10 @@ import java.time.Instant private val logger = KotlinLogging.logger {} -class AnalysisExecutor(private val slo: BenchmarkExecution.Slo) { +class AnalysisExecutor( + private val slo: BenchmarkExecution.Slo, + private val executionId: Int +) { private val fetcher = MetricFetcher( prometheusURL = slo.prometheusUrl, @@ -26,8 +29,7 @@ class AnalysisExecutor(private val slo: BenchmarkExecution.Slo) { query = "sum by(group)(kafka_consumergroup_group_lag >= 0)" ) - CsvExporter().toCsv(name = "${load.get()}_${res.get()}_${slo.sloType}", prom = prometheusData) - + CsvExporter().toCsv(name = "$executionId-${load.get()}-${res.get()}-${slo.sloType}", prom = prometheusData) val sloChecker = SloCheckerFactory().create( slotype = slo.sloType, externalSlopeURL = slo.externalSloUrl, diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt index 9332af89810d24f87016fd02ce56d35a5ab07ca5..a6eae8bbfb1273b90e3d457db3287d6fc1546fbe 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt @@ -25,7 +25,8 @@ abstract class BenchmarkExecutor( val results: Results, val executionDuration: Duration, configurationOverrides: List<ConfigurationOverride?>, - val slo: BenchmarkExecution.Slo + val slo: BenchmarkExecution.Slo, + val executionId: Int ) { var run: AtomicBoolean = AtomicBoolean(true) diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt index 027b9328336adcfc66299c94335a895d48a7181f..03c66e757abab9de69bc97ff98a67fffdafdf3d1 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt @@ -19,8 +19,9 @@ class BenchmarkExecutorImpl( results: Results, executionDuration: Duration, private val configurationOverrides: List<ConfigurationOverride?>, - slo: BenchmarkExecution.Slo -) : BenchmarkExecutor(benchmark, results, executionDuration, configurationOverrides, slo) { + slo: BenchmarkExecution.Slo, + executionId: Int +) : BenchmarkExecutor(benchmark, results, executionDuration, configurationOverrides, slo, executionId) { override fun runExperiment(load: LoadDimension, res: Resource): Boolean { var result = false val benchmarkDeployment = benchmark.buildDeployment(load, res, this.configurationOverrides) @@ -36,7 +37,7 @@ class BenchmarkExecutorImpl( if (this.run.get()) { result = - AnalysisExecutor(slo = slo).analyze(load = load, res = res, executionDuration = executionDuration) + AnalysisExecutor(slo = slo, executionId = executionId).analyze(load = load, res = res, executionDuration = executionDuration) this.results.setResult(Pair(load, res), result) } benchmarkDeployment.teardown() diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/Main.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/Main.kt index 3246d0a7930aab22d16ed94a6c0f10a9d3fde10e..4518ef7957104819b26eae95cf4e6e9b35c4e995 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/Main.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/Main.kt @@ -13,7 +13,7 @@ object Main { @JvmStatic fun main(args: Array<String>) { - val mode = System.getenv("MODE") ?: "operator" + val mode = System.getenv("MODE") ?: "yaml-executor" logger.info { "Start Theodolite with mode $mode" } when(mode) { diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt index 589dee1ac2e39d107794df1e3aa85ea56d6db92b..92aaef2923ed87e2bb2c3706ac4fc862538a222f 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt @@ -23,4 +23,4 @@ class Shutdown(private val benchmarkExecution: BenchmarkExecution, private val b deployment.teardown() logger.info { "Teardown completed" } } -} \ No newline at end of file +} diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt index 3d7718214e15488ad8c0d7c1cea7441474653571..0c3e0d2fd6d1f40df5299c64b214d84323cba853 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt @@ -1,5 +1,6 @@ package theodolite.execution +import com.google.gson.GsonBuilder import theodolite.benchmark.BenchmarkExecution import theodolite.benchmark.KubernetesBenchmark import theodolite.patcher.PatcherDefinitionFactory @@ -9,6 +10,7 @@ import theodolite.util.Config import theodolite.util.LoadDimension import theodolite.util.Resource import theodolite.util.Results +import java.io.PrintWriter import java.time.Duration class TheodoliteExecutor( @@ -37,11 +39,12 @@ class TheodoliteExecutor( executor = BenchmarkExecutorImpl( - kubernetesBenchmark, - results, - executionDuration, - config.configOverrides, - config.slos[0] + benchmark = kubernetesBenchmark, + results = results, + executionDuration = executionDuration, + configurationOverrides = config.configOverrides, + slo = config.slos[0], + executionId = config.executionId ) return Config( @@ -72,6 +75,9 @@ class TheodoliteExecutor( } fun run() { + storeAsFile(this.config, "${this.config.executionId}-execution-configuration") + storeAsFile(kubernetesBenchmark, "${this.config.executionId}-benchmark-configuration") + val config = buildConfig() // execute benchmarks for each load for (load in config.loads) { @@ -79,5 +85,14 @@ class TheodoliteExecutor( config.compositeStrategy.findSuitableResource(load, config.resources) } } + storeAsFile(config.compositeStrategy.benchmarkExecutor.results, "${this.config.executionId}-result") + } + + private fun <T> storeAsFile(saveObject: T, filename: String) { + val gson = GsonBuilder().enableComplexMapKeySerialization().setPrettyPrinting().create() + + PrintWriter(filename).use { pw -> + pw.println(gson.toJson(saveObject)) + } } } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteYamlExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteYamlExecutor.kt index f35e43a8f95d5fc20a9000af7b55d95881694804..6bddea20c05fb5c0eb6a5a3bd60b3ec2c6b9bd5d 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteYamlExecutor.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteYamlExecutor.kt @@ -17,11 +17,9 @@ class TheodoliteYamlExecutor { val executionPath = System.getenv("THEODOLITE_EXECUTION") ?: "./config/BenchmarkExecution.yaml" val benchmarkPath = System.getenv("THEODOLITE_BENCHMARK") ?: "./config/BenchmarkType.yaml" - val appResource = System.getenv("THEODOLITE_APP_RESOURCES") ?: "./config" logger.info { "Using $executionPath for BenchmarkExecution" } logger.info { "Using $benchmarkPath for BenchmarkType" } - logger.info { "Using $appResource for Resources" } // load the BenchmarkExecution and the BenchmarkType @@ -29,15 +27,16 @@ class TheodoliteYamlExecutor { parser.parse(path = executionPath, E = BenchmarkExecution::class.java)!! val benchmark = parser.parse(path = benchmarkPath, E = KubernetesBenchmark::class.java)!! - benchmark.path = appResource - val shutdown = Shutdown(benchmarkExecution, benchmark) - Runtime.getRuntime().addShutdownHook(thread { shutdown.run()}) + // Add shutdown hook + // Use thread{} with start = false, else the thread will start right away + val shutdown = thread(start = false) { Shutdown(benchmarkExecution, benchmark).run() } + Runtime.getRuntime().addShutdownHook(shutdown) val executor = TheodoliteExecutor(benchmarkExecution, benchmark) executor.run() logger.info { "Theodolite finished" } - Runtime.getRuntime().removeShutdownHook(thread { shutdown.run()}) + Runtime.getRuntime().removeShutdownHook(shutdown) exitProcess(0) } } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionEventHandler.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionEventHandler.kt index 1752ac112ea84ea179e238f7ab8d808779014d1b..0152cd7bef808d4652cd893fb282e0cb8b18dd5a 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionEventHandler.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionEventHandler.kt @@ -3,6 +3,7 @@ package theodolite.execution.operator import io.fabric8.kubernetes.client.informers.ResourceEventHandler import mu.KotlinLogging import theodolite.benchmark.BenchmarkExecution +import java.lang.NullPointerException private val logger = KotlinLogging.logger {} @@ -16,17 +17,25 @@ class ExecutionHandler(private val controller: TheodoliteController): ResourceEv override fun onUpdate(oldExecution: BenchmarkExecution, newExecution: BenchmarkExecution) { logger.info { "Add updated execution to queue." } newExecution.name = newExecution.metadata.name - this.controller.executionsQueue.removeIf { e -> e.name == newExecution.metadata.name } + try { + this.controller.executionsQueue.removeIf { e -> e.name == newExecution.metadata.name } + } catch(e: NullPointerException) { + logger.warn { "No execution found for deletion" } + } this.controller.executionsQueue.addFirst(newExecution) - if (this.controller.isInitialized() && this.controller.executor.getExecution().name == newExecution.metadata.name) { + if (this.controller.isInitialized() && this.controller.executor.getExecution().name == newExecution.metadata.name) { this.controller.isUpdated.set(true) this.controller.executor.executor.run.compareAndSet(true, false) } } override fun onDelete(execution: BenchmarkExecution, b: Boolean) { - logger.info { "Delete execution ${execution.metadata.name} from queue." } - this.controller.executionsQueue.removeIf { e -> e.name == execution.metadata.name } + try { + this.controller.executionsQueue.removeIf { e -> e.name == execution.metadata.name } + logger.info { "Delete execution ${execution.metadata.name} from queue." } + } catch(e: NullPointerException) { + logger.warn { "No execution found for deletion" } + } if (this.controller.isInitialized() && this.controller.executor.getExecution().name == execution.metadata.name) { this.controller.isUpdated.set(true) this.controller.executor.executor.run.compareAndSet(true, false) diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt index c966739a4744f448f60223738d379753b098c060..9f6cd64528874a1dc5f20c6d6c1563b1aa9f003d 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt @@ -2,6 +2,7 @@ package theodolite.execution.operator import io.fabric8.kubernetes.client.NamespacedKubernetesClient import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext +import khttp.patch import mu.KotlinLogging import theodolite.benchmark.BenchmarkExecution import theodolite.benchmark.KubernetesBenchmark @@ -10,17 +11,20 @@ import java.lang.Thread.sleep import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentLinkedDeque import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger private val logger = KotlinLogging.logger {} class TheodoliteController( val client: NamespacedKubernetesClient, - val executionContext: CustomResourceDefinitionContext + val executionContext: CustomResourceDefinitionContext, + val path: String ) { lateinit var executor: TheodoliteExecutor val executionsQueue: ConcurrentLinkedDeque<BenchmarkExecution> = ConcurrentLinkedDeque() val benchmarks: ConcurrentHashMap<String, KubernetesBenchmark> = ConcurrentHashMap() var isUpdated = AtomicBoolean(false) + var executionID = AtomicInteger(0) fun run() { while (true) { @@ -59,14 +63,22 @@ class TheodoliteController( @Synchronized fun runExecution(execution: BenchmarkExecution, benchmark: KubernetesBenchmark) { + execution.executionId = executionID.getAndSet(executionID.get() + 1) isUpdated.set(false) + benchmark.path = path logger.info { "Start execution ${execution.name} with benchmark ${benchmark.name}." } executor = TheodoliteExecutor(config = execution, kubernetesBenchmark = benchmark) executor.run() - if (!isUpdated.get()) { - client.customResource(executionContext).delete(client.namespace, execution.metadata.name) + try { + if (!isUpdated.get()) { + this.executionsQueue.removeIf { e -> e.name == execution.name } + client.customResource(executionContext).delete(client.namespace, execution.metadata.name) + } + } catch (e: Exception) { + logger.warn { "Deletion skipped." } } + logger.info { "Execution of ${execution.name} is finally stopped." } } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt index 33b71c8fa247511c371b7a154071634db7092660..c6181d19bcedfdb36e455b540e19bf0a54a2a297 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt @@ -7,6 +7,7 @@ import theodolite.benchmark.BenchmarkExecution import theodolite.benchmark.BenchmarkExecutionList import theodolite.benchmark.KubernetesBenchmark import theodolite.benchmark.KubernetesBenchmarkList +import theodolite.k8s.K8sContextFactory private const val DEFAULT_NAMESPACE = "default" @@ -43,7 +44,8 @@ class TheodoliteOperator { val executionContext = contextFactory.create(API_VERSION, SCOPE, GROUP, EXECUTION_PLURAL) val benchmarkContext = contextFactory.create(API_VERSION, SCOPE, GROUP, BENCHMARK_PLURAL) - val controller = TheodoliteController(client = client, executionContext = executionContext) + val appResource = System.getenv("THEODOLITE_APP_RESOURCES") ?: "./config" + val controller = TheodoliteController(client = client, executionContext = executionContext, path = appResource) val informerFactory = client.informers() val informerExecution = informerFactory.sharedIndexInformerForCustomResource( diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/K8sContextFactory.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sContextFactory.kt similarity index 91% rename from theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/K8sContextFactory.kt rename to theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sContextFactory.kt index 94adf61a435df743aea168c5451b2f3faa24ca17..8fd822c615da4fab37dafb6927032f64db6c4462 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/K8sContextFactory.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sContextFactory.kt @@ -1,4 +1,4 @@ -package theodolite.execution.operator +package theodolite.k8s import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sCustomResourceWrapper.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sCustomResourceWrapper.kt deleted file mode 100644 index 8b3ac08265d5caa75f602b18e279e36efd24e187..0000000000000000000000000000000000000000 --- a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sCustomResourceWrapper.kt +++ /dev/null @@ -1,54 +0,0 @@ -package theodolite.k8s - -import io.fabric8.kubernetes.client.CustomResource -import io.fabric8.kubernetes.client.NamespacedKubernetesClient -import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext - -/** -* Fabric8 handles custom resources as plain HashMaps. These need to be handled differently than normal -* Kubernetes resources. The K8sCustomResourceWrapper class provides a wrapper to deploy and delete -* custom resources in a uniform way. -* -* @property map custom resource as plain hashmap -* @constructor Create empty K8s custom resource wrapper -*/ -class K8sCustomResourceWrapper(private val map : Map<String,String>) : CustomResource() { - - - /** - * Deploy a custom resource - * - * @param client a namespaced Kubernetes client which are used to deploy the CR object. - */ - fun deploy(client : NamespacedKubernetesClient){ - val kind = this.map["kind"] - // Search the CustomResourceDefinition to which the CR Object belongs. - // This should be exactly one if the CRD is registered for Kubernetes, zero otherwise. - val crds = client.apiextensions().v1beta1().customResourceDefinitions().list() - crds.items - .filter { crd -> crd.toString().contains("kind=$kind") } - .map { crd -> CustomResourceDefinitionContext.fromCrd(crd) } - .forEach { context -> - client.customResource(context).createOrReplace(client.configuration.namespace,this.map as Map<String, Any>) - } - } - - /** - * Delete a custom resource - * - * @param client a namespaced Kubernetes client which are used to delete the CR object. - */ - fun delete(client : NamespacedKubernetesClient){ - val kind = this.map["kind"] - val metadata = this.map["metadata"] as HashMap<String,String> - val name = metadata["name"] - - val crds = client.apiextensions().v1beta1().customResourceDefinitions().list() - crds.items - .filter { crd -> crd.toString().contains("kind=$kind") } - .map { crd -> CustomResourceDefinitionContext.fromCrd(crd) } - .forEach { context -> - client.customResource(context).delete(client.configuration.namespace,name) } - } - -} \ No newline at end of file diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt index 38a07545513d7713f61e484702cb48143ab3aed0..029f18c30b79bbbf36c3761ced107e23f39682d8 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt @@ -19,7 +19,7 @@ class K8sManager(private val client: NamespacedKubernetesClient) { this.client.configMaps().createOrReplace(resource) is StatefulSet -> this.client.apps().statefulSets().createOrReplace(resource) - is K8sCustomResourceWrapper -> resource.deploy(client) + is ServiceMonitorWrapper -> resource.deploy(client) else -> throw IllegalArgumentException("Unknown Kubernetes resource.") } } @@ -34,7 +34,7 @@ class K8sManager(private val client: NamespacedKubernetesClient) { this.client.configMaps().delete(resource) is StatefulSet -> this.client.apps().statefulSets().delete(resource) - is K8sCustomResourceWrapper -> resource.delete(client) + is ServiceMonitorWrapper -> resource.delete(client) else -> throw IllegalArgumentException("Unknown Kubernetes resource.") } } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sResourceLoader.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sResourceLoader.kt index 658af46611ec7e1e784d286391ecf1e25a25366e..bf4af6c531064d42a9fcee1b22058850a560427d 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sResourceLoader.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sResourceLoader.kt @@ -26,8 +26,8 @@ class K8sResourceLoader(private val client: NamespacedKubernetesClient) { * @param path of the yaml file * @return customResource from fabric8 */ - fun loadCustomResource(path: String): K8sCustomResourceWrapper { - return loadGenericResource(path) { x: String -> K8sCustomResourceWrapper(YamlParser().parse(path, HashMap<String, String>()::class.java)!!) } + private fun loadServiceMonitor(path: String): ServiceMonitorWrapper { + return loadGenericResource(path) { x: String -> ServiceMonitorWrapper(YamlParser().parse(path, HashMap<String, String>()::class.java)!!) } } /** @@ -73,16 +73,11 @@ class K8sResourceLoader(private val client: NamespacedKubernetesClient) { return when (kind) { "Deployment" -> loadDeployment(path) "Service" -> loadService(path) - "ServiceMonitor" -> loadCustomResource(path) + "ServiceMonitor" -> loadServiceMonitor(path) "ConfigMap" -> loadConfigmap(path) else -> { - logger.warn { "Try to load $kind from $path as Custom ressource" } - try{ - loadCustomResource(path) - } catch (e:Exception){ - logger.error { "Error during loading of unspecified CustomResource: $e" } - throw e - } + logger.error { "Error during loading of unspecified resource Kind" } + throw java.lang.IllegalArgumentException("error while loading resource with kind: $kind") } } } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/ServiceMonitorWrapper.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/ServiceMonitorWrapper.kt new file mode 100644 index 0000000000000000000000000000000000000000..4950cee225e103ff095def91de64471ec1894a79 --- /dev/null +++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/ServiceMonitorWrapper.kt @@ -0,0 +1,56 @@ +package theodolite.k8s + +import io.fabric8.kubernetes.client.CustomResource +import io.fabric8.kubernetes.client.NamespacedKubernetesClient +import mu.KotlinLogging + +private val logger = KotlinLogging.logger {} + +class ServiceMonitorWrapper(private val serviceMonitor: Map<String, String>) : CustomResource() { + + /** + * 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) { + val serviceMonitorContext = K8sContextFactory().create( + api = "v1", + scope = "Namespaced", + group = "monitoring.coreos.com", + plural = "servicemonitors" + ) + client.customResource(serviceMonitorContext) + .createOrReplace(client.configuration.namespace, this.serviceMonitor 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) { + val serviceMonitorContext = K8sContextFactory().create( + api = "v1", + scope = "Namespaced", + group = "monitoring.coreos.com", + plural = "servicemonitors" + ) + try { + client.customResource(serviceMonitorContext) + .delete(client.configuration.namespace, this.getServiceMonitorName()) + } catch (e: Exception) { + logger.warn { "Could not delete service monitor" } + } + } + + /** + * @throws NullPointerException if name or metadata is null + */ + private fun getServiceMonitorName(): String { + val smAsMap = this.serviceMonitor["metadata"]!! as Map<String, String> + return smAsMap["name"]!! + } +} diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/TopicManager.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/TopicManager.kt index 390974cd247645197ebe6044bf785710164155aa..7529a45310f219fc0c5248b4031a020227a86049 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/TopicManager.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/TopicManager.kt @@ -2,7 +2,6 @@ package theodolite.k8s import mu.KotlinLogging import org.apache.kafka.clients.admin.AdminClient -import org.apache.kafka.clients.admin.ListTopicsResult import org.apache.kafka.clients.admin.NewTopic import java.util.* @@ -20,9 +19,15 @@ class TopicManager(private val kafkaConfig: HashMap<String, Any>) { */ fun createTopics(newTopics: Collection<NewTopic>) { var kafkaAdmin: AdminClient = AdminClient.create(this.kafkaConfig) - kafkaAdmin.createTopics(newTopics) + val result = kafkaAdmin.createTopics(newTopics) + result.all().get()// wait for the future object + logger.info { + "Topics created finished with result: ${ + result.values().map { it -> it.key + ": " + it.value.isDone } + .joinToString(separator = ",") + } " + } kafkaAdmin.close() - logger.info { "Topics created" } } @@ -32,15 +37,19 @@ class TopicManager(private val kafkaConfig: HashMap<String, Any>) { */ fun removeTopics(topics: List<String>) { var kafkaAdmin: AdminClient = AdminClient.create(this.kafkaConfig) - val result = kafkaAdmin.deleteTopics(topics) - try { - result.all().get() + val result = kafkaAdmin.deleteTopics(topics) + result.all().get() // wait for the future object + logger.info { + "\"Topics deletion finished with result: ${ + result.values().map { it -> it.key + ": " + it.value.isDone } + .joinToString(separator = ",") + } " + } } catch (e: Exception) { - logger.error { "Error while removing topics: $e" } - logger.debug { "Existing topics are: ${kafkaAdmin.listTopics()}." } + logger.error { "Error while removing topics: $e" } + logger.debug { "Existing topics are: ${kafkaAdmin.listTopics()}." } } kafkaAdmin.close() - logger.info { "Topics removed" } } } diff --git a/theodolite-quarkus/src/main/resources/application.properties b/theodolite-quarkus/src/main/resources/application.properties index d5ff26fd074ec74c02a25bc4dd0dd3734433401b..42647e2391706286602945cf2be7baa96857ba19 100644 --- a/theodolite-quarkus/src/main/resources/application.properties +++ b/theodolite-quarkus/src/main/resources/application.properties @@ -1,3 +1,6 @@ quarkus.native.additional-build-args=\ --initialize-at-run-time=io.fabric8.kubernetes.client.internal.CertUtils,\ - --report-unsupported-elements-at-runtime \ No newline at end of file + --initialize-at-run-time=io.fabric8.kubernetes.client.dsl.internal.uploadable.PodUpload,\ + --initialize-at-run-time=io.fabric8.kubernetes.client.dsl.internal.core.v1.PodOperationsImpl$1,\ + --initialize-at-run-time=io.fabric8.kubernetes.client.dsl.internal.core.v1.PodOperationsImpl$3,\ + --report-unsupported-elements-at-runtime diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/CompositeStrategyTest.kt b/theodolite-quarkus/src/test/kotlin/theodolite/CompositeStrategyTest.kt index 67c9857220a0d419183644ffaf8c6a6e16a6ce9b..7802529bfda309131cafc0ab3f39fda43285c32f 100644 --- a/theodolite-quarkus/src/test/kotlin/theodolite/CompositeStrategyTest.kt +++ b/theodolite-quarkus/src/test/kotlin/theodolite/CompositeStrategyTest.kt @@ -31,7 +31,7 @@ class CompositeStrategyTest { val results = Results() val benchmark = TestBenchmark() val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo() - val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, sloChecker) + val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, sloChecker, 0) val linearSearch = LinearSearch(benchmarkExecutor) val lowerBoundRestriction = LowerBoundRestriction(results) val strategy = @@ -65,7 +65,7 @@ class CompositeStrategyTest { val benchmark = TestBenchmark() val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo() val benchmarkExecutorImpl = - TestBenchmarkExecutorImpl(mockResults, benchmark, results, sloChecker) + TestBenchmarkExecutorImpl(mockResults, benchmark, results, sloChecker, 0) val binarySearch = BinarySearch(benchmarkExecutorImpl) val lowerBoundRestriction = LowerBoundRestriction(results) val strategy = @@ -98,7 +98,7 @@ class CompositeStrategyTest { val results = Results() val benchmark = TestBenchmark() val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo() - val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, sloChecker) + val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, sloChecker, 0) val binarySearch = BinarySearch(benchmarkExecutor) val lowerBoundRestriction = LowerBoundRestriction(results) val strategy = diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/TestBenchmarkExecutorImpl.kt b/theodolite-quarkus/src/test/kotlin/theodolite/TestBenchmarkExecutorImpl.kt index 2de07d48dbe59313506745e6c97b2ae9a5ed114e..2bafcb76dfc3463d9aa350b88c9f73d52cea6629 100644 --- a/theodolite-quarkus/src/test/kotlin/theodolite/TestBenchmarkExecutorImpl.kt +++ b/theodolite-quarkus/src/test/kotlin/theodolite/TestBenchmarkExecutorImpl.kt @@ -12,14 +12,16 @@ class TestBenchmarkExecutorImpl( private val mockResults: Array<Array<Boolean>>, benchmark: Benchmark, results: Results, - slo: BenchmarkExecution.Slo + slo: BenchmarkExecution.Slo, + executionId: Int ) : BenchmarkExecutor( benchmark, results, executionDuration = Duration.ofSeconds(1), configurationOverrides = emptyList(), - slo = slo + slo = slo, + executionId = executionId ) { override fun runExperiment(load: LoadDimension, res: Resource): Boolean {