Skip to content

Commit

Permalink
feat(loadtest): Skeleton loadtest (#1270)
Browse files Browse the repository at this point in the history
Sets up the bare minimum structure for load tests in orca. This will
be progressively enhanced to help enable vetting of nu-orca's reliability
characteristics.
  • Loading branch information
robzienert committed Apr 10, 2017
1 parent fefe9c6 commit fdc6621
Show file tree
Hide file tree
Showing 10 changed files with 259 additions and 1 deletion.
5 changes: 5 additions & 0 deletions orca-loadtest/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# orca-loadtest

This module is a work in progress for testing the reliability and
scale characteristics of Orca. It is not run as part of CI: It is
currently intended for ad-hoc execution.
27 changes: 27 additions & 0 deletions orca-loadtest/orca-loadtest.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
tasks.compileGroovy.enabled = false

apply plugin: 'application'
apply plugin: 'scala'

ext {
scalaVersion = "2.11.8"
gatlingVersion = "2.2.4"
}

dependencies {
compile "org.scala-lang:scala-library:${scalaVersion}"
compile "org.scala-lang:scala-compiler:${scalaVersion}"

compile "io.gatling:gatling-core:${gatlingVersion}"
compile "io.gatling:gatling-http:${gatlingVersion}"
compile "io.gatling.highcharts:gatling-charts-highcharts:${gatlingVersion}"

compile 'com.owlike:genson-scala_2.11:1.4'
}

ScalaCompileOptions.metaClass.daemonServer = true
ScalaCompileOptions.metaClass.fork = true
ScalaCompileOptions.metaClass.useAnt = false
ScalaCompileOptions.metaClass.useCompileDaemon = false

mainClassName = "com.netflix.spinnaker.orca.loadtest.OrcaSimulationEngine"
11 changes: 11 additions & 0 deletions orca-loadtest/src/main/resources/orca-simulation.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
service.orca {
serviceUrl = "http://localhost:8083"

rampUpPeriod = 10
duration = 30

submitOrchestration {
rampUsersPerSec = 1
rampUsersTo = 3
}
}
16 changes: 16 additions & 0 deletions orca-loadtest/src/main/resources/request-bodies/wait.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"application": "spindemo",
"name": "Wait on v2 test",
"stages": [
{
"requisiteStageRefIds": [],
"refId": "1",
"type": "wait",
"name": "Wait",
"waitTime": 5
}
],
"limitConcurrent": true,
"parallel": true,
"appConfig": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2017 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.spinnaker.orca.loadtest

import com.netflix.spinnaker.orca.loadtest.scenarios.OrcaScenarios
import io.gatling.core.Predef._
import io.gatling.core.structure.PopulationBuilder
import io.gatling.http.Predef._

import scala.collection.mutable.ListBuffer
import scala.concurrent.duration._

class OrcaSimulation extends Simulation {

val config = new OrcaSimulationConfig(OrcaSimulationConfig.loadConfig())

// TODO rz - Make a circular feeder of RawFileBody instead. This might take a bit more digging.
lazy val CircularTaskFeeder: Feeder[String] = {
Iterator.continually(Map(
"body" -> ""
))
}

setUp {
createScenarioList()
}

def createScenarioList(): List[PopulationBuilder] = {
val scenarios: ListBuffer[PopulationBuilder] = new ListBuffer()

if (config.submitOrchestration.rampUsersTo > 0) {
scenarios.append(
OrcaScenarios.submitOrchestration(CircularTaskFeeder).inject(
rampUsersPerSec(config.submitOrchestration.rampUsersPerSec) to config.submitOrchestration.rampUsersTo during config.rampUpPeriod.seconds,
constantUsersPerSec(config.submitOrchestration.rampUsersTo) during config.duration
).protocols(http.baseURL(config.serviceUrl))
)
}

scenarios.toList
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2017 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.spinnaker.orca.loadtest

import java.io.File

import com.typesafe.config.{Config, ConfigFactory}

object OrcaSimulationConfig {

def loadConfig(): Config = {
val configFilePath = sys.props.get("simulation.config")

if (configFilePath.isDefined) {
val file = new File(configFilePath.get)
ConfigFactory.parseFile(file)
} else {
ConfigFactory.parseResources("orca-simulation.conf")
}
}
}

class OrcaSimulationConfig(config: Config) {

val serviceUrl = config.getString("service.orca.serviceUrl")

val rampUpPeriod = config.getInt("service.orca.rampUpPeriod")
val duration = config.getInt("service.orca.duration")

val submitOrchestration = new {
val rampUsersPerSec = config.getInt("service.orca.submitOrchestration.rampUsersPerSec")
val rampUsersTo = config.getInt("service.orca.submitOrchestration.rampUsersTo")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2017 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.spinnaker.orca.loadtest

import io.gatling.app.Gatling
import io.gatling.core.config.GatlingPropertiesBuilder

// TODO rz - Ultra simple implementation at this point. Will eventually create a clouddriver engine if we need that
// level of load (but prob not).
object OrcaSimulationEngine extends App {

// TODO rz - Add ~/.spinnaker/gatling.conf for setting up SSL
val props = new GatlingPropertiesBuilder
props.simulationClass(classOf[OrcaSimulation].getName)
props.resultsDirectory("build/reports/gatling")
props.binariesDirectory("build/classes/main")
props.bodiesDirectory(getClass.getClassLoader.getResource("request-bodies").getPath)
props.dataDirectory(getClass.getClassLoader.getResource("data").getPath)

Gatling.fromMap(props.build)
sys.exit()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2017 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.spinnaker.orca.loadtest.actions

import io.gatling.core.Predef._
import io.gatling.http.Predef._
import io.gatling.http.request.builder.HttpRequestBuilder

object OrcaActions {

val postOrchestration: HttpRequestBuilder = http(s"Orchestrate task")
.post("/orchestrate")
// TODO: get the body from the session instead
.header("Content-Type", "application/json")
.body(RawFileBody("wait.json"))
.check(status is 200)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2017 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.spinnaker.orca.loadtest.scenarios

import com.netflix.spinnaker.orca.loadtest.actions.OrcaActions
import io.gatling.core.Predef._
import io.gatling.core.feeder.FeederBuilder
import io.gatling.core.structure.ScenarioBuilder

object OrcaScenarios {

def submitOrchestration(tasks: FeederBuilder[String]): ScenarioBuilder = {
scenario("Submit orchestration")
.feed(tasks)
.exec(OrcaActions.postOrchestration)
}
}

3 changes: 2 additions & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ include "orca-extensionpoint",
"orca-applications",
"orca-spring-batch",
"orca-pipelinetemplate",
"orca-validation"
"orca-validation",
"orca-loadtest"

rootProject.name = "orca"

Expand Down

0 comments on commit fdc6621

Please sign in to comment.