-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Migrations-1079 - Adding Benchmark Testing Environment #231
Changes from 3 commits
a7efcfe
b2aa2d3
cb25bd7
054b32a
c0d7ec8
c458778
e2e7ef2
5792426
a60b081
788e169
534a146
126f372
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
plugins { | ||
id 'org.opensearch.migrations.java-library-conventions' | ||
id "com.avast.gradle.docker-compose" version "0.16.12" | ||
id "com.bmuschko.docker-java-application" version "9.3.1" | ||
} | ||
|
||
import java.security.MessageDigest | ||
import com.bmuschko.gradle.docker.tasks.image.DockerBuildImage | ||
import org.apache.tools.ant.taskdefs.condition.Os | ||
|
||
def calculateDockerHash(String projectName) { | ||
MessageDigest digest = MessageDigest.getInstance('SHA-256') | ||
fileTree("src/main/docker/${projectName}") | ||
.each( file -> | ||
file.withInputStream { is -> | ||
var buffer = new byte[1024] | ||
int read | ||
while ((read = is.read(buffer)) != -1) { | ||
digest.update(buffer, 0, read) | ||
} | ||
} | ||
) | ||
return digest.digest().encodeHex().toString() | ||
} | ||
|
||
dependencies { | ||
implementation project(':trafficCaptureProxyServer') | ||
testImplementation project(':trafficCaptureProxyServerTest') | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can fall away by merging these changes into the ...ProxyServerTest directory |
||
|
||
def dockerFilesForExternalServices = [ | ||
"nginx": "nginx" | ||
] | ||
|
||
// Create the static docker files that aren't hosting migrations java code from this repo | ||
dockerFilesForExternalServices.each { projectName, dockerImageName -> | ||
task("buildDockerImage_${projectName}", type: DockerBuildImage) { | ||
def hash = calculateDockerHash(projectName) | ||
images.add("testenv/${dockerImageName}:$hash") | ||
images.add("testenv/${dockerImageName}:latest") | ||
inputDir = project.file("src/main/docker/${projectName}") | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be refactored into a common shared class and imported by both projects |
||
} | ||
|
||
def javaContainerServices = [ | ||
"trafficCaptureProxyServer": "capture_proxy", | ||
"trafficCaptureProxyServerTest": "jmeter" | ||
] | ||
def baseImageProjectOverrides = [ | ||
"nginx": "nginx" | ||
] | ||
|
||
javaContainerServices.each { projectName, dockerImageName -> | ||
def dockerBuildDir = "build/docker/${projectName}" | ||
def artifactsDir = "${dockerBuildDir}/jars"; | ||
task("copyArtifact_${projectName}", type: Copy) { | ||
dependsOn ":${projectName}:build" | ||
dependsOn ":${projectName}:jar" | ||
from { project(":${projectName}").configurations.findByName("runtimeClasspath").files } | ||
from { project(":${projectName}").tasks.getByName('jar') } | ||
into artifactsDir | ||
include "*.jar" | ||
duplicatesStrategy = DuplicatesStrategy.WARN | ||
|
||
if (projectName == "trafficCaptureProxyServerTest") { | ||
include "*.properties" | ||
from project.file("src/main/docker/${projectName}/jmeter.properties").absolutePath | ||
into "${dockerBuildDir}" | ||
} | ||
} | ||
|
||
task "createDockerfile_${projectName}"(type: com.bmuschko.gradle.docker.tasks.image.Dockerfile) { | ||
dependsOn "copyArtifact_${projectName}" | ||
destFile = project.file("${dockerBuildDir}/Dockerfile") | ||
def baseImageOverrideProjectName = baseImageProjectOverrides.get(projectName) | ||
if (baseImageOverrideProjectName) { | ||
def dependentDockerImageName = dockerFilesForExternalServices.get(baseImageOverrideProjectName) | ||
def hashNonce = calculateDockerHash(baseImageOverrideProjectName) | ||
from "testenv/${dependentDockerImageName}:${hashNonce}" | ||
dependsOn "buildDockerImage_${baseImageOverrideProjectName}" | ||
runCommand("sed -i -e \"s|mirrorlist=|#mirrorlist=|g\" /etc/yum.repos.d/CentOS-* ; sed -i -e \"s|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g\" /etc/yum.repos.d/CentOS-*") | ||
runCommand("yum -y install nmap-ncat") | ||
} else { | ||
from 'openjdk:11-jre' | ||
if (projectName == "trafficCaptureProxyServerTest") { | ||
instruction 'COPY jmeter.properties /jmeter.properties' | ||
} | ||
runCommand("apt-get update && apt-get install -y netcat") | ||
} | ||
|
||
copyFile("jars", "/jars") | ||
// can't set the environment variable from the runtimeClasspath because the Dockerfile is | ||
// constructed in the configuration phase and the classpath won't be realized until the | ||
// execution phase. Therefore, we need to have docker run the command to resolve the classpath | ||
// and it's simplest to pack that up into a helper script. | ||
runCommand("printf \"#!/bin/sh\\njava -cp `echo /jars/*.jar | tr \\ :` \\\"\\\$@\\\" \" > /runJavaWithClasspath.sh"); | ||
runCommand("chmod +x /runJavaWithClasspath.sh") | ||
// container stay-alive | ||
defaultCommand('tail', '-f', '/dev/null') | ||
//defaultCommand('/runJavaWithClasspath.sh', '...') | ||
} | ||
} | ||
|
||
(javaContainerServices).forEach { projectName, dockerImageName -> | ||
def dockerBuildDir = "build/docker/${projectName}" | ||
task "buildDockerImage_${projectName}"(type: DockerBuildImage) { | ||
dependsOn "createDockerfile_${projectName}" | ||
inputDir = project.file("${dockerBuildDir}") | ||
images.add("testenv/${dockerImageName}:${version}") | ||
images.add("testenv/${dockerImageName}:latest") | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be refactored into a common shared class and imported by both projects |
||
} | ||
|
||
dockerCompose { | ||
useComposeFiles.add("src/main/docker/docker-compose.yml") | ||
} | ||
|
||
task buildDockerImages { | ||
dependsOn buildDockerImage_nginx | ||
dependsOn buildDockerImage_trafficCaptureProxyServer | ||
dependsOn buildDockerImage_trafficCaptureProxyServerTest | ||
} | ||
|
||
tasks.getByName('composeUp') | ||
.dependsOn(tasks.getByName('buildDockerImages')) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
version: '3.7' | ||
services: | ||
nginx: | ||
image: 'testenv/nginx:latest' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: A slightly more descriptive name than 'nginx' might be helpful for others |
||
ports: | ||
- "8080:80" | ||
|
||
captureproxy: | ||
image: 'testenv/capture_proxy:latest' | ||
networks: | ||
- testing | ||
ports: | ||
- "9201:9201" | ||
command: /runJavaWithClasspath.sh org.opensearch.migrations.trafficcapture.proxyserver.Main --destinationUri http://nginx:80 --listenPort 9201 --noCapture | ||
depends_on: | ||
- nginx | ||
|
||
jmeter: | ||
image: 'testenv/jmeter:latest' | ||
networks: | ||
- testing | ||
command: /bin/sh -c "while sleep 10; do /runJavaWithClasspath.sh org.opensearch.migrations.trafficcapture.JMeterLoadTest -p 9201 -d captureproxy; done" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you want to run this within a loop? The JMeter configuration would be able to loop as many times as you'd like it to. Managing multiple processes in a container is less desirable than managing just one. |
||
depends_on: | ||
- captureproxy | ||
|
||
|
||
|
||
networks: | ||
testing: | ||
driver: bridge |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,13 @@ | ||
ARG NGINX_VERSION=1.23.4 | ||
FROM nginx:${NGINX_VERSION} as build | ||
|
||
RUN apt-get update | ||
RUN apt-get update && apt-get upgrade -y | ||
RUN apt-get install -y \ | ||
openssh-client \ | ||
git \ | ||
wget \ | ||
libxml2 \ | ||
libxslt1-dev \ | ||
libxml2-dev \ | ||
libxslt-dev \ | ||
libpcre3 \ | ||
libpcre3-dev \ | ||
zlib1g \ | ||
|
@@ -43,8 +43,12 @@ RUN NGINX_ARGS=$(nginx -V 2>&1 | sed -n -e 's/^.*arguments: //p') \ | |
|
||
FROM nginx:${NGINX_VERSION} | ||
|
||
RUN apt-get update | ||
RUN apt-get install -y vim | ||
# Switch to noninteractive mode to avoid interactive prompts during package installation | ||
ENV DEBIAN_FRONTEND=noninteractive | ||
|
||
RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends vim \ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this to install vim? What does the process need vim for? Please leave a comment, especially if it's just nice to have for somebody exec-ing a shell within the container. |
||
&& rm -rf /var/lib/apt/lists/* \ | ||
&& apt-get clean | ||
|
||
RUN /bin/bash -c '\ | ||
export HTMLDIR=/usr/share/nginx/html ; \ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,7 +49,7 @@ static class Parameters { | |
@Parameter(required = false, | ||
names = {"--noCapture"}, | ||
arity = 0, | ||
description = "Directory to store trace files in.") | ||
description = "If enabled, Does NOT store trace files in.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ... s/store trace files/capture traffic to ANY sink/ |
||
boolean noCapture; | ||
@Parameter(required = false, | ||
names = {"--kafkaConfigFile"}, | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,8 @@ | ||
package org.opensearch.migrations.trafficcapture; | ||
|
||
import com.beust.jcommander.JCommander; | ||
import com.beust.jcommander.Parameter; | ||
import com.beust.jcommander.ParameterException; | ||
import lombok.AllArgsConstructor; | ||
import org.apache.jmeter.assertions.ResponseAssertion; | ||
import org.apache.jmeter.control.LoopController; | ||
|
@@ -23,23 +26,49 @@ | |
|
||
public class JMeterLoadTest { | ||
|
||
static class Parameters { | ||
@Parameter(required = true, | ||
names = {"-p", "--port"}, | ||
description = "Port number") | ||
int backsidePort; | ||
@Parameter(required = true, | ||
names = {"-d", "--domain-name"}, | ||
description = "Domain name") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: s/domain-name/server-name/. |
||
String domainName; | ||
} | ||
|
||
public static Parameters parseArgs(String[] args) { | ||
Parameters p = new Parameters(); | ||
JCommander jCommander = new JCommander(p); | ||
try { | ||
jCommander.parse(args); | ||
return p; | ||
} catch (ParameterException e) { | ||
System.err.println(e.getMessage()); | ||
System.err.println("Got args: "+ String.join("; ", args)); | ||
jCommander.usage(); | ||
return null; | ||
} | ||
} | ||
|
||
public static void main(String[] args) { | ||
var params = parseArgs(args); | ||
StandardJMeterEngine jmeter = new StandardJMeterEngine(); | ||
File home = new File("."); | ||
JMeterUtils.setJMeterHome(home.getPath()); | ||
JMeterUtils.loadJMeterProperties("jmeter.properties"); | ||
JMeterUtils.loadJMeterProperties("/jmeter.properties"); | ||
|
||
String outputFileName = null; | ||
//outputFileName = "results.jtl"; | ||
ListedHashTree hashTree = createTestPlan(100, 8, 1, | ||
ListedHashTree hashTree = createTestPlan(params.domainName, params.backsidePort, 100, 8, 1, | ||
false, outputFileName); | ||
|
||
jmeter.configure(hashTree); | ||
jmeter.run(); | ||
} | ||
|
||
@NotNull | ||
private static ListedHashTree createTestPlan(int loopCount, int workerThreadCount, | ||
private static ListedHashTree createTestPlan(String domain, int port, int loopCount, int workerThreadCount, | ||
int summaryUpdateFrequencySeconds, boolean verifyResponse, | ||
String logOutputFileName) { | ||
ListedHashTree hashTree = new ListedHashTree(); | ||
|
@@ -62,12 +91,12 @@ private static ListedHashTree createTestPlan(int loopCount, int workerThreadCoun | |
{ | ||
var threadGroupHashTree = hashTree.add(testPlan, createThreadGroup("FireAndForgetGroup", | ||
loopCount, workerThreadCount, 1)); | ||
Arrays.stream(files).forEach(fr -> threadGroupHashTree.add(createHttpSampler(fr, "GET", verifyResponse))); | ||
Arrays.stream(files).forEach(fr -> threadGroupHashTree.add(createHttpSampler(domain, port, fr, "GET", verifyResponse))); | ||
} | ||
{ | ||
var threadGroupHashTree = hashTree.add(testPlan, createThreadGroup("TransactionGroup", | ||
loopCount, workerThreadCount, 1)); | ||
Arrays.stream(files).forEach(fr -> threadGroupHashTree.add(createHttpSampler(fr, "POST", verifyResponse))); | ||
Arrays.stream(files).forEach(fr -> threadGroupHashTree.add(createHttpSampler(domain, port, fr, "POST", verifyResponse))); | ||
} | ||
hashTree.add(testPlan, createResultCollector(logOutputFileName, summaryUpdateFrequencySeconds)); | ||
return hashTree; | ||
|
@@ -114,13 +143,13 @@ private static ResponseAssertion createResponseAssertion(char c, int count) { | |
} | ||
|
||
@NotNull | ||
private static HashTree createHttpSampler(FileReference fr, String method, boolean verifyResponse) { | ||
private static HashTree createHttpSampler(String domain, int port, FileReference fr, String method, boolean verifyResponse) { | ||
HTTPSampler httpSampler = new HTTPSampler(); | ||
httpSampler.setProperty(TestElement.TEST_CLASS, HTTPSampler.class.getName()); | ||
httpSampler.setName(method + " Sampler"); | ||
httpSampler.setMethod(method); | ||
httpSampler.setDomain("localhost"); | ||
httpSampler.setPort(80); | ||
httpSampler.setDomain(domain); | ||
httpSampler.setPort(port); | ||
httpSampler.setPath(fr.filename); | ||
var hashTree = new ListedHashTree(); | ||
hashTree.add(httpSampler) | ||
|
@@ -167,4 +196,4 @@ public static class FileReference { | |
public final int count; | ||
public final boolean verifyResponse; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be refactored into a common shared class and imported by both projects