
Instead of using string manipulation and hardcoded path separators, use the os.path.join method for better consistency. Add some initial pytest unit tests. Tests include one to verify the generator can create a fluxcd application. Apply flake8 corrections to entire codebase. Apply pylint corrections to entire codebase. Diabled bandit. Update tox.ini file and test-requirements.txt Make updates to .zuul.yaml to enable coverage tests with helm. Coverage check will fail if coverage falls below 55% for now. Zuul is using the same version of helm as is current in STX; v3.12.2. TODO - Make sure flake8, pylint, etc.. are good. Test Plan: PASS - Verify the generator can still build an example fluxcd app TODO - Verify the generator can still buld an example armada app Story: 2010937 Task: 48918 Task: 48917 Change-Id: I18a52cd98b730cc96b2b51cd4c1ab16785ad9857 Signed-off-by: Reed, Joshua <Joshua.Reed@windriver.com>
246 lines
8.5 KiB
Python
246 lines
8.5 KiB
Python
"""Generator Main Module."""
|
|
import getopt
|
|
import os
|
|
import re
|
|
import shutil
|
|
import sys
|
|
import yaml
|
|
|
|
from app_gen_tool.application import Application
|
|
|
|
|
|
def parse_yaml(yaml_in):
|
|
"""Pare generator input yaml file."""
|
|
yaml_data = ''
|
|
try:
|
|
with open(yaml_in, 'r', encoding='utf-8') as f:
|
|
yaml_data = yaml.safe_load(f)
|
|
except FileNotFoundError:
|
|
print('Error: {yaml_in} not found')
|
|
except Exception:
|
|
print('Error: Invalid yaml file')
|
|
return yaml_data
|
|
|
|
|
|
def check_manifest(manifest_data): # pylint: disable=too-many-return-statements
|
|
"""Check generator input yaml file for correct inputs."""
|
|
# TODO: check more mandatory key/values in manifest yaml
|
|
|
|
# check app values
|
|
if 'appName' not in manifest_data['appManifestFile-config']:
|
|
print('Error: \'appName\' is missing.')
|
|
return False
|
|
|
|
if 'namespace' not in manifest_data['appManifestFile-config']:
|
|
print('Error: \'namespace\' is missing.')
|
|
return False
|
|
|
|
if 'appVersion' not in manifest_data['appManifestFile-config']:
|
|
print('Error: \'appVersion\' is missing.')
|
|
return False
|
|
|
|
# check chartGroup values
|
|
if 'chartGroup' not in manifest_data['appManifestFile-config']:
|
|
print('Error: \'chartGroup\' is missing.')
|
|
return False
|
|
|
|
# check chart values
|
|
if 'chart' not in manifest_data['appManifestFile-config']:
|
|
print('Error: \'chart\' is missing.')
|
|
return False
|
|
|
|
for chart in manifest_data['appManifestFile-config']['chart']:
|
|
# check chart name
|
|
if 'name' not in chart:
|
|
print('Error: Chart attribute \'name\' is missing.')
|
|
return False
|
|
|
|
# check chart version
|
|
if 'version' not in chart:
|
|
print('Error: Chart attribute \'version\' is missing.')
|
|
return False
|
|
|
|
# check chart path, supporting: dir, git, tarball
|
|
if 'path' not in chart:
|
|
print(f'Error: Chart attribute \'path\' is missing in chart {chart["name"]}.')
|
|
return False
|
|
else:
|
|
# TODO: To support branches/tags in git repo
|
|
if chart['path'].endswith('.git'):
|
|
if 'subpath' not in chart:
|
|
print(
|
|
'Error: Chart attribute \'subpath\' is missing in '
|
|
f'chart {chart["name"]}.'
|
|
)
|
|
return False
|
|
chart['_pathType'] = 'git'
|
|
gitname = re.search(r'[^/]+(?=\.git$)', chart['path']).group()
|
|
if gitname:
|
|
chart['_gitname'] = gitname
|
|
else:
|
|
print(f'Error: Invalid \'path\' in chart {chart["name"]}.')
|
|
print(
|
|
' only \'local dir\', \'.git\', \'.tar.gz\', \'.tgz\' are supported'
|
|
)
|
|
return False
|
|
elif chart['path'].endswith('.tar.gz') or chart['path'].endswith('.tgz'):
|
|
if 'subpath' not in chart:
|
|
print(
|
|
'Error: Chart attribute \'subpath\' is missing in '
|
|
f'chart {chart["name"]}.'
|
|
)
|
|
return False
|
|
chart['_pathType'] = 'tarball'
|
|
tarname = \
|
|
re.search(
|
|
r'[^/]+(?=\.tgz)|[^/]+(?=\.tar\.gz)',
|
|
chart['path']
|
|
).group()
|
|
if tarname:
|
|
chart['_tarname'] = tarname
|
|
else:
|
|
print(f'Error: Invalid \'path\' in chart {chart["name"]}.')
|
|
print(
|
|
' only \'local dir\', \'.git\', \'.tar.gz\', \'.tgz\' are supported'
|
|
)
|
|
return False
|
|
else:
|
|
|
|
if not os.path.isdir(chart['path']):
|
|
print(f'Error: Invalid \'path\' in chart {chart["name"]}.')
|
|
print(
|
|
' only \'local dir\', \'.git\', \'.tar.gz\', \'.tgz\' are supported'
|
|
)
|
|
return False
|
|
chart['_pathType'] = 'dir'
|
|
|
|
return True
|
|
|
|
|
|
def print_help():
|
|
"""Print CLI Helm Menu."""
|
|
print('StarlingX User Application Generator')
|
|
print('')
|
|
print('Usage:')
|
|
print(' python app-gen.py [Option]')
|
|
print('')
|
|
print('Options:')
|
|
print(' -i, --input yaml_file generate app from yaml_file')
|
|
print(' -o, --output folder generate app to output folder')
|
|
print(' -t, --type package select Armada, Fluxcd or Both '
|
|
'packaging')
|
|
print(' --overwrite overwrite the output dir')
|
|
print(' --no-package does not create app tarball')
|
|
print(' --package-only only creates tarball from dir')
|
|
print(' -h, --help help for StarlingX User Application '
|
|
'Generator')
|
|
|
|
|
|
def check_input_file(app_data) -> bool:
|
|
"""Check if input file passed by user is valid."""
|
|
if not app_data:
|
|
print('Parse yaml error')
|
|
return False
|
|
if not check_manifest(app_data):
|
|
print('Application manifest is not valid')
|
|
return False
|
|
return True
|
|
|
|
|
|
def check_app_directory(app_out, overwrite, package_only) -> bool:
|
|
"""Checks if the user gave enough information to modify or create app folder directory."""
|
|
|
|
if os.path.exists(app_out) and not overwrite and not package_only:
|
|
print(f'Output folder {app_out} exists, please remove it, use '
|
|
f'--overwrite, or use --package-only.')
|
|
return False
|
|
if not os.path.exists(app_out) and package_only:
|
|
print(f'Output folder {app_out} does not exist, cannot package it.')
|
|
return False
|
|
return True
|
|
|
|
|
|
def create_app_directories(app_out, overwrite):
|
|
"""Creates or/and removes the app generated directories"""
|
|
if os.path.exists(app_out) and overwrite:
|
|
shutil.rmtree(app_out)
|
|
if not os.path.exists(app_out):
|
|
os.makedirs(app_out)
|
|
|
|
|
|
def generate_app(app, package_type, no_package, package_only):
|
|
"""Generate app based on application packaging type."""
|
|
if package_type == 'armada' or package_type == 'both': # pylint: disable=consider-using-in
|
|
app.gen_armada_app(app.output_folder, no_package, package_only)
|
|
if package_type == 'fluxcd' or package_type == 'both': # pylint: disable=consider-using-in
|
|
app.gen_fluxcd_app(app.output_folder, no_package, package_only)
|
|
|
|
|
|
def main(argv):
|
|
"""Main Method with argument parsing."""
|
|
input_file = ''
|
|
output_folder = '.'
|
|
package_type = ''
|
|
overwrite = False
|
|
package_only = False
|
|
no_package = False
|
|
try:
|
|
options, _ = \
|
|
getopt.getopt(
|
|
argv,
|
|
'hi:o:t:',
|
|
[
|
|
'help',
|
|
'input=',
|
|
'output=',
|
|
'type=',
|
|
'overwrite',
|
|
'no-package',
|
|
'package-only'
|
|
]
|
|
)
|
|
except getopt.GetoptError:
|
|
print('Error: Invalid argument')
|
|
sys.exit(1)
|
|
for option, value in options:
|
|
if option in ('-h', '--help'):
|
|
print_help()
|
|
if option in ('--overwrite'):
|
|
overwrite = True
|
|
if option in ('-i', '--input', '--input='):
|
|
input_file = value
|
|
if option in ('-o', '--output', '--output='):
|
|
output_folder = value
|
|
if option in ('-t', '--type', '--type='):
|
|
package_type = value.lower()
|
|
if option in ('--no-package'):
|
|
no_package = True
|
|
if option in ('--package-only'):
|
|
package_only = True
|
|
|
|
if overwrite and package_only:
|
|
print('Error: Selecting both overwrite and package-only is not allowed'
|
|
'. Please consult our README if further clarification is needed')
|
|
sys.exit(1)
|
|
if not package_type:
|
|
print('Error: Select type of packaging (armada/fluxcd/both)')
|
|
sys.exit(1)
|
|
if not os.path.isfile(os.path.abspath(input_file)):
|
|
print('Error: input file not found')
|
|
sys.exit(1)
|
|
if input_file:
|
|
app_data = parse_yaml(input_file)
|
|
if not check_input_file(app_data):
|
|
sys.exit(1)
|
|
|
|
output_folder = os.path.abspath(output_folder)
|
|
|
|
app = Application(app_data, package_type, output_folder)
|
|
|
|
if not check_app_directory(app.output_folder, overwrite, package_only):
|
|
sys.exit(1)
|
|
|
|
create_app_directories(app.output_folder, overwrite)
|
|
|
|
generate_app(app, package_type, no_package, package_only)
|