Skip to content
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

Add performance test scripts #1671

Merged
merged 21 commits into from
Apr 4, 2022
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7affa4d
Add performance test scripts
kotwanikunal Feb 24, 2022
e93c8c7
Merge branch 'main' into perf-test
kotwanikunal Mar 2, 2022
7247194
Merge branch 'main' into perf-test
kotwanikunal Mar 2, 2022
440051d
Merge branch 'opensearch-project:main' into perf-test
kotwanikunal Mar 7, 2022
1347f5c
Update agent to docker
kotwanikunal Mar 3, 2022
5513bbf
Update parameter descriptions
kotwanikunal Mar 8, 2022
9fef51e
Merge branch 'opensearch-project:main' into perf-test
kotwanikunal Mar 11, 2022
7648533
Merge branch 'opensearch-project:main' into perf-test
kotwanikunal Mar 18, 2022
4f06122
Add parallel stages for performance tests, add tests
kotwanikunal Mar 18, 2022
57c8977
Update tests as per Python3.7
kotwanikunal Mar 18, 2022
e65e19e
Merge branch 'opensearch-project:main' into perf-test
kotwanikunal Mar 22, 2022
eee81d0
Add conditional stage for performance tests
kotwanikunal Mar 22, 2022
dea8876
Merge branch 'opensearch-project:main' into perf-test
kotwanikunal Mar 25, 2022
f9f5674
Add detailed job tests for performance tests
kotwanikunal Mar 25, 2022
c01ea40
Fix agent label for notifications
kotwanikunal Mar 25, 2022
75e8d53
Merge branch 'opensearch-project:main' into perf-test
kotwanikunal Mar 25, 2022
74bf99a
Run tests against actual Jenkins perf-test job
kotwanikunal Mar 25, 2022
a19db11
Update secure mode flag name, pythonify branch code
kotwanikunal Apr 1, 2022
fad13db
Fix notifications for perf-tests
kotwanikunal Apr 4, 2022
f79d5bd
Merge branch 'opensearch-project:main' into perf-test
kotwanikunal Apr 4, 2022
a2a40d5
Update perf-test jenkins call stack
kotwanikunal Apr 4, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pytest = "*"
coverage = "~=4.5.4"
pytest-cov = "~=2.10.0"
jproperties = "~=2.1.1"
retry = "~=0.9"
sortedcontainers = "*"
cerberus = "~=1.3.4"
psutil = "~=5.8"
Expand Down
209 changes: 117 additions & 92 deletions Pipfile.lock

Large diffs are not rendered by default.

224 changes: 224 additions & 0 deletions jenkins/opensearch/perf-test.jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
lib = library(identifier: "jenkins@20211118", retriever: legacySCM(scm))

kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
pipeline {
agent none
options {
timeout(time: 10, unit: 'HOURS')
gaiksaya marked this conversation as resolved.
Show resolved Hide resolved
}
environment {
AGENT_LABEL = 'Jenkins-Agent-al2-x64-c54xlarge-Docker-Host'
AGENT_IMAGE = 'opensearchstaging/ci-runner:ci-runner-centos7-v1'
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
BUNDLE_MANIFEST = 'bundle-manifest.yml'
}
parameters {
string(
name: 'GITHUB_TOKEN',
description: 'Github token for account access.',
trim: true
)
string(
name: 'BUNDLE_MANIFEST_URL',
description: 'The bundle manifest URL, e.g. https://ci.opensearch.org/ci/dbc/distribution-build-opensearch/1.2.2/98/linux/x64/builds/opensearch/manifest.yml.',
trim: true
)
string(
defaultValue: 'nyc_taxis',
name: 'TEST_WORKLOAD',
description: 'The workload name from OpenSearch Benchmark Workloads for Mensor (internal client).',
trim: true
)
string(
defaultValue: '1',
name: 'TEST_ITERATIONS',
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
description: 'Number of times to run a workload for Mensor (internal client).',
trim: true
)
string(
defaultValue: '0',
name: 'WARMUP_ITERATIONS',
description: 'Number of times to run a workload before collecting data for Mensor (internal client).',
trim: true
)
}

stages {
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
stage('validate-and-set-parameters') {
agent {
docker {
label AGENT_LABEL
image AGENT_IMAGE
alwaysPull true
}
}
steps {
script {
if (BUNDLE_MANIFEST_URL == '') {
currentBuild.result = 'ABORTED'
error("Performance Tests failed to start. Missing parameter: BUNDLE_MANIFEST_URL.")
}
if (GITHUB_TOKEN == '') {
currentBuild.result = 'ABORTED'
error("Performance Tests failed to start. Missing parameter: GITHUB_TOKEN.")
}
if (TEST_ITERATIONS != null && !TEST_ITERATIONS.isInteger()) {
currentBuild.result = 'ABORTED'
error("Performance Tests failed to start. Invalid value for parameter: TEST_ITERATIONS. Value should be an integer.")
}
if (WARMUP_ITERATIONS != null && !WARMUP_ITERATIONS.isInteger()) {
currentBuild.result = 'ABORTED'
error("Performance Tests failed to start. Invalid value for parameter: WARMUP_ITERATIONS. Value should be an integer.")
}
def bundleManifestObj = downloadBuildManifest(
url: BUNDLE_MANIFEST_URL,
path: BUNDLE_MANIFEST
)
String buildId = bundleManifestObj.getArtifactBuildId()
env.BUILD_ID = buildId
env.HAS_SECURITY = bundleManifestObj.components.containsKey("security")
env.ARCHITECTURE = bundleManifestObj.getArtifactArchitecture()
}
}
}
stage('perf-test') {
parallel {
stage('test-with-security') {
agent {
docker {
label AGENT_LABEL
image AGENT_IMAGE
alwaysPull true
}
}
when {
expression { HAS_SECURITY == true }
}
steps {
script {
def bundleManifestObj = downloadBuildManifest(
url: BUNDLE_MANIFEST_URL,
path: BUNDLE_MANIFEST
)
echo "BUNDLE_MANIFEST: ${BUNDLE_MANIFEST}"
echo "BUILD_ID: ${BUILD_ID}"
echo "Architecture: ${ARCHITECTURE}"

lib.jenkins.Messages.new(this).add('perf-test', "Performance tests for #${BUILD_ID}")

runPerfTestScript(bundleManifest: "${BUNDLE_MANIFEST}",
buildId: "${BUILD_ID}",
architecture: "${ARCHITECTURE}",
insecure: false,
workload: TEST_WORKLOAD,
testIterations: TEST_ITERATIONS,
warmupIterations: WARMUP_ITERATIONS)

lib.jenkins.Messages.new(this).add('perf-test', "Performance tests with security for ${BUILD_ID} completed")
}
}
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
post {
success {
script {
uploadTestResults(
buildManifestFileName: "${BUNDLE_MANIFEST}",
jobName: 'perf-test',
buildNumber: "${BUILD_ID}"
)
}
postCleanup()
}
failure {
postCleanup()
}
aborted {
postCleanup()
}
}
}
stage('test-without-security') {
agent {
docker {
label AGENT_LABEL
image AGENT_IMAGE
alwaysPull true
}
}
steps {
script {
def bundleManifestObj = downloadBuildManifest(
url: BUNDLE_MANIFEST_URL,
path: BUNDLE_MANIFEST
)

echo "BUNDLE_MANIFEST: ${BUNDLE_MANIFEST}"
echo "BUILD_ID: ${BUILD_ID}"
echo "Architecture: ${ARCHITECTURE}"

lib.jenkins.Messages.new(this).add('perf-test', "Performance tests for #${BUILD_ID}")

runPerfTestScript(bundleManifest: "${BUNDLE_MANIFEST}",
buildId: "${BUILD_ID}",
architecture: "${ARCHITECTURE}",
insecure: true,
workload: TEST_WORKLOAD,
testIterations: TEST_ITERATIONS,
warmupIterations: WARMUP_ITERATIONS)

lib.jenkins.Messages.new(this).add('perf-test', "Performance tests without security for ${BUILD_ID} completed")
}
}
post {
success {
script {
uploadTestResults(
buildManifestFileName: "${BUNDLE_MANIFEST}",
jobName: 'perf-test',
buildNumber: "${BUILD_ID}"
)
}
postCleanup()
}
failure {
postCleanup()
}
aborted {
postCleanup()
}
}
}
}
}
}

post {
success {
node(AGENT_LABEL) {
script {
def stashed = lib.jenkins.Messages.new(this).get(['perf-test'])
publishNotification(
icon: ':white_check_mark:',
message: 'Performance Tests Successful',
extra: stashed,
credentialsId: 'INTEG_TEST_WEBHOOK',
)

postCleanup()
}
}
}
failure {
node(AGENT_LABEL) {
script {
def stashed = lib.jenkins.Messages.new(this).get(['perf-test'])
publishNotification(
icon: ':warning:',
message: 'Failed Performance Tests',
extra: stashed,
credentialsId: 'INTEG_TEST_WEBHOOK',
)

postCleanup()
}
}
}
}
}
20 changes: 16 additions & 4 deletions src/run_perf_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import sys

import yaml
from retry.api import retry_call

from git.git_repository import GitRepository
from manifests.bundle_manifest import BundleManifest
Expand All @@ -35,6 +36,9 @@ def main():
parser.add_argument("--bundle-manifest", type=argparse.FileType("r"), help="Bundle Manifest file.", required=True)
parser.add_argument("--stack", dest="stack", help="Stack name for performance test")
parser.add_argument("--config", type=argparse.FileType("r"), help="Config file.", required=True)
parser.add_argument("--force-insecure-mode", dest="insecure", action="store_true",
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
help="Force the security of the cluster to be disabled.",
default=False)
parser.add_argument("--keep", dest="keep", action="store_true", help="Do not delete the working temporary directory.")
parser.add_argument("--workload", default="nyc_taxis", help="Mensor (internal client) param - Workload name from OpenSeach Benchmark Workloads")
parser.add_argument("--workload-options", default="{}", help="Mensor (internal client) param - Json object with OpenSearch Benchmark arguments")
Expand All @@ -45,14 +49,22 @@ def main():
manifest = BundleManifest.from_file(args.bundle_manifest)
config = yaml.safe_load(args.config)

security = "security" in manifest.components and not args.insecure
if security:
tests_dir = os.path.join(os.getcwd(), "test-results", "perf-test", "with-security")
else:
tests_dir = os.path.join(os.getcwd(), "test-results", "perf-test", "without-security")
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
os.makedirs(tests_dir, exist_ok=True)
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved

with TemporaryDirectory(keep=args.keep, chdir=True) as work_dir:
current_workspace = os.path.join(work_dir.name, "infra")
with GitRepository(get_infra_repo_url(), "main", current_workspace):
security = "security" in manifest.components
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we get security from bundle manifest?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would prevent us from parallelizing the tests in the future. We want to kick off two tests for the security based bundles, which can be done from the Jenkins pipeline

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like security enabled/disabled should be decided based on security param in manifest.components. To parallelize the test we can pass 2 different bundle manifest, one with security enabled and other disabled.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will have to publish these manifests to our buckets and distributions, which we do not currently.
Moving it out additionally gives more flexibility for extensibility moving forward for other use cases rather than updating/adding manifests for every build.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do publish manifests to our buckets. without-security tests is something that will run irrespective of what's in the manifest but for with-security can we add a check in the codebase and then start the test only if the security component is present?
If the performance tests are to be run nightly it is highly possible that security component is not present initially in the manifest.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refer to the comment below - #1671 (comment)

What I was trying to say is - we have a single manifest which might or might not have security in the components. By enabling the logic at the Jenkins level, we can execute the two runs in parallel, giving us more control over the test runs and statuses.

Also, it is based directly on the manifest. Look here for more - https://github.com/opensearch-project/opensearch-build/pull/1671/files#diff-4debf5e3ece07145d8395f15df88f49b8b784a274cead94e2a94c8b7152c11efR75

All I am doing is pulling out that logic for better control at the top level of job execution.

with WorkingDirectory(current_workspace):
with PerfTestCluster.create(manifest, config, args.stack, security, current_workspace) as (test_cluster_endpoint, test_cluster_port):
perf_test_suite = PerfTestSuite(manifest, test_cluster_endpoint, security, current_workspace, args)
perf_test_suite.execute()
with PerfTestCluster.create(manifest, config, args.stack, security, current_workspace) \
as (test_cluster_endpoint, test_cluster_port):
perf_test_suite = PerfTestSuite(manifest, test_cluster_endpoint, security,
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
current_workspace, tests_dir, args)
retry_call(perf_test_suite.execute, tries=3, delay=60, backoff=2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we expose retry options to the command line?

Copy link
Member Author

@kotwanikunal kotwanikunal Apr 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems like a good option to have. I do have a few things to follow up on post - based on the test run outcomes once everything is up and running.
Added an issue here for tracking - opensearch-project/OpenSearch#2718



if __name__ == "__main__":
Expand Down
23 changes: 14 additions & 9 deletions src/test_workflow/perf_test/perf_test_suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ class PerfTestSuite:
"""
Represents a performance test suite. This class runs rally test on the deployed cluster with the provided IP.
"""

def __init__(self, bundle_manifest, endpoint, security, current_workspace, args):
def __init__(self, bundle_manifest, endpoint, security, current_workspace, test_results_path, args):
self.manifest = bundle_manifest
self.work_dir = "mensor/"
self.endpoint = endpoint
Expand All @@ -23,16 +22,22 @@ def __init__(self, bundle_manifest, endpoint, security, current_workspace, args)
f" --warmup-iters {self.args.warmup_iters} --test-iters {self.args.test_iters}"
)

if test_results_path is not None:
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
self.command = (
f"pipenv run python test_config.py -i {self.endpoint} -b {self.manifest.build.id}"
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
f" -a {self.manifest.build.architecture} -p {test_results_path}"
f" --workload {self.args.workload} --workload-options '{self.args.workload_options}'"
f" --warmup-iters {self.args.warmup_iters} --test-iters {self.args.test_iters}"
)

def execute(self):
try:
with WorkingDirectory(self.work_dir):
dir = os.getcwd()
subprocess.check_call("python3 -m pipenv install", cwd=dir, shell=True)
subprocess.check_call("pipenv install", cwd=dir, shell=True)

current_workspace = os.path.join(self.current_workspace, self.work_dir)
with WorkingDirectory(current_workspace):
subprocess.check_call("pipenv install", cwd=current_workspace, shell=True)
if self.security:
subprocess.check_call(f"{self.command} -s", cwd=dir, shell=True)
subprocess.check_call(f"{self.command} -s", cwd=current_workspace, shell=True)
else:
subprocess.check_call(f"{self.command}", cwd=dir, shell=True)
subprocess.check_call(f"{self.command}", cwd=current_workspace, shell=True)
finally:
os.chdir(self.current_workspace)
5 changes: 4 additions & 1 deletion test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ case $1 in
"bwc-test")
"$DIR/run.sh" "$DIR/src/run_bwc_test.py" "${@:2}"
;;
"perf-test")
"$DIR/run.sh" "$DIR/src/run_perf_test.py" "${@:2}"
;;
*)
echo "Invalid test suite, run ./test.sh integ-test|bwc-test."
echo "Invalid test suite, run ./test.sh integ-test|bwc-test|perf-test."
exit 1
;;
esac
Expand Down
9 changes: 9 additions & 0 deletions tests/data/perf-test-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
Description: Configuration file to store contants required to run rally for performance test
Constants:
Repository: https://github.com/opensearch-project/
SecurityGroupId: test-security
VpcId: test-vpc
AccountId: 123456
Region: eu-east-1
Role: test-role-name
Loading