"""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)