Skip to content

Commit e6c5ce6

Browse files
authored
Add Validation Framework (opensearch-project#2725)
* DownloadUrl Validation Signed-off-by: Divya Madala <divyaasm@amazon.com> DownloadUrl Verification Signed-off-by: Divya Madala <divyaasm@amazon.com> Validate DownloadUrl's Signed-off-by: Divya Madala <divyaasm@amazon.com> Validate DownloadUrl's Signed-off-by: Divya Madala <divyaasm@amazon.com> Validate DownloadUrl's Signed-off-by: Divya Madala <divyaasm@amazon.com> Validate DownloadUrl's Signed-off-by: Divya Madala <divyaasm@amazon.com> Validate DownloadUrl's Signed-off-by: Divya Madala <divyaasm@amazon.com> Validate DownloadUrl's Signed-off-by: Divya Madala <divyaasm@amazon.com> Validate DownloadUrl's Signed-off-by: Divya Madala <divyaasm@amazon.com> Validate DownloadUrl's Signed-off-by: Divya Madala <divyaasm@amazon.com> Validate DownloadUrl's Signed-off-by: Divya Madala <divyaasm@amazon.com> Validate DownloadUrl's Signed-off-by: Divya Madala <divyaasm@amazon.com> Verify Download Url's Signed-off-by: Divya Madala <divyaasm@amazon.com> Verify Download Url's Signed-off-by: Divya Madala <divyaasm@amazon.com> Test Signed-off-by: Divya Madala <divyaasm@amazon.com> Test Signed-off-by: Divya Madala <divyaasm@amazon.com> Test Signed-off-by: Divya Madala <divyaasm@amazon.com> Test Signed-off-by: Divya Madala <divyaasm@amazon.com> Test Signed-off-by: Divya Madala <divyaasm@amazon.com> Test Signed-off-by: Divya Madala <divyaasm@amazon.com> Test Signed-off-by: Divya Madala <divyaasm@amazon.com> Test Signed-off-by: Divya Madala <divyaasm@amazon.com> Test Signed-off-by: Divya Madala <divyaasm@amazon.com> Test Signed-off-by: Divya Madala <divyaasm@amazon.com> Test Signed-off-by: Divya Madala <divyaasm@amazon.com> * Verify Download Url's Signed-off-by: Divya Madala <divyaasm@amazon.com> Add Validation WorkFlow Signed-off-by: Divya Madala <divyaasm@amazon.com> Add Validation Framework Signed-off-by: Divya Madala <divyaasm@amazon.com> Changes to Validation Workflow Signed-off-by: Divya Madala <divyaasm@amazon.com> Changes to Validation Workflow Signed-off-by: Divya Madala <divyaasm@amazon.com> * Changes updated Signed-off-by: Divya Madala <divyaasm@amazon.com> * Changes updtaed to Validation workflow Signed-off-by: Divya Madala <divyaasm@amazon.com> * Changes Updated Signed-off-by: Divya Madala <divyaasm@amazon.com> * Validation workflow Signed-off-by: Divya Madala <divyaasm@amazon.com> Signed-off-by: Divya Madala <divyaasm@amazon.com>
1 parent 66b52a2 commit e6c5ce6

16 files changed

+455
-0
lines changed

src/run_validation.py

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright OpenSearch Contributors
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# The OpenSearch Contributors require contributions made to
5+
# this file be licensed under the Apache-2.0 license or a
6+
# compatible open source license.
7+
8+
import logging
9+
import sys
10+
11+
from system import console
12+
from validation_workflow.validation_args import ValidationArgs
13+
14+
15+
def main() -> int:
16+
17+
args = ValidationArgs()
18+
19+
console.configure(level=args.logging_level)
20+
logging.getLogger("urllib3").setLevel(logging.WARNING)
21+
22+
dist = args.DISTRIBUTION_MAP.get(args.distribution, None)
23+
dist.download_artifacts(projects=args.projects, version=args.version)
24+
25+
return 0
26+
27+
28+
if __name__ == "__main__":
29+
sys.exit(main())

src/validation_workflow/README.md

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#### Automate the Validation during Releases.
2+
The input requires a mandatory version number , optional --distribution types(tar,rpm,yum) and --platform type (currently uses only linux) to automatically download and verify the artifacts.
3+
4+
*Usage*
5+
```
6+
./validation.sh --version 2.3.0 --distribution rpm --platform linux
7+
```
8+
The following options are available.
9+
10+
| name | description |
11+
|------------------------|---------------------------------------------------------------------|
12+
| version | Accepts a mandatory version number. |
13+
| -d, --distribution | Assigns the distribution type specifed by the user |
14+
| -p, --platform | Determines the platform type of the atrifacts. |
15+
| --verbose | Show more verbose output. |
16+

src/validation_workflow/__init__.py

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright OpenSearch Contributors
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# The OpenSearch Contributors require contributions made to
5+
# this file be licensed under the Apache-2.0 license or a
6+
# compatible open source license.
7+
#
8+
# This page intentionally left blank.
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright OpenSearch Contributors
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# The OpenSearch Contributors require contributions made to
5+
# this file be licensed under the Apache-2.0 license or a
6+
# compatible open source license.
7+
8+
9+
import os
10+
11+
import requests
12+
13+
from system.temporary_directory import TemporaryDirectory
14+
15+
16+
class DownloadUtils:
17+
@staticmethod
18+
def is_url_valid(url: str) -> bool:
19+
response = requests.head(url)
20+
status = bool(response.status_code == 200)
21+
return status
22+
23+
@staticmethod
24+
def download(url: str, tmp_dir: TemporaryDirectory) -> bool:
25+
# This method writes the contents from the response object into temporary directory file name fetched from the end of the url.
26+
response = requests.get(url, stream=True)
27+
path = os.path.join(tmp_dir.name, os.path.basename(url))
28+
status = bool(open(path, "wb").write(response.content))
29+
return status

src/validation_workflow/validation.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Copyright OpenSearch Contributors
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# The OpenSearch Contributors require contributions made to
5+
# this file be licensed under the Apache-2.0 license or a
6+
# compatible open source license.
7+
8+
9+
from abc import ABC, abstractmethod
10+
11+
from system.temporary_directory import TemporaryDirectory
12+
13+
14+
class Validation(ABC):
15+
base_url = "https://artifacts.opensearch.org/releases/bundle/"
16+
tmp_dir = TemporaryDirectory()
17+
18+
@classmethod
19+
@abstractmethod
20+
def download_artifacts(self, projects: list, version: str) -> bool:
21+
pass
+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Copyright OpenSearch Contributors
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# The OpenSearch Contributors require contributions made to
5+
# this file be licensed under the Apache-2.0 license or a
6+
# compatible open source license.
7+
8+
import argparse
9+
import logging
10+
11+
from validation_workflow.validation_rpm import ValidationRpm
12+
from validation_workflow.validation_tar import ValidationTar
13+
from validation_workflow.validation_yum import ValidationYum
14+
15+
16+
class ValidationArgs:
17+
SUPPORTED_PLATFORMS = ["linux"]
18+
DISTRIBUTION_MAP = {
19+
"tar": ValidationTar,
20+
"yum": ValidationYum,
21+
"rpm": ValidationRpm
22+
}
23+
24+
def __init__(self) -> None:
25+
parser = argparse.ArgumentParser(description="Validation Framework for Validation Workflow.")
26+
parser.add_argument(
27+
"--version",
28+
type=str,
29+
required=True,
30+
help="Product version to validate"
31+
)
32+
parser.add_argument(
33+
"-d",
34+
"--distribution",
35+
type=str,
36+
choices=self.DISTRIBUTION_MAP.keys(),
37+
help="Distribution to validate.",
38+
default="tar",
39+
dest="distribution"
40+
)
41+
parser.add_argument(
42+
"-p",
43+
"--platform",
44+
type=str,
45+
choices=self.SUPPORTED_PLATFORMS,
46+
help="Platform to validate.",
47+
default="linux"
48+
)
49+
parser.add_argument(
50+
"--stgosbuild",
51+
type=str,
52+
required=False,
53+
help="The opensearchstaging OpenSearch image build number if required, for example : 6039\n",
54+
default="",
55+
dest="stgosbuild",
56+
)
57+
parser.add_argument(
58+
"--stgosdbuild",
59+
type=str,
60+
required=False,
61+
help="The opensearchstaging OpenSearchDashboard image build number if required, for example : 4104\n",
62+
default="",
63+
dest="stgosdbuild",
64+
)
65+
parser.add_argument(
66+
"-v",
67+
"--verbose",
68+
help="Show more verbose output.",
69+
action="store_const",
70+
default=logging.INFO,
71+
const=logging.DEBUG,
72+
dest="logging_level",
73+
)
74+
75+
args = parser.parse_args()
76+
self.version = args.version
77+
self.logging_level = args.logging_level
78+
self.distribution = args.distribution
79+
self.platform = args.platform
80+
self.projects = ["opensearch", "opensearch-dashboards"]
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Copyright OpenSearch Contributors
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# The OpenSearch Contributors require contributions made to
5+
# this file be licensed under the Apache-2.0 license or a
6+
# compatible open source license.
7+
8+
import logging
9+
10+
from validation_workflow.download_utils import DownloadUtils
11+
from validation_workflow.validation import Validation
12+
13+
14+
class ValidationRpm(Validation, DownloadUtils):
15+
16+
@classmethod
17+
def download_artifacts(self, projects: list, version: str) -> bool:
18+
architectures = ["x64", "arm64"]
19+
for project in projects:
20+
for architecture in architectures:
21+
url = f"{self.base_url}{project}/{version}/{project}-{version}-linux-{architecture}.rpm"
22+
if ValidationRpm.is_url_valid(url) and ValidationRpm.download(url, self.tmp_dir):
23+
logging.info(f"Valid URL - {url} and Download Successful !")
24+
else:
25+
logging.info(f"Invalid URL - {url}")
26+
raise Exception(f"Invalid url - {url}")
27+
return True
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Copyright OpenSearch Contributors
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# The OpenSearch Contributors require contributions made to
5+
# this file be licensed under the Apache-2.0 license or a
6+
# compatible open source license.
7+
8+
import logging
9+
10+
from validation_workflow.download_utils import DownloadUtils
11+
from validation_workflow.validation import Validation
12+
13+
14+
class ValidationTar(Validation, DownloadUtils):
15+
16+
@classmethod
17+
def download_artifacts(self, projects: list, version: str) -> bool:
18+
architectures = ["x64", "arm64"]
19+
for project in projects:
20+
for architecture in architectures:
21+
url = f"{self.base_url}{project}/{version}/{project}-{version}-linux-{architecture}.tar.gz"
22+
if ValidationTar.is_url_valid(url) and ValidationTar.download(url, self.tmp_dir):
23+
logging.info(f"Valid URL - {url} and Download Successful !")
24+
else:
25+
logging.info(f"Invalid URL - {url}")
26+
raise Exception(f"Invalid url - {url}")
27+
return True
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Copyright OpenSearch Contributors
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# The OpenSearch Contributors require contributions made to
5+
# this file be licensed under the Apache-2.0 license or a
6+
# compatible open source license.
7+
8+
import logging
9+
10+
from validation_workflow.download_utils import DownloadUtils
11+
from validation_workflow.validation import Validation
12+
13+
14+
class ValidationYum(Validation, DownloadUtils):
15+
16+
@classmethod
17+
def download_artifacts(self, projects: list, version: str) -> bool:
18+
for project in projects:
19+
url = f"{self.base_url}{project}/{version[0:1]}.x/{project}-{version[0:1]}.x.repo"
20+
if ValidationYum.is_url_valid(url) and ValidationYum.download(url, self.tmp_dir):
21+
logging.info(f"Valid URL - {url} and Download Successful !")
22+
else:
23+
logging.info(f"Invalid URL - {url}")
24+
raise Exception(f"Invalid url - {url}")
25+
return True

tests/test_run_validation.py

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Copyright OpenSearch Contributors
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# The OpenSearch Contributors require contributions made to
5+
# this file be licensed under the Apache-2.0 license or a
6+
# compatible open source license.
7+
8+
9+
import unittest
10+
from typing import Any
11+
from unittest.mock import Mock, patch
12+
13+
import pytest
14+
from pytest import CaptureFixture
15+
16+
from src.run_validation import main
17+
from src.validation_workflow.validation_args import ValidationArgs
18+
19+
20+
class TestRunValidation(unittest.TestCase):
21+
22+
@pytest.fixture(autouse=True)
23+
def getCapfd(self, capfd: CaptureFixture) -> None:
24+
self.capfd = capfd
25+
26+
@patch("argparse._sys.argv", ["run_validation.py", "--help"])
27+
def test_usage(self, *mocks: Any) -> None:
28+
with self.assertRaises(SystemExit):
29+
main()
30+
31+
out, _ = self.capfd.readouterr()
32+
self.assertTrue(out.startswith("usage:"))
33+
34+
@patch("argparse._sys.argv", ["run_validation.py", "--version", "1.3.6"])
35+
@patch("src.validation_workflow.validation_tar.ValidationTar.download_artifacts", return_value=True)
36+
@patch("run_validation.main", return_value=0)
37+
def test_main_default(self, mock_tar: Mock, *mocks: Any) -> None:
38+
self.assertEqual(ValidationArgs().version, "1.3.6")
39+
self.assertEqual(ValidationArgs().distribution, "tar")
40+
self.assertNotEqual(ValidationArgs().distribution, "rpm")
41+
42+
@patch("argparse._sys.argv", ["run_validation.py", "--version", "2.1.0", "--distribution", "rpm"])
43+
@patch("src.validation_workflow.validation_rpm.ValidationRpm.download_artifacts", return_value=True)
44+
@patch("run_validation.main", return_value=0)
45+
def test_main_rpm(self, mock_tar: Mock, *mocks: Any) -> None:
46+
self.assertEqual(ValidationArgs().version, "2.1.0")
47+
self.assertEqual(ValidationArgs().distribution, "rpm")
48+
self.assertNotEqual(ValidationArgs().distribution, "tar")
49+
50+
@patch("argparse._sys.argv", ["run_validation.py", "--version", "2.1.0", "--distribution", "yum"])
51+
@patch("src.validation_workflow.validation_yum.ValidationYum.download_artifacts", return_value=True)
52+
@patch("run_validation.main", return_value=0)
53+
def test_main_yum(self, mock_tar: Mock, *mocks: Any) -> None:
54+
self.assertEqual(ValidationArgs().version, "2.1.0")
55+
self.assertEqual(ValidationArgs().distribution, "yum")
56+
self.assertNotEqual(ValidationArgs().distribution, "tar")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright OpenSearch Contributors
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# The OpenSearch Contributors require contributions made to
5+
# this file be licensed under the Apache-2.0 license or a
6+
# compatible open source license.
7+
8+
import os
9+
import sys
10+
11+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../src"))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Copyright OpenSearch Contributors
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# The OpenSearch Contributors require contributions made to
5+
# this file be licensed under the Apache-2.0 license or a
6+
# compatible open source license.
7+
8+
import logging
9+
import unittest
10+
from unittest.mock import patch
11+
12+
from validation_workflow.validation_args import ValidationArgs
13+
14+
15+
class TestValidationArgs(unittest.TestCase):
16+
17+
VALIDATION_PY = "./src/run_validation.py" \
18+
19+
20+
@patch("argparse._sys.argv", [VALIDATION_PY, "--version", "2.3.0"])
21+
def test_version(self) -> None:
22+
self.assertTrue(ValidationArgs().version)
23+
self.assertEqual(ValidationArgs().version, "2.3.0")
24+
self.assertNotEqual(ValidationArgs().version, "2.1.0")
25+
26+
@patch("argparse._sys.argv", [VALIDATION_PY, "--version", "2.1.0", "--distribution", "rpm"])
27+
def test_distribution(self) -> None:
28+
self.assertEqual(ValidationArgs().distribution, "rpm")
29+
30+
@patch("argparse._sys.argv", [VALIDATION_PY, "--version", "1.3.6", "--platform", "linux"])
31+
def test_platform_default(self) -> None:
32+
self.assertEqual(ValidationArgs().platform, "linux")
33+
34+
@patch("argparse._sys.argv", [VALIDATION_PY, "--version", "1.3.0"])
35+
def test_verbose_default(self) -> None:
36+
self.assertEqual(ValidationArgs().logging_level, logging.INFO)
37+
38+
@patch("argparse._sys.argv", [VALIDATION_PY, "--version", "1.3.0", "--verbose"])
39+
def test_verbose_true(self) -> None:
40+
self.assertTrue(ValidationArgs().logging_level, logging.DEBUG)

0 commit comments

Comments
 (0)