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

Support new distribution-validations and add few enhancements to validation workflow #4447

Merged
merged 6 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
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.
60 changes: 60 additions & 0 deletions src/validation_workflow/deb/validation_deb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# 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 not self.args.force_https else True) # noqa: E501
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
50 changes: 8 additions & 42 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,7 +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 = ['opensearchproject/' + project for project in self.args.projects]
self.image_names_list = [x for x in self.image_names_list if (os.path.basename(x) in self.args.projects)]
Copy link
Contributor

@jordarlu jordarlu Feb 15, 2024

Choose a reason for hiding this comment

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

what does these two lines of self.image_names_list do?

Copy link
Member

Choose a reason for hiding this comment

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

Second this.

Copy link
Collaborator Author

@Divyaasm Divyaasm Feb 15, 2024

Choose a reason for hiding this comment

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

I have unassigned the args self.args.OS_image, self.args.OSD_image in args.py and declared a list which stores the images names based on projects=["opensearch", "opensearchdashboards"]
FYI: self.OS_image = 'opensearchproject/opensearch'
self.OSD_image = 'opensearchproject/opensearch-dashboards'

Copy link
Contributor

@jordarlu jordarlu Feb 16, 2024

Choose a reason for hiding this comment

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

thanks, Divya, I just feel a bit redundant here becasue you constructed the image_names_list from args.projects on the first line of code, and then you do a filtering from the same args.projects on the next line of code. Could you give me an exmaple of expected output from the 2nd line of code assuming the image_names_list has ["opensearchproject/opensearch", "opensearchproject/opensearchdashboards"] at the first line?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I get it now. Sorry missed this line while editing. It's of no use. Will remove it Thanks for pointing.

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):
Expand All @@ -79,7 +75,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 +90,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 +125,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 +207,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 +217,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)
36 changes: 8 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,15 @@ 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 not self.args.force_https else True) # noqa: E501
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
37 changes: 8 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,23 @@ 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 not self.args.force_https else True) # noqa: E501
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
Loading