diff --git a/benchmark/README.md b/benchmark/README.md new file mode 100644 index 000000000..aad58267d --- /dev/null +++ b/benchmark/README.md @@ -0,0 +1,5 @@ +# How to run benchmarks + +* Setup your cortex instance with a user and api key +* Copy the `application.conf.sample` file to `application.conf` and fill in the details +* Run with sbt `benchmark/Gatling/testOnly ` diff --git a/benchmark/src/test/resources/.gitignore b/benchmark/src/test/resources/.gitignore new file mode 100644 index 000000000..e68401bff --- /dev/null +++ b/benchmark/src/test/resources/.gitignore @@ -0,0 +1 @@ +application.conf \ No newline at end of file diff --git a/benchmark/src/test/resources/application.conf.sample b/benchmark/src/test/resources/application.conf.sample new file mode 100644 index 000000000..4c9a20a71 --- /dev/null +++ b/benchmark/src/test/resources/application.conf.sample @@ -0,0 +1,11 @@ +user { + login = "gatling@cortex.io" + password = gatling + # Set value + apiKey = "" +} + +baseUrl = "http://localhost:9000" + +# Id of an analyzer +analyzer = "" \ No newline at end of file diff --git a/benchmark/src/test/resources/logback.xml b/benchmark/src/test/resources/logback.xml new file mode 100644 index 000000000..5d3831f4e --- /dev/null +++ b/benchmark/src/test/resources/logback.xml @@ -0,0 +1,25 @@ + + + + + + %d{HH:mm:ss.SSS} [%-5level] %logger{15} - %msg%n%rEx + + false + + + + + + + + + + + + + + + + + diff --git a/benchmark/src/test/scala/org/thp/cortex/benchmark/AnalyzerSimulation.scala b/benchmark/src/test/scala/org/thp/cortex/benchmark/AnalyzerSimulation.scala new file mode 100644 index 000000000..84b3ec9f2 --- /dev/null +++ b/benchmark/src/test/scala/org/thp/cortex/benchmark/AnalyzerSimulation.scala @@ -0,0 +1,53 @@ +package org.thp.cortex.benchmark + +import io.gatling.core.Predef._ +import io.gatling.http.Predef._ + +import java.util.UUID +import scala.concurrent.duration.DurationInt + +class AnalyzerSimulation extends Simulation { + + val httpProtocal = http + .baseUrl(Configuration.baseUrl) + .authorizationHeader(s"Bearer ${Configuration.userApiKey}") + + val analyzer = Configuration.conf.getString("analyzer") + + val random = UUID.randomUUID() + + val scn = scenario("Analyzer") + .exec( + http("launch analyzer") + .post(s"/api/analyzer/${analyzer}/run") + .body(StringBody { session => + s"""{ + | "dataType": "domain", + | "data": "${random}-${session.userId}", + | "pap": 2, + | "tlp": 2, + | "message": "", + | "parameters": {} + |}""".stripMargin + }) + .asJson + .check(status.is(200)) + .check(bodyString.saveAs("job")) + .check(jsonPath("$._id").saveAs("jobId")) + ) + .exitHereIfFailed + .exec(session => session.set("i", 0)) + .exec( + doWhile(session => session("i").as[Int] < 10 && session("jobStatus").asOption[String].contains("Waiting")) { + exec( + http("get job status") + .get("/api/job/#{jobId}/waitreport?atMost=1seconds") + .check(jsonPath("$.status").saveAs("jobStatus")) + ).exec(session => session.set("i", session("i").as[Int] + 1)) + } + ) + + setUp( + scn.inject(constantConcurrentUsers(6).during(60.seconds)) + ).protocols(httpProtocal) +} diff --git a/benchmark/src/test/scala/org/thp/cortex/benchmark/Configuration.scala b/benchmark/src/test/scala/org/thp/cortex/benchmark/Configuration.scala new file mode 100644 index 000000000..b102f545e --- /dev/null +++ b/benchmark/src/test/scala/org/thp/cortex/benchmark/Configuration.scala @@ -0,0 +1,13 @@ +package org.thp.cortex.benchmark + +import com.typesafe.config.{Config, ConfigFactory} + +object Configuration { + val conf: Config = ConfigFactory.load() + + val userLogin = conf.getString("user.login") + val userPassword = conf.getString("user.password") + val userApiKey = conf.getString("user.apiKey") + + val baseUrl = conf.getString("baseUrl") +} diff --git a/build.sbt b/build.sbt index 94b60e4ae..3926c7073 100644 --- a/build.sbt +++ b/build.sbt @@ -63,3 +63,14 @@ lazy val cortexWithDeps = (project in file("target/docker-withdeps")) Docker / version := version.value + "-withdeps", Docker / packageName := "cortex" ) + +lazy val benchmark = (project in file("benchmark")) + .enablePlugins(GatlingPlugin) + .settings( + scalaVersion := "2.13.11", + dependencyOverrides := Seq.empty, + libraryDependencies ++= Seq( + Dependencies.gatlingHighcharts % Test, + Dependencies.gatlingTestFramework % Test + ) + ) diff --git a/conf/logback.xml b/conf/logback.xml index 864f2b5f2..4612b11dc 100644 --- a/conf/logback.xml +++ b/conf/logback.xml @@ -13,7 +13,7 @@ - %coloredLevel %logger{15} - %message%n%xException{10} + %coloredLevel %logger{15} - %message%n%xException diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 7d07b526f..8b71d6a49 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -23,4 +23,7 @@ object Dependencies { val dockerClient = "com.spotify" % "docker-client" % "8.16.0" val akkaCluster = "com.typesafe.akka" %% "akka-cluster" % play.core.PlayVersion.akkaVersion val akkaClusterTyped = "com.typesafe.akka" %% "akka-cluster-typed" % play.core.PlayVersion.akkaVersion + + val gatlingHighcharts = "io.gatling.highcharts" % "gatling-charts-highcharts" % "3.9.5" + val gatlingTestFramework = "io.gatling" % "gatling-test-framework" % "3.9.5" } diff --git a/project/plugins.sbt b/project/plugins.sbt index 758cc8da9..1bbdccd13 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -7,3 +7,4 @@ addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.19") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") addSbtPlugin("org.thehive-project" % "sbt-github-changelog" % "0.4.0") addSbtPlugin("io.github.siculo" %% "sbt-bom" % "0.3.0") +addSbtPlugin("io.gatling" % "gatling-sbt" % "4.4.0")