Skip to content

Commit 0a20ec1

Browse files
authored
BWC test workflow (#1603)
* BWC test workflow Adding backwards compatability workflow for OpenSearch and OpenSearch Dashboards. Issue: #705 Signed-off-by: Kawika Avilla <[email protected]> * [BWC] Update docs and return cmd Signed-off-by: Kawika Avilla <[email protected]>
1 parent 37ae82f commit 0a20ec1

26 files changed

+3934
-91
lines changed

ONBOARDING.md

+2
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,5 @@ Add the new plugin to the [opensearch-plugins meta](https://github.com/opensearc
5151
2. If your plugin is dependent on `job-scheduler` zip, you can define that in `build-dependencies` in the config. Currently, the test workflow only supports `job-scheduler` as build dependency. Please create an issue if your plugin needs more support.
5252

5353
3. For backward compatibility testing, the `test-workflow` runs backward compatibility tests available in the plugin repository, (see [reference]((https://github.com/opensearch-project/anomaly-detection/blob/d9a122d05282f7efc1e24c61d64f18dec0fd47af/build.gradle#L428))). Like integration test, it has a set of configurable options defined in opensearch-1.3.0-test.yml, [example](manifests/1.3.0/opensearch-1.3.0-test.yml).
54+
55+
1. It supports two test configs - `with-security` and `without-security`, which runs test with security plugin enabled and disabled respectively. Choose one or both depending on what your plugin integration tests support.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
name: OpenSearch Dashboards
3+
components:
4+
- name: OpenSearch-Dashboards
5+
bwc-test:
6+
test-configs:
7+
- without-security
8+
- name: functionalTestDashboards
9+
integ-test:
10+
test-configs:
11+
- with-security
12+
- without-security
13+
schema-version: '1.0'

src/run_bwc_test.py

+13-7
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,28 @@
55
# this file be licensed under the Apache-2.0 license or a
66
# compatible open source license.
77

8-
import os
98
import sys
109

11-
from manifests.bundle_manifest import BundleManifest
10+
from manifests.test_manifest import TestManifest
1211
from system import console
13-
from system.temporary_directory import TemporaryDirectory
14-
from test_workflow.bwc_test.bwc_test_suite import BwcTestSuite
12+
from test_workflow.bwc_test.bwc_test_runners import BwcTestRunners
1513
from test_workflow.test_args import TestArgs
1614

1715

1816
def main():
1917
args = TestArgs()
18+
19+
# Any logging.info call preceding to next line in the execution chain will make the console output not displaying logs in console.
2020
console.configure(level=args.logging_level)
21-
with TemporaryDirectory(keep=args.keep) as work_dir:
22-
bundle_manifest = BundleManifest.from_urlpath(args.paths.get("opensearch", os.getcwd()))
23-
BwcTestSuite(bundle_manifest, work_dir.name, args.component, args.keep).execute()
21+
22+
test_manifest = TestManifest.from_path(args.test_manifest_path)
23+
24+
all_results = BwcTestRunners.from_test_manifest(args, test_manifest).run()
25+
26+
all_results.log()
27+
28+
if all_results.failed():
29+
sys.exit(1)
2430

2531

2632
if __name__ == "__main__":

src/test_workflow/README.md

+21
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,27 @@ Usage:
7676
./test.sh bwc-test <test-manifest-path> <target>
7777
```
7878

79+
For example, build locally and run BWC tests.
80+
81+
```bash
82+
./build.sh manifests/1.3.0/opensearch-1.3.0.yml
83+
./assemble.sh builds/opensearch/manifest.yml
84+
./test.sh bwc-test manifests/1.3.0/opensearch-1.3.0-test.yml . # looks for "./builds/opensearch/manifest.yml" and "./dist/opensearch/manifest.yml"
85+
```
86+
87+
Or run BWC tests against an existing build.
88+
89+
```bash
90+
./test.sh bwc-test manifests/1.3.0/opensearch-1.3.0-test.yml --paths opensearch=https://ci.opensearch.org/ci/dbc/bundle-build/1.2.0/869/linux/x64 # looks for https://.../builds/opensearch/manifest.yml and https://.../dist/opensearch/manifest.yml
91+
```
92+
93+
To run OpenSearch Dashboards BWC tests.
94+
95+
```bash
96+
./test.sh bwc-test manifests/1.3.0/opensearch-dashboards-1.3.0-test.yml --paths
97+
opensearch-dashboards=https://ci.opensearch.org/ci/dbc/bundle-build-dashboards/1.2.0/869/linux/x64
98+
```
99+
79100
### Performance Tests
80101

81102
TODO
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
#
3+
# The OpenSearch Contributors require contributions made to
4+
# this file be licensed under the Apache-2.0 license or a
5+
# compatible open source license.
6+
7+
import abc
8+
import logging
9+
import os
10+
11+
from system.temporary_directory import TemporaryDirectory
12+
from test_workflow.test_recorder.test_recorder import TestRecorder
13+
from test_workflow.test_result.test_suite_results import TestSuiteResults
14+
15+
16+
class BwcTestRunner(abc.ABC):
17+
def __init__(self, args, test_manifest):
18+
self.args = args
19+
self.test_manifest = test_manifest
20+
21+
self.tests_dir = os.path.join(os.getcwd(), "test-results")
22+
os.makedirs(self.tests_dir, exist_ok=True)
23+
self.test_recorder = TestRecorder(self.args.test_run_id, "bwc-test", self.tests_dir)
24+
25+
def run(self):
26+
with TemporaryDirectory(keep=self.args.keep, chdir=True) as work_dir:
27+
all_results = TestSuiteResults()
28+
for component in self.components.select(focus=self.args.component):
29+
if component.name in self.test_manifest.components:
30+
test_config = self.test_manifest.components[component.name]
31+
if test_config.bwc_test:
32+
test_suite = self.__create_test_suite__(component, test_config, work_dir)
33+
test_results = test_suite.execute_tests()
34+
all_results.append(component.name, test_results)
35+
else:
36+
logging.info(f"Skipping bwc-tests for {component.name}, as it is currently not supported")
37+
else:
38+
logging.info(f"Skipping bwc-tests for {component.name}, as it is currently not declared in the test manifest")
39+
40+
return all_results
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
#
3+
# The OpenSearch Contributors require contributions made to
4+
# this file be licensed under the Apache-2.0 license or a
5+
# compatible open source license.
6+
7+
import logging
8+
import os
9+
10+
from manifests.test_manifest import TestManifest
11+
from test_workflow.bwc_test.bwc_test_runner import BwcTestRunner
12+
from test_workflow.bwc_test.bwc_test_start_properties_opensearch import BwcTestStartPropertiesOpenSearch
13+
from test_workflow.bwc_test.bwc_test_suite_opensearch import BwcTestSuiteOpenSearch
14+
from test_workflow.test_args import TestArgs
15+
16+
17+
class BwcTestRunnerOpenSearch(BwcTestRunner):
18+
19+
def __init__(self, args: TestArgs, test_manifest: TestManifest):
20+
super().__init__(args, test_manifest)
21+
self.properties = BwcTestStartPropertiesOpenSearch(args.paths.get("opensearch", os.getcwd()))
22+
23+
self.components = self.properties.build_manifest.components
24+
25+
logging.info("Entering BWC test for OpenSearch")
26+
27+
def __create_test_suite__(self, component, test_config, work_dir):
28+
return BwcTestSuiteOpenSearch(
29+
work_dir.name,
30+
component,
31+
test_config,
32+
self.test_recorder,
33+
self.properties.bundle_manifest,
34+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
#
3+
# The OpenSearch Contributors require contributions made to
4+
# this file be licensed under the Apache-2.0 license or a
5+
# compatible open source license.
6+
7+
import logging
8+
import os
9+
10+
from manifests.test_manifest import TestManifest
11+
from test_workflow.bwc_test.bwc_test_runner import BwcTestRunner
12+
from test_workflow.bwc_test.bwc_test_start_properties_opensearch_dashboards import BwcTestStartPropertiesOpenSearchDashboards
13+
from test_workflow.bwc_test.bwc_test_suite_opensearch_dashboards import BwcTestSuiteOpenSearchDashboards
14+
from test_workflow.test_args import TestArgs
15+
16+
17+
class BwcTestRunnerOpenSearchDashboards(BwcTestRunner):
18+
19+
def __init__(self, args: TestArgs, test_manifest: TestManifest):
20+
super().__init__(args, test_manifest)
21+
self.properties = BwcTestStartPropertiesOpenSearchDashboards(args.paths.get("opensearch-dashboards", os.getcwd()))
22+
23+
self.components = self.properties.build_manifest.components
24+
logging.info("Entering BWC test for OpenSearch Dashboards")
25+
26+
def __create_test_suite__(self, component, test_config, work_dir):
27+
return BwcTestSuiteOpenSearchDashboards(
28+
work_dir.name,
29+
component,
30+
test_config,
31+
self.test_recorder,
32+
self.properties.bundle_manifest,
33+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
#
3+
# The OpenSearch Contributors require contributions made to
4+
# this file be licensed under the Apache-2.0 license or a
5+
# compatible open source license.
6+
7+
8+
from manifests.test_manifest import TestManifest
9+
from test_workflow.bwc_test.bwc_test_runner_opensearch import BwcTestRunnerOpenSearch
10+
from test_workflow.bwc_test.bwc_test_runner_opensearch_dashboards import BwcTestRunnerOpenSearchDashboards
11+
from test_workflow.test_args import TestArgs
12+
13+
14+
class BwcTestRunners:
15+
RUNNERS = {
16+
"OpenSearch": BwcTestRunnerOpenSearch,
17+
"OpenSearch Dashboards": BwcTestRunnerOpenSearchDashboards
18+
}
19+
20+
@classmethod
21+
def from_test_manifest(cls, args: TestArgs, test_manifest: TestManifest):
22+
return cls.RUNNERS[test_manifest.name](args, test_manifest)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
#
3+
# The OpenSearch Contributors require contributions made to
4+
# this file be licensed under the Apache-2.0 license or a
5+
# compatible open source license.
6+
7+
8+
import abc
9+
10+
from manifests.build_manifest import BuildManifest
11+
from manifests.bundle_manifest import BundleManifest
12+
13+
14+
class BwcTestStartProperties(abc.ABC):
15+
def __init__(self, path, build_dir, bundle_dir):
16+
self.path = path
17+
self.build_dir = build_dir
18+
self.bundle_dir = bundle_dir
19+
20+
self.bundle_manifest = BundleManifest.from_urlpath("/".join([self.path.rstrip("/"), self.bundle_dir]))
21+
self.build_manifest = BuildManifest.from_urlpath("/".join([self.path.rstrip("/"), self.build_dir]))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
#
3+
# The OpenSearch Contributors require contributions made to
4+
# this file be licensed under the Apache-2.0 license or a
5+
# compatible open source license.
6+
7+
from test_workflow.bwc_test.bwc_test_start_properties import BwcTestStartProperties
8+
9+
10+
class BwcTestStartPropertiesOpenSearch(BwcTestStartProperties):
11+
def __init__(self, path):
12+
super().__init__(path, "builds/opensearch/manifest.yml", "dist/opensearch/manifest.yml")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
#
3+
# The OpenSearch Contributors require contributions made to
4+
# this file be licensed under the Apache-2.0 license or a
5+
# compatible open source license.
6+
7+
from test_workflow.bwc_test.bwc_test_start_properties import BwcTestStartProperties
8+
9+
10+
class BwcTestStartPropertiesOpenSearchDashboards(BwcTestStartProperties):
11+
def __init__(self, path):
12+
super().__init__(path, "builds/opensearch-dashboards/manifest.yml", "dist/opensearch-dashboards/manifest.yml")

src/test_workflow/bwc_test/bwc_test_suite.py

+84-30
Original file line numberDiff line numberDiff line change
@@ -7,43 +7,97 @@
77
# Modifications Copyright OpenSearch Contributors. See
88
# GitHub history for details.
99

10+
import abc
1011
import logging
1112
import os
1213

14+
from git.git_repository import GitRepository
1315
from paths.script_finder import ScriptFinder
1416
from system.execute import execute
15-
from test_workflow.test_component import TestComponent
17+
from test_workflow.test_recorder.test_result_data import TestResultData
18+
from test_workflow.test_result.test_component_results import TestComponentResults
19+
from test_workflow.test_result.test_result import TestResult
1620

1721

18-
class BwcTestSuite:
19-
manifest: str
20-
work_dir: str
21-
component: str
22-
keep: bool
22+
class BwcTestSuite(abc.ABC):
2323

24-
def __init__(self, manifest, work_dir, component=None, keep=False):
25-
self.manifest = manifest
24+
def __init__(
25+
self,
26+
work_dir,
27+
component,
28+
test_config,
29+
test_recorder,
30+
manifest
31+
):
2632
self.work_dir = work_dir
2733
self.component = component
28-
self.keep = keep
29-
30-
def run_tests(self, work_dir, component_name):
31-
script = ScriptFinder.find_bwc_test_script(component_name, work_dir)
32-
(status, stdout, stderr) = execute(script, work_dir, True, False)
33-
return (status, stdout, stderr)
34-
35-
def component_bwc_tests(self, component):
36-
test_component = TestComponent(component.repository, component.commit_id)
37-
test_component.checkout(os.path.join(self.work_dir, component.name))
38-
try:
39-
console_output = self.run_tests(os.path.join(self.work_dir, component.name), component.name)
40-
return console_output
41-
except:
42-
# TODO: Store and report test failures for {component}
43-
logging.info(f"Exception while running BWC tests for {component.name}")
44-
45-
def execute(self):
46-
# For each component, check out the git repo and run `bwctest.sh`
47-
for component in self.manifest.components.select(focus=self.component):
48-
# TODO: Store and report test results, send notification via {console_output}
49-
self.component_bwc_tests(component)
34+
self.test_config = test_config
35+
self.test_recorder = test_recorder
36+
self.manifest = manifest
37+
38+
self.repo = GitRepository(
39+
self.component.repository,
40+
self.component.commit_id,
41+
os.path.join(self.work_dir, self.component.name),
42+
test_config.working_directory
43+
)
44+
45+
self.save_logs = test_recorder.test_results_logs
46+
47+
def execute_tests(self):
48+
test_results = TestComponentResults()
49+
50+
for config in self.test_config.bwc_test["test-configs"]:
51+
status = self.execute_bwctest_sh(config)
52+
53+
test_results.append(TestResult(self.component.name, config, status))
54+
return test_results
55+
56+
def execute_bwctest_sh(self, config):
57+
security = self.is_security_enabled(config)
58+
script = ScriptFinder.find_bwc_test_script(self.component.name, self.repo.working_directory)
59+
if os.path.exists(script):
60+
cmd = self.get_cmd(script, security, self.manifest.build.location)
61+
self.repo_work_dir = os.path.join(
62+
self.repo.dir, self.test_config.working_directory) if self.test_config.working_directory is not None else self.repo.dir
63+
(status, stdout, stderr) = execute(cmd, self.repo_work_dir, True, False)
64+
65+
test_result_data = TestResultData(
66+
self.component.name,
67+
self.test_config,
68+
status,
69+
stdout,
70+
stderr,
71+
self.test_artifact_files
72+
)
73+
self.save_logs.save_test_result_data(test_result_data)
74+
if stderr:
75+
logging.info("BWC test run failed for component " + self.component.name)
76+
logging.info(stderr)
77+
return status
78+
else:
79+
logging.info(f"{script} does not exist. Skipping integ tests for {self.component.name}")
80+
81+
def is_security_enabled(self, config):
82+
if config in ["with-security", "without-security"]:
83+
return True if config == "with-security" else False
84+
else:
85+
raise InvalidTestConfigError("Unsupported test config: " + config)
86+
87+
def pretty_print_message(self, message):
88+
logging.info("===============================================")
89+
logging.info(message)
90+
logging.info("===============================================")
91+
92+
@abc.abstractmethod
93+
def get_cmd(self):
94+
pass
95+
96+
@property
97+
@abc.abstractmethod
98+
def test_artifact_files(self):
99+
pass
100+
101+
102+
class InvalidTestConfigError(Exception):
103+
pass

0 commit comments

Comments
 (0)