Automatically add prechecks scripts to patch

When building a patch that includes the software package, the
deploy-precheck script and the upgrade_utils.py will be automatically
fetched from .deb file and included directly inside the patch. With this
there is no need to have element 'deploy_precheck' in XML recipe schema.

Test Plan:
    PASS: Create patch successfully with software package included.
        Upload it to a AIO-SX system.
        Run "software deploy precheck" on the installed patch.

    PASS: Create patch successfully without software package included.
        Upload it to a AIO-SX system.
        Run "software deploy precheck" on the installed patch.

Story: 2010676
Task: 49978

Change-Id: I9d470ed049d42880bb515320324de1e3a0e05680
Signed-off-by: Dostoievski Batista <dostoievski.albinobatista@windriver.com>
This commit is contained in:
Dostoievski Batista 2024-04-25 19:10:52 -03:00
parent ef12b8b2ba
commit 1f92282164
8 changed files with 100 additions and 49 deletions

View File

@ -20,7 +20,6 @@
<!-- Pre and Post install hook scripts -->
<pre_install>scripts/pre-install.sh</pre_install>
<post_install>scripts/post-install.sh</post_install>
<deploy_precheck></deploy_precheck>
<!-- Packages to be included -->
<stx_packages>
<!-- Starlingx packages -->

View File

@ -15,7 +15,6 @@
<!-- Pre and Post install hook scripts are optional -->
<pre_install></pre_install>
<post_install></post_install>
<deploy_precheck></deploy_precheck>
<!-- Packages to be included -->
<stx_packages>
<!-- Starlingx packages -->

View File

@ -20,7 +20,6 @@
<!-- Pre and Post install hook scripts -->
<pre_install>scripts/pre-install.sh</pre_install>
<post_install>scripts/post-install.sh</post_install>
<deploy_precheck>scripts/deploy-precheck.sh</deploy_precheck>
<!-- Packages to be included -->
<stx_packages>
<!-- Starlingx packages -->

View File

@ -20,7 +20,6 @@
<!-- Pre and Post install hook scripts are optional -->
<pre_install>scripts/pre-install.sh</pre_install>
<post_install>scripts/post-install.sh</post_install>
<deploy_precheck>scripts/deploy-precheck.sh</deploy_precheck>
<!-- Packages to be included -->
<stx_packages>
<!-- Starlingx packages -->

View File

@ -22,7 +22,6 @@
<xs:element name="semantics" type="xs:string"/>
<xs:element name="pre_install" type="xs:string"/>
<xs:element name="post_install" type="xs:string"/>
<xs:element name="deploy_precheck" type="xs:string"/>
<xs:element name="stx_packages">
<xs:complexType>
<xs:sequence>

View File

@ -35,7 +35,6 @@ WARNINGS = 'warnings'
REBOOT_REQUIRED = 'reboot_required'
PRE_INSTALL = 'pre_install'
POST_INSTALL = 'post_install'
DEPLOY_PRECHECK = 'deploy_precheck'
UNREMOVABLE = 'unremovable'
REQUIRES = 'requires'
REQUIRES_PATCH_ID = 'req_patch_id'
@ -97,7 +96,6 @@ class PatchMetadata(object):
# strip path from pre_install and post_install scripts
self.pre_install = self.pre_install.split('/')[-1]
self.post_install = self.post_install.split('/')[-1]
self.deploy_precheck = self.deploy_precheck.split('/')[-1]
top_tag = ET.Element(PATCH_ROOT_TAG)
self.__add_text_tag_to_xml(top_tag, PATCH_ID, self.patch_id)
@ -127,7 +125,6 @@ class PatchMetadata(object):
self.__add_text_tag_to_xml(top_tag, PRE_INSTALL, self.pre_install)
self.__add_text_tag_to_xml(top_tag, POST_INSTALL, self.post_install)
self.__add_text_tag_to_xml(top_tag, DEPLOY_PRECHECK, self.deploy_precheck)
packages_tag = ET.SubElement(top_tag, PACKAGES)
for package in sorted(self.debs):
@ -158,7 +155,6 @@ class PatchMetadata(object):
self.reboot_required = patch_recipe[REBOOT_REQUIRED]
self.pre_install = self.check_script_path(patch_recipe[PRE_INSTALL])
self.post_install = self.check_script_path(patch_recipe[POST_INSTALL])
self.deploy_precheck = self.check_script_path(patch_recipe[DEPLOY_PRECHECK])
self.unremovable = patch_recipe[UNREMOVABLE]
self.status = patch_recipe[STATUS]
if 'id' in patch_recipe[REQUIRES]:

View File

@ -40,7 +40,8 @@ PATCH_OUTPUT = os.path.join(BUILD_ROOT, "patch_output")
PATCH_SCRIPTS = {
"PRE_INSTALL": "pre-install.sh",
"POST_INSTALL": "post-install.sh",
"DEPLOY_PRECHECK": "deploy-precheck.sh"
"DEPLOY_PRECHECK": "deploy-precheck",
"UPGRADE_UTILS": "upgrade_utils.py",
}
class PatchBuilder(object):
@ -92,19 +93,33 @@ class PatchBuilder(object):
pre_install = self.metadata.pre_install
post_install = self.metadata.post_install
deploy_precheck = self.metadata.deploy_precheck
# pre/post install scripts
if pre_install:
logger.debug(f"Copying pre-install script: {pre_install}")
self.copy_script("PRE_INSTALL", pre_install)
self.copy_rename_script(pre_install, "PRE_INSTALL")
if post_install:
logger.debug(f"Copying post-install script: {post_install}")
self.copy_script("POST_INSTALL", post_install)
self.copy_rename_script(post_install, "POST_INSTALL")
if deploy_precheck:
logger.debug(f"Copying deploy pre-check script: {deploy_precheck}")
self.copy_script("DEPLOY_PRECHECK", deploy_precheck)
# if the patch includes the 'software' package we need to make deploy-precheck
# and upgrade_utils.py from .deb file accessible directly from patch file
if 'software' in self.metadata.stx_packages:
logger.info(f"Patch includes the software package, getting scripts from deb file...")
# create temporary folder to hold our files until we copy them to the patch
tmp_folder = tempfile.mkdtemp(prefix='deb_')
# Collect files
files_to_get = [PATCH_SCRIPTS["DEPLOY_PRECHECK"], PATCH_SCRIPTS["UPGRADE_UTILS"]]
path_files = self.get_files_from_deb(dl_dir, tmp_folder, 'software', files_to_get)
for path in path_files:
self.copy_rename_script(path_to_script=path, rename=False)
# removing the temporary folder
shutil.rmtree(tmp_folder)
if not pre_install and not post_install and self.metadata.reboot_required == 'N':
logger.warn("In service patch without restart scripts provided")
@ -120,20 +135,89 @@ class PatchBuilder(object):
# Pack .patch file
self.__sign_and_pack(self.patch_name)
def copy_script(self, script_type, install_script):
if not os.path.isfile(install_script):
erro_msg = f"Install script {install_script} not found"
def copy_rename_script(self, path_to_script, script_type=None, rename=True):
'''
Copy the script to the directory we are in and rename based
on PATCH_SCRIPT, if necessary.
:param path_to_script: Path to the script
:param script_type: Type of the script from the constant PATCH_SCRIPTS
:param rename: Select if we should
'''
if not os.path.isfile(path_to_script):
erro_msg = f"Install script {path_to_script} not found"
logger.error(erro_msg)
raise FileNotFoundError(erro_msg)
# We check the type to correctly rename the file to a expected value
script_name = PATCH_SCRIPTS.get(script_type, None)
# check if need a rename or not
if rename:
# We check the type to correctly rename the file to a expected value
script_name = PATCH_SCRIPTS.get(script_type, None)
if script_name:
logger.info(f"Renaming {install_script} to {script_name}")
shutil.copy(install_script, f"./{script_name}")
if script_name and rename:
logger.info(f"Renaming {path_to_script} to {script_name}")
shutil.copy(path_to_script, f"./{script_name}")
else:
raise ValueError(f"Script type provided is not valid one: {script_type}")
else:
raise ValueError(f"Script type provided is not valid one: {script_type}")
logger.info(f"Copying {path_to_script}...")
shutil.copy(path_to_script, "./")
def get_files_from_deb(self, download_dir, tmp_folder, package_name, files):
'''
Get files from inside the .deb and make it available in temporary folder
:param download_dir: Full path of directory where the deb is downloaded
:param tmp_folder: Temporary folder where file will be available
:param package_name: Name of the package
:param files: List of name of the files to be extracted
:returns list: full path for the script file
'''
# from download dir, search for {package_name}_*.deb package
pkg_name = None
for file in os.listdir(download_dir):
if file.startswith(f'{package_name}_') and file.endswith('.deb'):
pkg_name = file
if not pkg_name:
erro_msg = f'Unable to find {package_name} package inside download folder'
logger.error(erro_msg)
raise FileNotFoundError(erro_msg)
deb_path = os.path.join(download_dir, pkg_name)
# we copy deb to the temporary folder
shutil.copy(deb_path, tmp_folder)
# We first unpack deb file and get data.tar.xz from there
cmd = ['ar', '-x', os.path.join(tmp_folder, pkg_name)]
subprocess.check_call(cmd, cwd=tmp_folder)
# With data.tar.xz, we try to find script file
data_tar = tarfile.open(os.path.join(tmp_folder, 'data.tar.xz'))
paths = []
for f in files:
file_tarpath = None
for member in data_tar.getnames():
if member.endswith(f):
file_tarpath = member
if not file_tarpath:
erro_msg = f"Unable to find {f} inside data tar."
logger.error(erro_msg)
raise FileNotFoundError(erro_msg)
# We extract said file to the temporary folder
data_tar.extract(file_tarpath, path=tmp_folder)
# add it to our return
paths.append(os.path.join(tmp_folder, file_tarpath))
data_tar.close()
return paths
def __sign_and_pack(self, patch_file):
"""

View File

@ -1,24 +0,0 @@
#!/bin/bash
#
#
# Copyright (c) 2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
#
# The patching subsystem provides a patch-functions bash source file
# with useful function and variable definitions.
#
. /etc/patching/patch-functions
#
# Declare an overall script return code
#
declare -i GLOBAL_RC=$PATCH_STATUS_OK
echo "Pre deploy check script"
#
# Exit the script with the overall return code
#
exit $GLOBAL_RC