From 2d637dee145ca09e2e03bd21132bfcec011455de Mon Sep 17 00:00:00 2001 From: Carlos Mendes Date: Tue, 27 Feb 2024 14:07:11 -0300 Subject: [PATCH] remove module runner --- .github/workflows/build.yaml | 4 +- resilience-bench/Makefile | 7 +- resilience-bench/operator/.idea/.gitignore | 3 + resilience-bench/operator/.idea/compiler.xml | 13 ++ .../operator/.idea/jarRepositories.xml | 20 +++ resilience-bench/operator/.idea/jpa-buddy.xml | 6 + resilience-bench/operator/.idea/misc.xml | 15 ++ resilience-bench/operator/.idea/vcs.xml | 6 + resilience-bench/operator/pom.xml | 97 ++++++++++- .../resiliencebench/BenchmarkReconciler.java | 2 + .../ppgia/resiliencebench/ScenarioRunner.java | 162 ------------------ .../ppgia/resiliencebench/Scheduler.java | 32 +--- .../external/k6/K6WorkloadAdapter.java | 49 ------ .../scenarioexec/ExecutorStep.java | 20 +++ .../scenarioexec/IstioExecutorStep.java | 45 +++++ .../scenarioexec/IstioFaultStep.java | 61 +++++++ .../scenarioexec/IstioRetryStep.java | 66 +++++++ .../scenarioexec/K6LoadGeneratorStep.java | 96 +++++++++++ .../scenarioexec/ScenarioRunner2.java | 41 +++++ .../scenarioexec/ScenarioRunnerStep.java | 12 ++ .../src/main/resources/k6-resource.yaml | 10 -- .../ScenarioRunnerE2ETest.java | 63 ------- .../k6/K6WorkloadAdapterTest.java | 48 ------ resilience-bench/pom.xml | 123 ------------- resilience-bench/runner/pom.xml | 83 --------- .../unifor/ppgia/resiliencebench/Runner.java | 8 - 26 files changed, 507 insertions(+), 585 deletions(-) create mode 100644 resilience-bench/operator/.idea/.gitignore create mode 100644 resilience-bench/operator/.idea/compiler.xml create mode 100644 resilience-bench/operator/.idea/jarRepositories.xml create mode 100644 resilience-bench/operator/.idea/jpa-buddy.xml create mode 100644 resilience-bench/operator/.idea/misc.xml create mode 100644 resilience-bench/operator/.idea/vcs.xml delete mode 100644 resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/ScenarioRunner.java delete mode 100644 resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/external/k6/K6WorkloadAdapter.java create mode 100644 resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/ExecutorStep.java create mode 100644 resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/IstioExecutorStep.java create mode 100644 resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/IstioFaultStep.java create mode 100644 resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/IstioRetryStep.java create mode 100644 resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/K6LoadGeneratorStep.java create mode 100644 resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/ScenarioRunner2.java create mode 100644 resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/ScenarioRunnerStep.java delete mode 100644 resilience-bench/operator/src/main/resources/k6-resource.yaml delete mode 100644 resilience-bench/operator/src/test/java/br/unifor/ppgia/resiliencebench/ScenarioRunnerE2ETest.java delete mode 100644 resilience-bench/operator/src/test/java/br/unifor/ppgia/resiliencebench/k6/K6WorkloadAdapterTest.java delete mode 100644 resilience-bench/pom.xml delete mode 100644 resilience-bench/runner/pom.xml delete mode 100644 resilience-bench/runner/src/main/java/br/unifor/ppgia/resiliencebench/Runner.java diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 5acfb59..6018d59 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -25,7 +25,7 @@ jobs: - name: Test with Maven run: | kubectl cluster-info --context kind-chart-testing - mvn -B compile --file ./resilience-bench/pom.xml + mvn -B compile --file ./resilience-bench/operator/pom.xml find ./resilience-bench/operator/target/classes/META-INF/fabric8 -type f -name "*-v1.yml" | xargs -I {} kubectl apply -f {} kubectl get crd - mvn -B test --file ./resilience-bench/pom.xml + mvn -B test --file ./resilience-bench/operator/pom.xml diff --git a/resilience-bench/Makefile b/resilience-bench/Makefile index 2a957fd..2aaa224 100644 --- a/resilience-bench/Makefile +++ b/resilience-bench/Makefile @@ -1,7 +1,7 @@ test: - mvn clean compile + mvn clean compile -B operator/pom.xml find ./operator/target/classes/META-INF/fabric8 -type f -name "*-v1.yml" | xargs -I {} kubectl apply -f {} - mvn test + mvn test -B operator/pom.xml deploy: find ./operator/target/classes/META-INF/fabric8 -type f -name "*-v1.yml" | xargs -I {} kubectl apply -f {} @@ -10,4 +10,5 @@ deploy: clean: @kubectl delete scenario --all --all-namespaces @kubectl delete job --all --all-namespaces - @kubectl delete queues --all --all-namespaces \ No newline at end of file + @kubectl delete queues --all --all-namespaces + @kubectl delete benchmark --all --all-namespaces \ No newline at end of file diff --git a/resilience-bench/operator/.idea/.gitignore b/resilience-bench/operator/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/resilience-bench/operator/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/resilience-bench/operator/.idea/compiler.xml b/resilience-bench/operator/.idea/compiler.xml new file mode 100644 index 0000000..d0f3ae0 --- /dev/null +++ b/resilience-bench/operator/.idea/compiler.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/resilience-bench/operator/.idea/jarRepositories.xml b/resilience-bench/operator/.idea/jarRepositories.xml new file mode 100644 index 0000000..712ab9d --- /dev/null +++ b/resilience-bench/operator/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/resilience-bench/operator/.idea/jpa-buddy.xml b/resilience-bench/operator/.idea/jpa-buddy.xml new file mode 100644 index 0000000..966d5f5 --- /dev/null +++ b/resilience-bench/operator/.idea/jpa-buddy.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resilience-bench/operator/.idea/misc.xml b/resilience-bench/operator/.idea/misc.xml new file mode 100644 index 0000000..82ab2cc --- /dev/null +++ b/resilience-bench/operator/.idea/misc.xml @@ -0,0 +1,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/resilience-bench/operator/.idea/vcs.xml b/resilience-bench/operator/.idea/vcs.xml new file mode 100644 index 0000000..b2bdec2 --- /dev/null +++ b/resilience-bench/operator/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/resilience-bench/operator/pom.xml b/resilience-bench/operator/pom.xml index f8f82b6..08cf3e9 100644 --- a/resilience-bench/operator/pom.xml +++ b/resilience-bench/operator/pom.xml @@ -4,20 +4,22 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - - br.unifor.ppgia - resilience-bench - 1.0.0 - ../pom.xml - - - operator + br.unifor.ppgia + resilience-bench-operator 1.0.0 4.8.0 + 17 + 17 + 3.11.0 + 3.2.5 + 3.4.0 + 6.10.0 + 5.10.2 + @@ -42,12 +44,24 @@ + + + io.fabric8 + istio-client + ${fabric8-client.version} + + + io.fabric8 + kubernetes-httpclient-vertx + ${fabric8-client.version} + io.fabric8 crd-generator-apt ${fabric8-client.version} provided + org.apache.logging.log4j log4j-slf4j-impl @@ -65,6 +79,36 @@ operator-framework-junit-5 test + + org.awaitility + awaitility + 4.2.0 + test + + + org.mockito + mockito-core + 5.10.0 + test + + + org.junit.jupiter + junit-jupiter-api + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit.version} + test + @@ -84,6 +128,43 @@ + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + **/*E2ETest.java + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.12.1 + + ${maven.compiler.source} + ${maven.compiler.target} + + + + org.apache.maven.plugins + maven-failsafe-plugin + 3.2.5 + + + + integration-test + verify + + + + **/*E2ETest.java + + + + + diff --git a/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/BenchmarkReconciler.java b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/BenchmarkReconciler.java index 2ec4321..02c46b8 100644 --- a/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/BenchmarkReconciler.java +++ b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/BenchmarkReconciler.java @@ -70,4 +70,6 @@ private void createOrUpdateScenario(Scenario scenario, CustomResourceRepository< logger.debug("Scenario already exists: {}", scenario.getMetadata().getName()); } } + + } diff --git a/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/ScenarioRunner.java b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/ScenarioRunner.java deleted file mode 100644 index 675c268..0000000 --- a/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/ScenarioRunner.java +++ /dev/null @@ -1,162 +0,0 @@ -package br.unifor.ppgia.resiliencebench; - -import br.unifor.ppgia.resiliencebench.execution.scenario.Scenario; -import br.unifor.ppgia.resiliencebench.execution.scenario.ScenarioFaultTemplate; -import br.unifor.ppgia.resiliencebench.execution.scenario.ScenarioSpec; -import br.unifor.ppgia.resiliencebench.execution.scenario.ScenarioWorkload; -import br.unifor.ppgia.resiliencebench.external.k6.K6WorkloadAdapter; -import br.unifor.ppgia.resiliencebench.modeling.service.ResilientService; -import br.unifor.ppgia.resiliencebench.modeling.workload.Workload; -import br.unifor.ppgia.resiliencebench.support.CustomResourceRepository; -import io.fabric8.istio.api.networking.v1beta1.HTTPFaultInjection; -import io.fabric8.istio.api.networking.v1beta1.HTTPRetry; -import io.fabric8.istio.api.networking.v1beta1.VirtualService; -import io.fabric8.istio.client.DefaultIstioClient; -import io.fabric8.istio.client.IstioClient; -import io.fabric8.kubernetes.client.KubernetesClient; - -import java.util.Map; - -import static java.lang.String.format; - -public class ScenarioRunner { - - private final KubernetesClient kubernetesClient; - private final IstioClient istioClient; - - public ScenarioRunner(KubernetesClient kubernetesClient, IstioClient istioClient) { - this.istioClient = new DefaultIstioClient(); - this.kubernetesClient = kubernetesClient; - } - - public void run(String namespace, String name) { - var scenarioRepository = new CustomResourceRepository<>(kubernetesClient, Scenario.class); - var scenario = scenarioRepository.get(namespace, name); - if (scenario.isPresent()) { - internalRun(scenario.get()); - } else { - throw new RuntimeException(format("Scenario not found: %s.%s", namespace, name)); - } - } - - private void internalRun(Scenario scenario) { - var spec = scenario.getSpec(); - - // get services - var targetService = - findVirtualService( - scenario.getMetadata().getNamespace(), - spec.getTargetServiceName() - ); - - var sourceService = - findVirtualService( - scenario.getMetadata().getNamespace(), - spec.getSourceServiceName() - ); - - // prepare retry pattern - prepareRetry(spec, sourceService); - - // prepare fault - prepareFault(spec, targetService); - - // run workload - var workloadRepository = new CustomResourceRepository<>(kubernetesClient, Workload.class); - var workload = workloadRepository.get(scenario.getMetadata().getNamespace(), scenario.getSpec().getWorkload().getWorkloadName()); - runWorkload(workload.get(), scenario.getSpec().getWorkload()); - } - - private VirtualService findVirtualService(String namespace, String name) { - var serviceRepository = new CustomResourceRepository<>(kubernetesClient, ResilientService.class); - var targetService = serviceRepository.get(namespace, name); - - if (targetService.isPresent()) { - var virtualServiceName = targetService.get().getMetadata().getAnnotations().get("resiliencebench.io/virtual-service"); - var virtualServiceNamespace = targetService.get().getMetadata().getAnnotations().getOrDefault("resiliencebench.io/virtual-service-ns", "default"); - return istioClient - .v1beta1() - .virtualServices() - .inNamespace(virtualServiceNamespace) - .withName(virtualServiceName) - .get(); - } else { - throw new RuntimeException(format("Service not found: %s.%s", namespace, name)); - } - } - - private void prepareFault(ScenarioSpec spec, VirtualService targetService) { - var newVirtualService = targetService - .edit() - .editSpec() - .editFirstHttp() - .withFault(configureFault(spec.getFault())) - .endHttp() - .endSpec() - .build(); - - istioClient - .v1beta1() - .virtualServices() - .inNamespace(targetService.getMetadata().getNamespace() ) - .resource(newVirtualService) - .update(); - } - - private void prepareRetry(ScenarioSpec spec, VirtualService targetService) { - var newVirtualService = targetService - .edit() - .editSpec() - .editFirstHttp() - .withRetries(configureRetryPattern(spec.getPatternConfig())) - .endHttp() - .endSpec() - .build(); - - istioClient - .v1beta1() - .virtualServices() - .inNamespace(targetService.getMetadata().getNamespace()) - .resource(newVirtualService) - .update(); - } - - public HTTPRetry configureRetryPattern(Map patternConfig) { - var builder = new HTTPRetry().toBuilder(); - var attempts = (Integer) patternConfig.get("attempts"); - if (attempts != null) { - builder.withAttempts(attempts); - } else { - throw new IllegalArgumentException("attempts is required"); - } - var perTryTimeout = (Integer) patternConfig.get("perTryTimeout"); - if (perTryTimeout != null) { - builder.withPerTryTimeout(perTryTimeout + "ms"); - } else { - throw new IllegalArgumentException("perTryTimeout is required"); - } - return builder.build(); - } - - public HTTPFaultInjection configureFault(ScenarioFaultTemplate faultTemplate) { - var builder = new HTTPFaultInjection().toBuilder(); - if (faultTemplate.getDelay() != null) { - builder.withNewDelay() - .withNewPercentage(faultTemplate.getPercentage().doubleValue()) - .withNewHTTPFaultInjectionDelayFixedHttpType(faultTemplate.getDelay().duration() + "ms") - .endDelay(); - } else { - builder.withNewAbort() - .withNewPercentage(faultTemplate.getPercentage().doubleValue()) - .withNewHTTPFaultInjectionAbortHttpStatusErrorType(faultTemplate.getAbort().httpStatus()) - .endAbort(); - } - return builder.build(); - } - - public void runWorkload(Workload workload, ScenarioWorkload scenarioWorkload) { - var resource = new K6WorkloadAdapter().adapt(workload, scenarioWorkload); - var created = this.kubernetesClient.resource(resource).inNamespace(workload.getMetadata().getNamespace()).create(); - System.out.println("Created: " + created); - } -} diff --git a/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/Scheduler.java b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/Scheduler.java index d23590e..57935d5 100644 --- a/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/Scheduler.java +++ b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/Scheduler.java @@ -3,9 +3,10 @@ import br.unifor.ppgia.resiliencebench.execution.queue.ExecutionQueue; import br.unifor.ppgia.resiliencebench.execution.queue.Item; import br.unifor.ppgia.resiliencebench.execution.scenario.Scenario; +import br.unifor.ppgia.resiliencebench.scenarioexec.ScenarioRunner2; import br.unifor.ppgia.resiliencebench.support.CustomResourceRepository; +import io.fabric8.istio.client.DefaultIstioClient; import io.fabric8.kubernetes.api.model.batch.v1.Job; -import io.fabric8.kubernetes.api.model.batch.v1.JobBuilder; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.Watcher; import io.fabric8.kubernetes.client.WatcherException; @@ -15,7 +16,6 @@ import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Objects; -import java.util.UUID; import static br.unifor.ppgia.resiliencebench.support.Annotations.OWNED_BY; @@ -65,32 +65,12 @@ private boolean existsJobRunning(String namespace) { return deve; } - private void createJob(String namespace, Item scenario) { - var jobName = UUID.randomUUID().toString(); - var job = new JobBuilder() - .withApiVersion("batch/v1") - .withNewMetadata() - .withName(jobName) - .withNamespace(namespace) - .addToAnnotations("scenario", scenario.getScenario()) - .endMetadata() - .withNewSpec() - .withBackoffLimit(4) - .withNewTemplate() - .withNewSpec() - .withRestartPolicy("Never") - .addNewContainer() - .withName("kubectl") - .withCommand("sleep", "3") - .withImage("alpine") - .endContainer() - .endSpec() - .endTemplate().and().build(); - + private void createJob(String namespace, Item item) { + var runner = new ScenarioRunner2(this.client, new DefaultIstioClient()); + var job = runner.run(namespace, item.getScenario()); var jobsClient = client.batch().v1().jobs(); job = jobsClient.resource(job).create(); jobsClient.resource(job).watch(this); - logger.debug("Created job: {}", jobName); } private void updateStatus(Item queueItem, String namespace, String status, ExecutionQueue executionQueue) { @@ -106,7 +86,7 @@ public void eventReceived(Action action, Job resource) { // TODO precisa melhora if (action.equals(Action.MODIFIED)) { if (Objects.nonNull(resource.getStatus().getCompletionTime())) { logger.debug("Finished job: {}", resource.getMetadata().getName()); - var scenarioName = resource.getMetadata().getAnnotations().get("scenario"); + var scenarioName = resource.getMetadata().getAnnotations().get("resiliencebench.io/scenario"); var scenario = new CustomResourceRepository<>(client, Scenario.class).get(namespace, scenarioName).get(); var executionQueue = new CustomResourceRepository<>(client, ExecutionQueue.class).get(namespace, scenario.getMetadata().getAnnotations().get(OWNED_BY)).get(); var queueItem = executionQueue.getSpec().getItems().stream().filter(item -> item.getScenario().equals(scenarioName)).findFirst().get(); diff --git a/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/external/k6/K6WorkloadAdapter.java b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/external/k6/K6WorkloadAdapter.java deleted file mode 100644 index bf54b01..0000000 --- a/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/external/k6/K6WorkloadAdapter.java +++ /dev/null @@ -1,49 +0,0 @@ -package br.unifor.ppgia.resiliencebench.external.k6; - -import br.unifor.ppgia.resiliencebench.execution.scenario.ScenarioWorkload; -import br.unifor.ppgia.resiliencebench.modeling.workload.Workload; -import br.unifor.ppgia.resiliencebench.modeling.workload.WorkloadResourceAdapter; -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.fabric8.kubernetes.client.utils.Serialization; - -import java.util.Arrays; -import java.util.Map; - -import static br.unifor.ppgia.resiliencebench.support.Annotations.CREATED_BY; - -public class K6WorkloadAdapter implements WorkloadResourceAdapter { - @Override - public HasMetadata adapt(Workload workload, ScenarioWorkload scenarioWorkload) { - var args = String.join(" ", Arrays.asList( - "--vus", String.valueOf(scenarioWorkload.getUsers()), - "--tag", "workloadName=" + workload.getMetadata().getName(), - "--duration", workload.getSpec().getDuration() + "s" - )); - - var spec = Map.of( - "parallelism", 1, - "arguments", args, - "script", Map.of( - "config", Map.of( - "name", workload.getSpec().getScript().getConfigMap().getName(), - "file", workload.getSpec().getScript().getConfigMap().getFile() - ) - ) - ); - - var meta = new ObjectMetaBuilder() - .withName(workload.getMetadata().getName()) - .withNamespace(workload.getMetadata().getNamespace()) - .addToAnnotations(CREATED_BY, "resiliencebench-operator") - .addToAnnotations("resiliencebench.io/workload", workload.getMetadata().getName()) - .build(); - - return Serialization.unmarshal(Serialization.asJson(Map.of( - "apiVersion", "k6.io/v1alpha1", - "kind", "TestRun", - "metadata", meta, - "spec", spec - )), HasMetadata.class); - } -} diff --git a/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/ExecutorStep.java b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/ExecutorStep.java new file mode 100644 index 0000000..6699661 --- /dev/null +++ b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/ExecutorStep.java @@ -0,0 +1,20 @@ +package br.unifor.ppgia.resiliencebench.scenarioexec; + +import br.unifor.ppgia.resiliencebench.execution.scenario.Scenario; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.KubernetesClient; + +public abstract class ExecutorStep { + + private final KubernetesClient kubernetesClient; + + public ExecutorStep(KubernetesClient kubernetesClient) { + this.kubernetesClient = kubernetesClient; + } + + protected KubernetesClient kubernetesClient() { + return kubernetesClient; + } + + public abstract TResult execute(Scenario scenario); +} diff --git a/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/IstioExecutorStep.java b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/IstioExecutorStep.java new file mode 100644 index 0000000..32d4a47 --- /dev/null +++ b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/IstioExecutorStep.java @@ -0,0 +1,45 @@ +package br.unifor.ppgia.resiliencebench.scenarioexec; + +import br.unifor.ppgia.resiliencebench.execution.scenario.Scenario; +import br.unifor.ppgia.resiliencebench.modeling.service.ResilientService; +import br.unifor.ppgia.resiliencebench.support.CustomResourceRepository; +import io.fabric8.istio.api.networking.v1beta1.VirtualService; +import io.fabric8.istio.client.IstioClient; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.KubernetesClient; + +import static java.lang.String.format; + +public abstract class IstioExecutorStep extends ExecutorStep { + + private final IstioClient istioClient; + + public IstioExecutorStep(KubernetesClient kubernetesClient, IstioClient istioClient) { + super(kubernetesClient); + this.istioClient = istioClient; + } + + protected IstioClient istioClient() { + return istioClient; + } + + protected VirtualService findVirtualService(String namespace, String name) { + var serviceRepository = new CustomResourceRepository<>(kubernetesClient(), ResilientService.class); + var targetService = serviceRepository.get(namespace, name); + + if (targetService.isPresent()) { + var virtualServiceName = targetService.get().getMetadata().getAnnotations().get("resiliencebench.io/virtual-service"); + return istioClient + .v1beta1() + .virtualServices() + .inNamespace(namespace) + .withName(virtualServiceName) + .get(); + } else { + throw new RuntimeException(format("Service not found: %s.%s", namespace, name)); + } + } + + @Override + public abstract TResult execute(Scenario scenario); +} diff --git a/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/IstioFaultStep.java b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/IstioFaultStep.java new file mode 100644 index 0000000..a96960d --- /dev/null +++ b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/IstioFaultStep.java @@ -0,0 +1,61 @@ +package br.unifor.ppgia.resiliencebench.scenarioexec; + +import br.unifor.ppgia.resiliencebench.execution.scenario.Scenario; +import br.unifor.ppgia.resiliencebench.execution.scenario.ScenarioFaultTemplate; +import io.fabric8.istio.api.networking.v1beta1.HTTPFaultInjection; +import io.fabric8.istio.api.networking.v1beta1.VirtualService; +import io.fabric8.istio.client.IstioClient; +import io.fabric8.kubernetes.client.KubernetesClient; + +public class IstioFaultStep extends IstioExecutorStep { + + public IstioFaultStep(KubernetesClient kubernetesClient, IstioClient istioClient) { + super(kubernetesClient, istioClient); + } + + @Override + public VirtualService execute(Scenario scenario) { + var targetService = + findVirtualService( + scenario.getMetadata().getNamespace(), + scenario.getSpec().getTargetServiceName() + ); + + var fault = configureFault(scenario.getSpec().getFault()); + + // TODO Handler error + // TODO check if the virtual service already has a fault. if yes, update it + + var editedVirtualService = targetService + .edit() + .editSpec() + .editFirstHttp() + .withFault(fault) + .endHttp() + .endSpec() + .build(); + + return istioClient() + .v1beta1() + .virtualServices() + .inNamespace(targetService.getMetadata().getNamespace() ) + .resource(editedVirtualService) + .update(); + } + + public HTTPFaultInjection configureFault(ScenarioFaultTemplate faultTemplate) { + var builder = new HTTPFaultInjection().toBuilder(); + if (faultTemplate.getDelay() != null) { + builder.withNewDelay() + .withNewPercentage(faultTemplate.getPercentage().doubleValue()) + .withNewHTTPFaultInjectionDelayFixedHttpType(faultTemplate.getDelay().duration() + "ms") + .endDelay(); + } else { + builder.withNewAbort() + .withNewPercentage(faultTemplate.getPercentage().doubleValue()) + .withNewHTTPFaultInjectionAbortHttpStatusErrorType(faultTemplate.getAbort().httpStatus()) + .endAbort(); + } + return builder.build(); + } +} diff --git a/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/IstioRetryStep.java b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/IstioRetryStep.java new file mode 100644 index 0000000..05ff806 --- /dev/null +++ b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/IstioRetryStep.java @@ -0,0 +1,66 @@ +package br.unifor.ppgia.resiliencebench.scenarioexec; + +import br.unifor.ppgia.resiliencebench.execution.scenario.Scenario; +import io.fabric8.istio.api.networking.v1beta1.HTTPRetry; +import io.fabric8.istio.api.networking.v1beta1.VirtualService; +import io.fabric8.istio.client.IstioClient; +import io.fabric8.kubernetes.client.KubernetesClient; + +import java.util.Map; + +public class IstioRetryStep extends IstioExecutorStep { + + public IstioRetryStep(KubernetesClient kubernetesClient, IstioClient istioClient) { + super(kubernetesClient, istioClient); + } + + @Override + public VirtualService execute(Scenario scenario) { + + // TODO verify if scenario has a retry pattern configured + + var targetService = + findVirtualService( + scenario.getMetadata().getNamespace(), + scenario.getSpec().getTargetServiceName() + ); + + // TODO verify if the virtual service already has a retry. if yes, update it + var retry = configureRetryPattern(scenario.getSpec().getPatternConfig()); + + var newVirtualService = targetService + .edit() + .editSpec() + .editFirstHttp() + .withRetries(retry) + .endHttp() + .endSpec() + .build(); + + return istioClient() + .v1beta1() + .virtualServices() + .inNamespace(targetService.getMetadata().getNamespace()) + .resource(newVirtualService) + .update(); + } + + + + public HTTPRetry configureRetryPattern(Map patternConfig) { + var builder = new HTTPRetry().toBuilder(); + var attempts = (Integer) patternConfig.get("attempts"); + if (attempts != null) { + builder.withAttempts(attempts); + } else { + throw new IllegalArgumentException("attempts is required"); + } + var perTryTimeout = (Integer) patternConfig.get("perTryTimeout"); + if (perTryTimeout != null) { + builder.withPerTryTimeout(perTryTimeout + "ms"); + } else { + throw new IllegalArgumentException("perTryTimeout is required"); + } + return builder.build(); + } +} diff --git a/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/K6LoadGeneratorStep.java b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/K6LoadGeneratorStep.java new file mode 100644 index 0000000..93da073 --- /dev/null +++ b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/K6LoadGeneratorStep.java @@ -0,0 +1,96 @@ +package br.unifor.ppgia.resiliencebench.scenarioexec; + +import br.unifor.ppgia.resiliencebench.execution.scenario.Scenario; +import br.unifor.ppgia.resiliencebench.execution.scenario.ScenarioWorkload; +import br.unifor.ppgia.resiliencebench.modeling.workload.Workload; +import br.unifor.ppgia.resiliencebench.support.CustomResourceRepository; +import io.fabric8.kubernetes.api.model.*; +import io.fabric8.kubernetes.api.model.batch.v1.Job; +import io.fabric8.kubernetes.api.model.batch.v1.JobBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import static br.unifor.ppgia.resiliencebench.support.Annotations.CREATED_BY; + +public class K6LoadGeneratorStep extends ExecutorStep { + public K6LoadGeneratorStep(KubernetesClient kubernetesClient) { + super(kubernetesClient); + } + + @Override + public Job execute(Scenario scenario) { + var workloadRepository = new CustomResourceRepository<>(kubernetesClient(), Workload.class); + var workload = workloadRepository.get(scenario.getMetadata().getNamespace(), scenario.getSpec().getWorkload().getWorkloadName()); + return createJob(scenario, workload.get(), scenario.getSpec().getWorkload()); // TODO verify if workload exists + } + + public ObjectMeta createMeta(Scenario scenario, Workload workload) { + return new ObjectMetaBuilder() + .withName(workload.getMetadata().getName() + "-" + UUID.fromString(scenario.getMetadata().getUid())) + .withNamespace(workload.getMetadata().getNamespace()) + .addToAnnotations(CREATED_BY, "resiliencebench-operator") + .addToAnnotations("resiliencebench.io/scenario", scenario.getMetadata().getName()) + .addToAnnotations("resiliencebench.io/workload", workload.getMetadata().getName()) + .build(); + } + + public List createCommand(Scenario scenario, ScenarioWorkload scenarioWorkload, Workload workload) { + return Arrays.asList( + "k6", "run", "/scripts/k6.js", + "--out", String.format("csv=/results/%s.gz", scenario.getMetadata().getName()), + "--vus", String.valueOf(scenarioWorkload.getUsers()), + "--tag", "workloadName=" + workload.getMetadata().getName(), + "--duration", workload.getSpec().getDuration() + "s" + ); + } + + public Job createJob(Scenario scenario, Workload workload, ScenarioWorkload scenarioWorkload) { + return new JobBuilder() + .withMetadata(createMeta(scenario, workload)) + .withNewSpec() + .withNewTemplate() + .withNewMetadata() + .addToAnnotations("sidecar.istio.io/inject", "false") + .endMetadata() + .withNewSpec() + .withRestartPolicy("Never") + .withContainers(createK6Container(scenario, scenarioWorkload, workload)) + .withVolumes(createResultsVolume(), createScriptVolume(workload)) + .endSpec() + .endTemplate() + .withBackoffLimit(4) + .endSpec() + .build(); + } + + public Container createK6Container(Scenario scenario, ScenarioWorkload scenarioWorkload, Workload workload) { + return new ContainerBuilder() + .withName("k6") + .withImage("grafana/k6") + .withCommand(createCommand(scenario, scenarioWorkload, workload)) + .withVolumeMounts( + new VolumeMount("/scripts", "None", "script-volume", false, null, null), + new VolumeMount("/results", "HostToContainer", "test-results", false, null, null) + ) + .build(); + } + + public Volume createResultsVolume() { + return new VolumeBuilder() + .withName("test-results") + .withNewPersistentVolumeClaim("test-results", false) + .build(); + } + + public Volume createScriptVolume(Workload workload) { + return new VolumeBuilder() + .withName("script-volume") + .withNewConfigMap() + .withName(workload.getSpec().getScript().getConfigMap().getName()) + .endConfigMap() + .build(); + } +} diff --git a/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/ScenarioRunner2.java b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/ScenarioRunner2.java new file mode 100644 index 0000000..fdc8b85 --- /dev/null +++ b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/ScenarioRunner2.java @@ -0,0 +1,41 @@ +package br.unifor.ppgia.resiliencebench.scenarioexec; + +import br.unifor.ppgia.resiliencebench.execution.scenario.Scenario; +import br.unifor.ppgia.resiliencebench.support.CustomResourceRepository; +import io.fabric8.istio.client.IstioClient; +import io.fabric8.kubernetes.api.model.batch.v1.Job; +import io.fabric8.kubernetes.client.KubernetesClient; + +import java.util.Arrays; + +import static java.lang.String.format; + +public class ScenarioRunner2 { + + private final KubernetesClient kubernetesClient; + private final IstioClient istioClient; + + public ScenarioRunner2(KubernetesClient kubernetesClient, IstioClient istioClient) { + this.istioClient = istioClient; + this.kubernetesClient = kubernetesClient; + } + + public Job run(String namespace, String name) { + var scenarioRepository = new CustomResourceRepository<>(kubernetesClient, Scenario.class); + var scenario = scenarioRepository.get(namespace, name); + if (scenario.isPresent()) { + var preparationSteps = Arrays.asList( + new IstioRetryStep(kubernetesClient, istioClient), + // new IstioCircuitBreakerStep(kubernetesClient, istioClient), + new IstioFaultStep(kubernetesClient, istioClient) + ); + + preparationSteps.forEach(step -> step.execute(scenario.get())); + var loadGeneratorStep = new K6LoadGeneratorStep(kubernetesClient); + return loadGeneratorStep.execute(scenario.get()); + + } else { + throw new RuntimeException(format("Scenario not found: %s.%s", namespace, name)); + } + } +} diff --git a/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/ScenarioRunnerStep.java b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/ScenarioRunnerStep.java new file mode 100644 index 0000000..25e4f22 --- /dev/null +++ b/resilience-bench/operator/src/main/java/br/unifor/ppgia/resiliencebench/scenarioexec/ScenarioRunnerStep.java @@ -0,0 +1,12 @@ +package br.unifor.ppgia.resiliencebench.scenarioexec; + +import br.unifor.ppgia.resiliencebench.execution.scenario.Scenario; +import io.fabric8.kubernetes.client.KubernetesClient; + +public abstract class ScenarioRunnerStep { + + public ScenarioRunnerStep(KubernetesClient kubernetesClient) { + } + + public abstract void run(Scenario scenario); +} diff --git a/resilience-bench/operator/src/main/resources/k6-resource.yaml b/resilience-bench/operator/src/main/resources/k6-resource.yaml deleted file mode 100644 index 11443e2..0000000 --- a/resilience-bench/operator/src/main/resources/k6-resource.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: k6.io/v1alpha1 -kind: K6 -metadata: - name: k6-sample -spec: - parallelism: 4 - script: - configMap: - name: crocodile-stress-test - file: test.js diff --git a/resilience-bench/operator/src/test/java/br/unifor/ppgia/resiliencebench/ScenarioRunnerE2ETest.java b/resilience-bench/operator/src/test/java/br/unifor/ppgia/resiliencebench/ScenarioRunnerE2ETest.java deleted file mode 100644 index c048acd..0000000 --- a/resilience-bench/operator/src/test/java/br/unifor/ppgia/resiliencebench/ScenarioRunnerE2ETest.java +++ /dev/null @@ -1,63 +0,0 @@ -package br.unifor.ppgia.resiliencebench; - -import br.unifor.ppgia.resiliencebench.execution.scenario.Scenario; -import br.unifor.ppgia.resiliencebench.modeling.benchmark.Benchmark; -import br.unifor.ppgia.resiliencebench.modeling.service.ResilientService; -import br.unifor.ppgia.resiliencebench.modeling.workload.Workload; -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.junit.AbstractOperatorExtension; -import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import static java.util.concurrent.TimeUnit.MINUTES; -import static org.awaitility.Awaitility.await; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -public class ScenarioRunnerE2ETest { - @RegisterExtension - static AbstractOperatorExtension operator = LocallyRunOperatorExtension.builder() - .waitForNamespaceDeletion(true) - .oneNamespacePerClass(true) - .withReconciler(new ResilienceServiceReconciler()) - .withReconciler(new BenchmarkReconciler()) - .build(); - - public static void awaitForCreation(Class clazz, String name) { - await().atMost(5, MINUTES).untilAsserted(() -> - assertNotNull(operator.resources(clazz).withName(name).get()) - ); - } - - public static T create(Class resourceClass, String name) { - return operator.resources(resourceClass).load(resourceClass.getResourceAsStream("/e2e-scenario-runner/" + name + ".yaml")).create(); - } - - @Test - public void creationTest() { - var workload = create(Workload.class, "workload"); - awaitForCreation(Workload.class, workload.getMetadata().getName()); - - var service1 = create(ResilientService.class, "productpage-service"); - awaitForCreation(ResilientService.class, service1.getMetadata().getName()); - - var service2 = create(ResilientService.class, "ratings-service"); - awaitForCreation(ResilientService.class, service2.getMetadata().getName()); - - var benchmark = create(Benchmark.class, "benchmark"); - awaitForCreation(Benchmark.class, benchmark.getMetadata().getName()); - await().atMost(5, MINUTES).untilAsserted(() -> - assertFalse(operator.resources(Scenario.class).list().getItems().isEmpty())); - - var scenarios = operator.resources(Scenario.class).list().getItems(); - var scenario = scenarios.get(0); - - var runner = new ScenarioRunner(operator.getKubernetesClient(), null); - runner.run(scenario.getMetadata().getNamespace(), scenario.getMetadata().getName()); - -// var resource = operator.resources(Benchmark.class).load(getClass().getResourceAsStream("/benchmark-sample.yaml")); -// var created = resource.create(); -// assertNotNull(created); - } -} diff --git a/resilience-bench/operator/src/test/java/br/unifor/ppgia/resiliencebench/k6/K6WorkloadAdapterTest.java b/resilience-bench/operator/src/test/java/br/unifor/ppgia/resiliencebench/k6/K6WorkloadAdapterTest.java deleted file mode 100644 index ad7fab3..0000000 --- a/resilience-bench/operator/src/test/java/br/unifor/ppgia/resiliencebench/k6/K6WorkloadAdapterTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package br.unifor.ppgia.resiliencebench.k6; - -import br.unifor.ppgia.resiliencebench.execution.scenario.ScenarioWorkload; -import br.unifor.ppgia.resiliencebench.external.k6.K6WorkloadAdapter; -import br.unifor.ppgia.resiliencebench.modeling.workload.ScriptConfig; -import br.unifor.ppgia.resiliencebench.modeling.workload.Workload; -import br.unifor.ppgia.resiliencebench.modeling.workload.WorkloadSpec; -import br.unifor.ppgia.resiliencebench.support.ConfigMapReference; -import io.fabric8.kubernetes.api.model.GenericKubernetesResource; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import org.assertj.core.util.Lists; -import org.junit.jupiter.api.Test; - -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -class K6WorkloadAdapterTest { - - @Test - public void testAdapt() { - var workload = new Workload(); - var config = new ScriptConfig(new ConfigMapReference("configMap", "file")); - var workloadSpec = new WorkloadSpec(Lists.list(10, 20), 5000, "http://test.com/status/200", config); - workload.setSpec(workloadSpec); - workload.setMetadata(new ObjectMetaBuilder().withName("test").withNamespace("default").build()); - - var scenarioWorkload = new ScenarioWorkload(); - scenarioWorkload.setUsers(10); - scenarioWorkload.setWorkloadName("test"); - - var adapter = new K6WorkloadAdapter(); - var actual = adapter.adapt(workload, scenarioWorkload); - - assertEquals(actual.getMetadata().getName(), workload.getMetadata().getName()); - assertEquals(actual.getMetadata().getNamespace(), workload.getMetadata().getNamespace()); - assertEquals(actual.getKind(), "TestRun"); - assertEquals(actual.getApiVersion(), "k6.io/v1alpha1"); - - var spec = (Map)((GenericKubernetesResource) actual).getAdditionalProperties().get("spec"); - assertEquals(spec.get("parallelism"), 1); - assertEquals(spec.get("arguments"), "--vus 10 --tag workloadName=test --duration 5000s"); - var script = (Map) spec.get("script"); - var scriptConfig = (Map) script.get("config"); - assertEquals(scriptConfig.get("name"), "configMap"); - assertEquals(scriptConfig.get("file"), "file"); - } -} \ No newline at end of file diff --git a/resilience-bench/pom.xml b/resilience-bench/pom.xml deleted file mode 100644 index 91d0d53..0000000 --- a/resilience-bench/pom.xml +++ /dev/null @@ -1,123 +0,0 @@ - - - 4.0.0 - - br.unifor.ppgia - resilience-bench - 1.0.0 - pom - - - operator - runner - - - - 17 - 17 - 3.11.0 - 3.2.5 - 3.4.0 - 6.10.0 - 5.10.2 - - - - - - io.fabric8 - istio-client - ${fabric8-client.version} - - - io.fabric8 - kubernetes-httpclient-vertx - ${fabric8-client.version} - - - - - - - - - - - org.awaitility - awaitility - 4.2.0 - test - - - org.mockito - mockito-core - 5.10.0 - test - - - org.junit.jupiter - junit-jupiter-api - ${junit.version} - test - - - org.junit.jupiter - junit-jupiter-engine - ${junit.version} - test - - - org.junit.jupiter - junit-jupiter-params - ${junit.version} - test - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - ${maven-surefire-plugin.version} - - - **/*E2ETest.java - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.12.1 - - ${maven.compiler.source} - ${maven.compiler.target} - - - - org.apache.maven.plugins - maven-failsafe-plugin - 3.2.5 - - - - integration-test - verify - - - - **/*E2ETest.java - - - - - - - - - - - diff --git a/resilience-bench/runner/pom.xml b/resilience-bench/runner/pom.xml deleted file mode 100644 index 1b29e8e..0000000 --- a/resilience-bench/runner/pom.xml +++ /dev/null @@ -1,83 +0,0 @@ - - 4.0.0 - - - br.unifor.ppgia - resilience-bench - 1.0.0 - ../pom.xml - - - runner - 1.0.0 - - - 17 - 17 - 3.4.0 - - - - - br.unifor.ppgia - resilience-bench-operator - 1.0.0 - - - io.fabric8 - kubernetes-server-mock - 6.10.0 - test - - - io.fabric8 - kubernetes-junit-jupiter - 6.10.0 - test - - - - - - - com.google.cloud.tools - jib-maven-plugin - ${jib-maven-plugin.version} - - - gcr.io/distroless/java17-debian11 - - - resilience-bench-runner - - - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - - - - - k8s-e2e - - - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} - - - - - - diff --git a/resilience-bench/runner/src/main/java/br/unifor/ppgia/resiliencebench/Runner.java b/resilience-bench/runner/src/main/java/br/unifor/ppgia/resiliencebench/Runner.java deleted file mode 100644 index fe9ce95..0000000 --- a/resilience-bench/runner/src/main/java/br/unifor/ppgia/resiliencebench/Runner.java +++ /dev/null @@ -1,8 +0,0 @@ -package br.unifor.ppgia.resiliencebench; - -public class Runner { - - public static void main(String[] args) { - System.out.println("Hello, World!"); - } -}