Skip to content

Commit

Permalink
Support new distribution-validations and add few enhancements to vali…
Browse files Browse the repository at this point in the history
…dation workflow (#4447)

Signed-off-by: Divya Madala <divyaasm@amazon.com>
  • Loading branch information
Divyaasm authored Feb 20, 2024
1 parent 04c7e7f commit f1f52ad
Show file tree
Hide file tree
Showing 21 changed files with 850 additions and 239 deletions.
8 changes: 8 additions & 0 deletions src/validation_workflow/deb/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright OpenSearch Contributors
# SPDX-License-Identifier: Apache-2.0
#
# The OpenSearch Contributors require contributions made to
# this file be licensed under the Apache-2.0 license or a
# compatible open source license.
#
# This page intentionally left blank.
61 changes: 61 additions & 0 deletions src/validation_workflow/deb/validation_deb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Copyright OpenSearch Contributors
# SPDX-License-Identifier: Apache-2.0
#
# The OpenSearch Contributors require contributions made to
# this file be licensed under the Apache-2.0 license or a
# compatible open source license.

import logging
import os

from system.execute import execute
from test_workflow.integ_test.utils import get_password
from validation_workflow.api_test_cases import ApiTestCases
from validation_workflow.download_utils import DownloadUtils
from validation_workflow.validation import Validation
from validation_workflow.validation_args import ValidationArgs


class ValidateDeb(Validation, DownloadUtils):
def __init__(self, args: ValidationArgs) -> None:
super().__init__(args)

def installation(self) -> bool:
try:
for project in self.args.projects:
set_password = f' env OPENSEARCH_INITIAL_ADMIN_PASSWORD={get_password(str(self.args.version))}' if project == "opensearch" else ""
execute(f'sudo dpkg --purge {project}', ".")
execute(f'sudo {set_password} dpkg -i {os.path.basename(self.args.file_path.get(project))}', str(self.tmp_dir.path))
except:
raise Exception("Failed to install OpenSearch/OpenSearch-Dashboards")
return True

def start_cluster(self) -> bool:
try:
for project in self.args.projects:
execute(f'sudo systemctl enable {project}', ".")
execute(f'sudo systemctl start {project}', ".")
execute(f'sudo systemctl status {project}', ".")
except:
raise Exception('Failed to Start Cluster')
return True

def validation(self) -> bool:
if self.check_cluster_readiness():
test_result, counter = ApiTestCases().test_apis(self.args.version, self.args.projects,
self.check_for_security_plugin(os.path.join(os.sep, "usr", "share", "opensearch")) if self.args.allow_http else True)
if (test_result):
logging.info(f'All tests Pass : {counter}')
return True
else:
raise Exception(f'Not all tests Pass : {counter}')
else:
raise Exception("Cluster is not ready for API test")

def cleanup(self) -> bool:
try:
for project in self.args.projects:
execute(f'sudo dpkg --purge {project}', ".")
except Exception as e:
raise Exception(f'Exception occurred either while attempting to stop cluster or removing OpenSearch/OpenSearch-Dashboards. {str(e)}')
return True
2 changes: 1 addition & 1 deletion src/validation_workflow/docker/inspect_docker_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def __init__(self, image_id: str, image_name: str, prod_image_tag: str) -> None:
self.image_id = image_id
self.image_name = image_name
self.prod_image_tag = prod_image_tag
self.image_tag = ValidationArgs().stg_tag('opensearch_dashboards').replace(" ", "") if ("dashboards" in self.image_name) else ValidationArgs().stg_tag('opensearch').replace(" ", "")
self.image_tag = ValidationArgs().stg_tag('opensearch-dashboards').replace(" ", "") if ("dashboards" in self.image_name) else ValidationArgs().stg_tag('opensearch').replace(" ", "")
self.auth_token_url = "https://auth.docker.io/token?"
self.auth_service_scope = "service=registry.docker.io&scope=repository:"
self.registry_url = "https://index.docker.io/v2/"
Expand Down
51 changes: 8 additions & 43 deletions src/validation_workflow/docker/validation_docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,10 @@
import os
import shutil
import subprocess
import time
from subprocess import PIPE
from typing import Any

import requests

from system.temporary_directory import TemporaryDirectory
from validation_workflow.api_request import ApiTest
from test_workflow.integ_test.utils import get_password
from validation_workflow.api_test_cases import ApiTestCases
from validation_workflow.docker.inspect_docker_image import InspectDockerImage
from validation_workflow.validation import Validation
Expand Down Expand Up @@ -61,8 +57,7 @@ def start_cluster(self) -> bool:
def validation(self) -> bool:
# STEP 2 . inspect image digest between opensearchproject(downloaded/local) and opensearchstaging(dockerHub)
if not self.args.using_staging_artifact_only:
self.image_names_list = [self.args.OS_image, self.args.OSD_image]
self.image_names_list = [x for x in self.image_names_list if (os.path.basename(x) in self.args.projects)]
self.image_names_list = ['opensearchproject/' + project for project in self.args.projects]
self.image_digests = list(map(lambda x: self.inspect_docker_image(x[0], x[1]), zip(self.image_ids.values(), self.image_names_list))) # type: ignore
if all(self.image_digests):
logging.info('Image digest is validated.\n\n')
Expand All @@ -79,7 +74,7 @@ def validation(self) -> bool:
self.args.version
)
if return_code:
logging.info('Checking if cluster is ready for API test in every 10 seconds\n\n')
logging.info('Checking if cluster is ready for API test in every 5 seconds\n\n')

if self.check_cluster_readiness():
# STEP 4 . OS, OSD API validation
Expand All @@ -94,8 +89,8 @@ def validation(self) -> bool:
raise Exception(f'Not all tests Pass : {_counter}')
else:
raise Exception("Cluster is not ready for API test.")
else:
raise Exception('The container failed to start. Exiting the validation.')
else:
raise Exception('The container failed to start. Exiting the validation.')

return True

Expand Down Expand Up @@ -129,36 +124,6 @@ def cleanup_process(self) -> bool:

return('returncode=0' in (str(result)))

def check_http_request(self) -> bool:
self.test_readiness_urls = {
'https://localhost:9200/': 'opensearch cluster API',
'http://localhost:5601/api/status': 'opensearch-dashboards API',
}

for url, name in self.test_readiness_urls.items():
try:
status_code, response_text = ApiTest(url, self.args.version).api_get()
if status_code != 200:
logging.error(f'Error connecting to {name} ({url}): status code {status_code}')
return False
except (requests.exceptions.ConnectionError, requests.exceptions.ConnectTimeout) as e:
logging.error(f'Error connecting to {name} ({url}): {e}')
return False
return True

def check_cluster_readiness(self) -> bool:
max_retry = 20
retry_count = 0
while retry_count < max_retry:
logging.info(f'sleeping 10sec for retry {retry_count + 1}/{max_retry}')
time.sleep(10)
if self.check_http_request():
logging.info('\n\ncluster is now ready for API test\n\n')
return True
retry_count += 1
logging.error(f"Maximum number of retries ({max_retry}) reached. Cluster is not ready for API test.")
return False

def get_artifact_image_name(self, artifact: str, using_staging_artifact_only: str) -> Any:
self.image_names = {
'dockerhub': {
Expand Down Expand Up @@ -241,7 +206,6 @@ def run_container(self, image_ids: dict, version: str) -> Any:
'2': 'docker-compose-2.x.yml'
}

self.tmp_dir = TemporaryDirectory()
self.target_yml_file = os.path.join(self.tmp_dir.name, 'docker-compose.yml')

self.major_version_number = version[0]
Expand All @@ -252,8 +216,9 @@ def run_container(self, image_ids: dict, version: str) -> Any:
self.replacements = [(f'opensearchproject/{key}:{self.major_version_number}', value) for key, value in image_ids.items()]

list(map(lambda r: self.inplace_change(self.target_yml_file, r[0], r[1]), self.replacements))

os.environ["OPENSEARCH_INITIAL_ADMIN_PASSWORD"] = get_password(str(version))
# spin up containers
self.docker_compose_up = f'docker-compose -f {self.target_yml_file} up -d'
services = "opensearch-node1 opensearch-node2" if "opensearch-dashboards" not in self.args.projects else ""
self.docker_compose_up = f'docker-compose -f {self.target_yml_file} up -d {services}'
result = subprocess.run(self.docker_compose_up, shell=True, stdout=PIPE, stderr=PIPE, universal_newlines=True)
return ('returncode=0' in (str(result)), self.target_yml_file)
37 changes: 9 additions & 28 deletions src/validation_workflow/rpm/validation_rpm.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@

import logging
import os
import time

from system.execute import execute
from system.temporary_directory import TemporaryDirectory
from test_workflow.integ_test.utils import get_password
from validation_workflow.api_test_cases import ApiTestCases
from validation_workflow.download_utils import DownloadUtils
Expand All @@ -22,26 +20,6 @@ class ValidateRpm(Validation, DownloadUtils):

def __init__(self, args: ValidationArgs) -> None:
super().__init__(args)
self.base_url_production = "https://artifacts.opensearch.org/releases/bundle/"
self.base_url_staging = "https://ci.opensearch.org/ci/dbc/distribution-build-"
self.tmp_dir = TemporaryDirectory()

def download_artifacts(self) -> bool:
isFilePathEmpty = bool(self.args.file_path)
for project in self.args.projects:
if (isFilePathEmpty):
if ("https:" not in self.args.file_path.get(project)):
self.copy_artifact(self.args.file_path.get(project), str(self.tmp_dir.path))
else:
self.args.version = self.get_version(self.args.file_path.get(project))
self.check_url(self.args.file_path.get(project))
else:
if (self.args.artifact_type == "staging"):
self.args.file_path[project] = f"{self.base_url_staging}{project}/{self.args.version}/{self.args.build_number[project]}/linux/{self.args.arch}/{self.args.distribution}/dist/{project}/{project}-{self.args.version}-linux-{self.args.arch}.rpm" # noqa: E501
else:
self.args.file_path[project] = f"{self.base_url_production}{project}/{self.args.version}/{project}-{self.args.version}-linux-{self.args.arch}.rpm"
self.check_url(self.args.file_path.get(project))
return True

def installation(self) -> bool:
try:
Expand All @@ -58,7 +36,6 @@ def start_cluster(self) -> bool:
try:
for project in self.args.projects:
execute(f'sudo systemctl start {project}', ".")
time.sleep(20)
(stdout, stderr, status) = execute(f'sudo systemctl status {project}', ".")
if(status == 0):
logging.info(stdout)
Expand All @@ -70,12 +47,16 @@ def start_cluster(self) -> bool:
return True

def validation(self) -> bool:
test_result, counter = ApiTestCases().test_apis(self.args.version, self.args.projects, self.check_for_security_plugin(os.path.join(os.sep, "usr", "share", "opensearch")) if not self.args.force_https else True) # noqa: E501
if (test_result):
logging.info(f'All tests Pass : {counter}')
return True
if self.check_cluster_readiness():
test_result, counter = ApiTestCases().test_apis(self.args.version, self.args.projects,
self.check_for_security_plugin(os.path.join(os.sep, "usr", "share", "opensearch")) if self.args.allow_http else True)
if (test_result):
logging.info(f'All tests Pass : {counter}')
return True
else:
raise Exception(f'Not all tests Pass : {counter}')
else:
raise Exception(f'Not all tests Pass : {counter}')
raise Exception("Cluster is not ready for API test")

def cleanup(self) -> bool:
try:
Expand Down
38 changes: 9 additions & 29 deletions src/validation_workflow/tar/validation_tar.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@

import logging
import os
import time

from system.execute import execute
from system.process import Process
from system.temporary_directory import TemporaryDirectory
from test_workflow.integ_test.utils import get_password
from validation_workflow.api_test_cases import ApiTestCases
from validation_workflow.download_utils import DownloadUtils
Expand All @@ -23,29 +21,9 @@ class ValidateTar(Validation, DownloadUtils):

def __init__(self, args: ValidationArgs) -> None:
super().__init__(args)
self.base_url_production = "https://artifacts.opensearch.org/releases/bundle/"
self.base_url_staging = "https://ci.opensearch.org/ci/dbc/distribution-build-"
self.tmp_dir = TemporaryDirectory()
self.os_process = Process()
self.osd_process = Process()

def download_artifacts(self) -> bool:
isFilePathEmpty = bool(self.args.file_path)
for project in self.args.projects:
if (isFilePathEmpty):
if ("https:" not in self.args.file_path.get(project)):
self.copy_artifact(self.args.file_path.get(project), str(self.tmp_dir.path))
else:
self.args.version = self.get_version(self.args.file_path.get(project))
self.check_url(self.args.file_path.get(project))
else:
if (self.args.artifact_type == "staging"):
self.args.file_path[project] = f"{self.base_url_staging}{project}/{self.args.version}/{self.args.build_number[project]}/linux/{self.args.arch}/{self.args.distribution}/dist/{project}/{project}-{self.args.version}-linux-{self.args.arch}.tar.gz" # noqa: E501
else:
self.args.file_path[project] = f"{self.base_url_production}{project}/{self.args.version}/{project}-{self.args.version}-linux-{self.args.arch}.tar.gz"
self.check_url(self.args.file_path.get(project))
return True

def installation(self) -> bool:
try:
for project in self.args.projects:
Expand All @@ -58,22 +36,24 @@ def installation(self) -> bool:
def start_cluster(self) -> bool:
try:
self.os_process.start(f'export OPENSEARCH_INITIAL_ADMIN_PASSWORD={get_password(str(self.args.version))} && ./opensearch-tar-install.sh', os.path.join(self.tmp_dir.path, "opensearch"))
time.sleep(85)
if ("opensearch-dashboards" in self.args.projects):
self.osd_process.start(os.path.join(str(self.tmp_dir.path), "opensearch-dashboards", "bin", "opensearch-dashboards"), ".")
time.sleep(20)
logging.info('Started cluster')
except:
raise Exception('Failed to Start Cluster')
return True

def validation(self) -> bool:
test_result, counter = ApiTestCases().test_apis(self.args.version, self.args.projects, self.check_for_security_plugin(os.path.join(self.tmp_dir.path, "opensearch")) if not self.args.force_https else True) # noqa: E501
if (test_result):
logging.info(f'All tests Pass : {counter}')
if self.check_cluster_readiness():
test_result, counter = ApiTestCases().test_apis(self.args.version, self.args.projects,
self.check_for_security_plugin(os.path.join(self.tmp_dir.path, "opensearch")) if self.args.allow_http else True)
if (test_result):
logging.info(f'All tests Pass : {counter}')
return True
else:
raise Exception(f'Not all tests Pass : {counter}')
else:
raise Exception(f'Not all tests Pass : {counter}')
return True
raise Exception("Cluster is not ready for API test")

def cleanup(self) -> bool:
try:
Expand Down
Loading

0 comments on commit f1f52ad

Please sign in to comment.