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 1 commit
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
100 changes: 100 additions & 0 deletions jenkins/opensearch/perf-test.jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
lib = library(identifier: "jenkins@20211118", retriever: legacySCM(scm))

kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
pipeline {
agent none
environment {
AWS_ROLE_ARN = "arn:aws:iam::${AWS_ACCOUNT_PUBLIC}:role/opensearch-test"
AWS_ROLE_SESSION_NAME = "jenkins-test-session"
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
BUNDLE_MANIFEST = "bundle-manifest.yml"
}
tools {
jdk "JDK14"
maven "maven-3.8.2"
}
parameters {
string(
defaultValue: '',
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
name: 'GITHUB_TOKEN',
description: 'Github token for account access.',
trim: true
)
string(
defaultValue: '',
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: '',
name: 'AGENT_LABEL',
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
description: 'The agent label where the tests should be executed, e.g. Jenkins-Agent-al2-x64-c54xlarge-Docker-Host.',
trim: true
)
string(
defaultValue: '',
name: 'TEST_WORKLOAD',
description: 'The agent label where the tests should be executed, e.g. Jenkins-Agent-al2-x64-c54xlarge-Docker-Host.',
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
trim: true
)
string(
defaultValue: '',
name: 'TEST_ITERATIONS',
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
description: 'The agent label where the tests should be executed, e.g. Jenkins-Agent-al2-x64-c54xlarge-Docker-Host.',
trim: true
)
string(
defaultValue: '',
name: 'WARMUP_ITERATIONS',
description: 'The agent label where the tests should be executed, e.g. Jenkins-Agent-al2-x64-c54xlarge-Docker-Host.',
trim: true
)
}

stages {
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
stage('perf-test') {
agent {
node {
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
label "${AGENT_LABEL}"
}
}
steps {
script {
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")
echo "BUNDLE_MANIFEST: ${BUNDLE_MANIFEST}"
echo "BUILD_ID: ${BUILD_ID}"
echo "Security: ${HAS_SECURITY}"

runPerfTestScript(bundleManifest: "${BUNDLE_MANIFEST}",
buildId: "${BUILD_ID}",
security: false)

if(env.HAS_SECURITY) {
runPerfTestScript(bundleManifest: "${BUNDLE_MANIFEST}",
buildId: "${BUILD_ID}",
security: true)
}
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved

}
}

post {
success {
script {
uploadTestResults(
buildManifestFileName: "${BUNDLE_MANIFEST}",
jobName: 'perf-test',
buildNumber: "${BUILD_ID}"
)
}
cleanWs disableDeferredWipeout: true, deleteDirs: true
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}
}
17 changes: 14 additions & 3 deletions src/run_perf_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import argparse
import os
import sys
import time

import yaml

Expand All @@ -35,19 +36,29 @@ 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("--security", dest="security", action="store_true",
Copy link
Member

Choose a reason for hiding this comment

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

We already have a configuration file, and we have a manifest that has or does not have security, why are we promoting something so specific to a top level feature/option and how is it going to add up with that?

Copy link
Member Author

Choose a reason for hiding this comment

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

I am trying to parallelize the two jobs (security/non security) at the top level to get clear status and result visibility. Moving it at the python script level will require additional async logic to be added in to make the script execute in parallel.

Copy link
Member

Choose a reason for hiding this comment

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

The tooling is trying hard not to be too specific for OpenSearch/OpenSearch Dashboards so I'd think about this twice. My concern though is that with this change we have 2 ways to say "with security" and "without security", you should collapse it in 1 way as part of this change.

Copy link
Member Author

Choose a reason for hiding this comment

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

I have updated the config to be more appropriate with the general use case. I can work on abstracting it out/utilizing a test config for example, when we move to add more components to this script.

help="Security of the cluster should be True/False",
default=False)
parser.add_argument("--keep", dest="keep", action="store_true", help="Do not delete the working temporary directory.")
args = parser.parse_args()

manifest = BundleManifest.from_file(args.bundle_manifest)
config = yaml.safe_load(args.config)

tests_dir = os.path.join(os.getcwd(), "test-results", "perf-test")
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)
with PerfTestCluster.create(manifest, config, args.stack, args.security, current_workspace) \
as (test_cluster_endpoint, test_cluster_port):
# Stack creation returns control before user-data script execution is complete and the server starts
# Sleep helps with consistent service discovery and test initialization success.
time.sleep(120)
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
perf_test_suite = PerfTestSuite(manifest, test_cluster_endpoint, args.security, current_workspace,
tests_dir)
perf_test_suite.execute()


Expand Down
28 changes: 17 additions & 11 deletions src/test_workflow/perf_test/perf_test_suite.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import os
import subprocess

from system.working_directory import WorkingDirectory


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):
def __init__(self, bundle_manifest, endpoint, security, current_workspace, test_results_path):
self.manifest = bundle_manifest
self.work_dir = "mensor/"
self.endpoint = endpoint
Expand All @@ -20,16 +18,24 @@ def __init__(self, bundle_manifest, endpoint, security, current_workspace):
f" -a {self.manifest.build.architecture} -p {self.current_workspace}"
)

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}"
)

print(self.command)

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)
os.chdir(os.path.join(self.current_workspace, self.work_dir))
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
dir = os.getcwd()
subprocess.check_call("python3 -m pipenv install", cwd=dir, shell=True)
subprocess.check_call("pipenv install", cwd=dir, shell=True)

if self.security:
subprocess.check_call(f"{self.command} -s", cwd=dir, shell=True)
else:
subprocess.check_call(f"{self.command}", cwd=dir, shell=True)
if self.security:
subprocess.check_call(f"{self.command} -s", cwd=dir, shell=True)
else:
subprocess.check_call(f"{self.command}", cwd=dir, 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
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ def setUp(self):
self.manifest_filename = os.path.join(self.data_path, "bundle_manifest.yml")
self.manifest = BundleManifest.from_path(self.manifest_filename)
self.endpoint = None
self.perf_test_suite = PerfTestSuite(bundle_manifest=self.manifest, endpoint=None, security=False, current_workspace="current_workspace")
self.perf_test_suite = PerfTestSuite(bundle_manifest=self.manifest, endpoint=None, security=False,
current_workspace="current_workspace", test_results_path="test/results/")

def test_execute(self):
with patch("test_workflow.perf_test.perf_test_suite.os.chdir"):
Expand Down
47 changes: 47 additions & 0 deletions vars/runPerfTestScript.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
void call(Map args = [:]) {
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
String jobName = args.jobName ?: 'distribution-build-opensearch'
lib = library(identifier: 'jenkins@20211123', retriever: legacySCM(scm))
def buildManifest = lib.jenkins.BuildManifest.new(readYaml(file: args.bundleManifest))
String artifactRootUrl = buildManifest.getArtifactRootUrl(jobName, args.buildId)

install_npm()
install_dependencies()
install_opensearch_infra_dependencies()
withAWS(role: 'opensearch-test', roleAccount: "${AWS_ACCOUNT_PUBLIC}", duration: 900, roleSessionName: 'jenkins-session') {
kotwanikunal marked this conversation as resolved.
Show resolved Hide resolved
s3Download(file: "config.yml", bucket: "${ARTIFACT_BUCKET_NAME}", path: "${PERF_TEST_CONFIG_LOCATION}/config.yml", force: true)
}

sh([
'./test.sh',
'perf-test',
args.security ? "--stack test-single-security-${args.buildId}" :
"--stack test-single-${args.buildId}",
"--bundle-manifest ${args.bundleManifest}",
"--config config.yml",
args.security ? "--security" : ""
].join(' '))
}

void install_opensearch_infra_dependencies() {
abhinavGupta16 marked this conversation as resolved.
Show resolved Hide resolved
sh'''
pipenv install "dataclasses_json~=0.5" "aws_requests_auth~=0.4" "json2html~=1.3.0"
pipenv install "aws-cdk.core~=1.143.0" "aws_cdk.aws_ec2~=1.143.0" "aws_cdk.aws_iam~=1.143.0"
pipenv install "boto3~=1.18" "setuptools~=57.4" "retry~=0.9"
Copy link
Member

Choose a reason for hiding this comment

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

This shouldn't be necessary. Dependencies needed by the tools should go into Pipfile.

Copy link
Member Author

Choose a reason for hiding this comment

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

The is because of the nature in which the packages are separated and it being pulled at run time. pipenv does not support nested module pipfile installation, which is why I had to resort to installing it via this script.

Copy link
Member

Choose a reason for hiding this comment

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

You should fix this in the other project. Implement a similar wrapper as test.sh, a Pipfile and have the .sh script run pipenv install to get these dependencies. It's not on the caller's responsibility to ensure that the dependencies are met, or you'll be constantly chasing changes in that project here.

Copy link
Member Author

Choose a reason for hiding this comment

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

I can work on this in a follow up PR. I want to get the changes going as they are blocking other performance test related integrations and tests.

'''
}

void install_npm(){
sh'''
sudo yum install -y gcc-c++ make
curl -sL https://rpm.nodesource.com/setup_16.x | sudo -E bash -
sudo yum install -y nodejs --enablerepo=nodesource
node -v
'''
}

void install_dependencies() {
sh '''
sudo npm install -g aws-cdk
sudo npm install -g cdk-assume-role-credential-plugin
'''
}