diff --git a/kvirt/baseconfig.py b/kvirt/baseconfig.py index e7222f6ae..81bd94da8 100644 --- a/kvirt/baseconfig.py +++ b/kvirt/baseconfig.py @@ -8,7 +8,7 @@ from jinja2.exceptions import TemplateSyntaxError, TemplateError, TemplateNotFound from kvirt import common from kvirt.common import error, pprint, warning, container_mode, ssh, scp, NoAliasDumper, olm_app -from kvirt.common import PlanLoader +from kvirt.common import PlanLoader, get_kubetype from kvirt import defaults as kdefaults from kvirt.cluster import hypershift from kvirt.cluster import k3s @@ -965,33 +965,77 @@ def info_openshift_sno(self, quiet, web=False): inputfile = f'{plandir}/sno.yml' self.info_plan(inputfile, quiet=quiet, web=web) - def list_apps_generic(self, quiet=True): - plandir = os.path.dirname(kubeadm.create.__code__.co_filename) - appdir = plandir + '/apps' - return sorted([x for x in os.listdir(appdir) if os.path.isdir(f"{appdir}/{x}") and x != '__pycache__']) - - def list_apps_openshift(self, quiet=True, installed=False): - if installed: - header = 'subscription.operators.coreos.com/' - results = [] - manifestscmd = "oc get subscriptions.operators.coreos.com -A -o name" - manifestsdata = os.popen(manifestscmd).read().split('\n') + def create_app(self, app, overrides={}, outputdir=None): + kubetype = get_kubetype() + if kubetype == 'openshift': + return self.create_app_openshift(app, overrides, outputdir) else: - header = 'packagemanifest.packages.operators.coreos.com/' - results = ['autolabeller', 'users', 'metal3', 'nfs'] - manifestscmd = "oc get packagemanifest -n openshift-marketplace -o name" - manifestsdata = os.popen(manifestscmd).read().split('\n') - results.extend([entry.replace(header, '') for entry in manifestsdata if entry != '']) - return sorted(results) + return self.create_app_generic(app, overrides, outputdir) def create_app_generic(self, app, overrides={}, outputdir=None): appdir = f"{os.path.dirname(kubeadm.create.__code__.co_filename)}/apps" - return common.app_create_generic(self, app, appdir, overrides=overrides, outputdir=outputdir) + return common.create_app_generic(self, app, appdir, overrides=overrides, outputdir=outputdir) + + def create_app_openshift(self, app, overrides={}, outputdir=None): + appdir = f"{os.path.dirname(openshift.create.__code__.co_filename)}/apps" + if app in kdefaults.LOCAL_OPENSHIFT_APPS: + app_data = overrides.copy() + app_data['name'] = app + if app == 'users' and overrides.get('hypershift', False): + app_data['hypershift'] = True + return common.create_app_generic(self, app, appdir, overrides=overrides, outputdir=outputdir) + else: + name, catalog, channel, csv, description, namespace, channels, crds = common.olm_app(app, overrides) + if name is None: + error(f"Couldn't find any app matching {app}. Skipping...") + return 1 + if 'channel' in overrides: + overrides_channel = overrides['channel'] + if overrides_channel not in channels: + error(f"Target channel {channel} not found in {channels}. Skipping...") + return 1 + else: + channel = overrides_channel + if 'namespace' in overrides: + namespace = overrides['namespace'] + app_data = {'catalog': catalog, 'channel': channel, 'namespace': namespace, 'csv': csv} + app_data.update(overrides) + return common.create_app_openshift(self, app, appdir, app_data, outputdir) + + def delete_app(self, app, overrides={}): + kubetype = get_kubetype() + if kubetype == 'openshift': + return self.delete_app_openshift(app, overrides) + else: + return self.delete_app_generic(app, overrides) def delete_app_generic(self, app, overrides={}): appdir = f"{os.path.dirname(kubeadm.create.__code__.co_filename)}/apps" return common.delete_app_delete_generic(self, app, appdir, overrides=overrides) + def delete_app_openshift(self, app, overrides={}): + appdir = f"{os.path.dirname(openshift.create.__code__.co_filename)}/apps" + if app in kdefaults.LOCAL_OPENSHIFT_APPS: + app_data = overrides.copy() + if app == 'users' and overrides.get('hypershift', False): + app_data['hypershift'] = True + return common.delete_app_generic(self, app, appdir, app_data) + else: + name, catalog, channel, csv, description, namespace, channels, crds = common.olm_app(app, overrides) + if name is None: + error(f"Couldn't find any app matching {app}. Skipping...") + return 1 + app_data = {'catalog': catalog, 'channel': channel, 'namespace': namespace, 'crds': crds} + app_data.update(overrides) + return common.delete_app_openshift(self, app, appdir, app_data) + + def info_app(self, app, overrides={}): + kubetype = get_kubetype() + if kubetype == 'openshift': + return self.info_app_openshift(app, overrides) + else: + return self.info_app_generic(app) + def info_app_generic(self, app): plandir = os.path.dirname(kubeadm.create.__code__.co_filename) appdir = f"{plandir}/apps/{app}" @@ -1004,20 +1048,6 @@ def info_app_generic(self, app): with open(default_parameter_file, 'r') as f: print(f.read().strip()) - def create_app_openshift(self, app, overrides={}, outputdir=None): - appdir = f"{os.path.dirname(openshift.create.__code__.co_filename)}/apps" - if app in kdefaults.LOCAL_OPENSHIFT_APPS: - return common.create_app_generic(self, app, appdir, overrides=overrides, outputdir=outputdir) - else: - return common.create_app_openshift(self, app, appdir, overrides=overrides, outputdir=outputdir) - - def delete_app_openshift(self, app, overrides={}): - appdir = f"{os.path.dirname(openshift.create.__code__.co_filename)}/apps" - if app in kdefaults.LOCAL_OPENSHIFT_APPS: - return common.delete_app_generic(self, app, appdir, overrides=overrides) - else: - return common.delete_app_openshift(self, app, appdir, overrides=overrides) - def info_app_openshift(self, app, overrides={}): plandir = os.path.dirname(openshift.create.__code__.co_filename) if app not in kdefaults.LOCAL_OPENSHIFT_APPS: @@ -1039,6 +1069,32 @@ def info_app_openshift(self, app, overrides={}): with open(default_parameter_file, 'r') as f: print(f.read().strip()) + def list_apps(self, quiet=True, installed=False): + kubetype = get_kubetype() + if kubetype == 'openshift': + return self.list_apps_openshift(quiet=quiet, installed=installed) + else: + return self.list_apps_generic(quiet=quiet) + + def list_apps_generic(self, quiet=True): + plandir = os.path.dirname(kubeadm.create.__code__.co_filename) + appdir = plandir + '/apps' + return sorted([x for x in os.listdir(appdir) if os.path.isdir(f"{appdir}/{x}") and x != '__pycache__']) + + def list_apps_openshift(self, quiet=True, installed=False): + if installed: + header = 'subscription.operators.coreos.com/' + results = [] + manifestscmd = "oc get subscriptions.operators.coreos.com -A -o name" + manifestsdata = os.popen(manifestscmd).read().split('\n') + else: + header = 'packagemanifest.packages.operators.coreos.com/' + results = ['autolabeller', 'users', 'metal3', 'nfs'] + manifestscmd = "oc get packagemanifest -n openshift-marketplace -o name" + manifestsdata = os.popen(manifestscmd).read().split('\n') + results.extend([entry.replace(header, '') for entry in manifestsdata if entry != '']) + return sorted(results) + def download_openshift_installer(self, overrides={}): pull_secret = overrides.get('pull_secret', 'openshift_pull.json') upstream = overrides.get('upstream', False) diff --git a/kvirt/cli.py b/kvirt/cli.py index c91a3534f..365657669 100755 --- a/kvirt/cli.py +++ b/kvirt/cli.py @@ -19,7 +19,7 @@ from kvirt.common import get_git_version, compare_git_versions, interactive_kube, interactive_vm, convert_yaml_to_cmd from kvirt.config import Kconfig from kvirt.containerconfig import Kcontainerconfig -from kvirt.defaults import IMAGES, VERSION, LOCAL_OPENSHIFT_APPS, SSH_PUB_LOCATIONS, PLANTYPES, OPENSHIFT_TAG +from kvirt.defaults import IMAGES, VERSION, SSH_PUB_LOCATIONS, PLANTYPES, OPENSHIFT_TAG import os from prettytable import PrettyTable import random @@ -1252,7 +1252,7 @@ def list_subnets(args): print(subnetstable) -def create_app_generic(args): +def create_app(args): apps = args.apps outputdir = args.outputdir if outputdir is not None: @@ -1264,150 +1264,53 @@ def create_app_generic(args): elif not os.path.exists(outputdir): os.mkdir(outputdir) overrides = handle_parameters(args.param, args.paramfile, cluster=True) - if which('kubectl') is None: - error("You need kubectl to install apps") + kubectl = which('kubectl') or which('oc') + if kubectl is None: + error("You need kubectl/oc to install apps") sys.exit(1) if 'KUBECONFIG' not in os.environ: warning("KUBECONFIG not set...Using .kube/config instead") elif not os.path.isabs(os.environ['KUBECONFIG']): os.environ['KUBECONFIG'] = f"{os.getcwd()}/{os.environ['KUBECONFIG']}" baseconfig = Kbaseconfig(client=args.client, debug=args.debug, offline=True) - available_apps = baseconfig.list_apps_generic(quiet=True) for app in apps: - if app not in available_apps: - error(f"app {app} not available. Skipping...") - continue pprint(f"Adding app {app}") overrides[f'{app}_version'] = overrides[f'{app}_version'] if f'{app}_version' in overrides else 'latest' - baseconfig.create_app_generic(app, overrides, outputdir=outputdir) + baseconfig.create_app(app, overrides, outputdir) -def create_app_openshift(args): - apps = args.apps - outputdir = args.outputdir - if outputdir is not None: - if container_mode() and not outputdir.startswith('/'): - outputdir = f"/workdir/{outputdir}" - if os.path.exists(outputdir) and os.path.isfile(outputdir): - error(f"Invalid outputdir {outputdir}") - sys.exit(1) - elif not os.path.exists(outputdir): - os.mkdir(outputdir) - overrides = handle_parameters(args.param, args.paramfile, cluster=True) - if which('oc') is None: - error("You need oc to install apps") - sys.exit(1) - if 'KUBECONFIG' not in os.environ: - warning("KUBECONFIG not set...Using .kube/config instead") - elif not os.path.isabs(os.environ['KUBECONFIG']): - os.environ['KUBECONFIG'] = f"{os.getcwd()}/{os.environ['KUBECONFIG']}" - baseconfig = Kbaseconfig(client=args.client, debug=args.debug, offline=True) - for app in apps: - if app in LOCAL_OPENSHIFT_APPS: - name = app - app_data = overrides.copy() - app_data['name'] = name - if app == 'users' and args.subcommand_create_app == 'hypershift': - app_data['hypershift'] = True - else: - name, catalog, channel, csv, description, namespace, channels, crds = common.olm_app(app, overrides) - if name is None: - error(f"Couldn't find any app matching {app}. Skipping...") - continue - if 'channel' in overrides: - overrides_channel = overrides['channel'] - if overrides_channel not in channels: - error(f"Target channel {channel} not found in {channels}. Skipping...") - continue - else: - channel = overrides_channel - if 'namespace' in overrides: - namespace = overrides['namespace'] - app_data = {'catalog': catalog, 'channel': channel, 'namespace': namespace, 'csv': csv} - app_data.update(overrides) - pprint(f"Adding app {app}") - baseconfig.create_app_openshift(name, app_data, outputdir=outputdir) - - -def delete_app_generic(args): +def delete_app(args): yes = args.yes yes_top = args.yes_top if not yes and not yes_top: common.confirm("Are you sure?") apps = args.apps overrides = handle_parameters(args.param, args.paramfile, cluster=True) - if which('kubectl') is None: - error("You need kubectl to install apps") + kubectl = which('kubectl') or which('oc') + if kubectl is None: + error("You need kubectl/oc to install apps") sys.exit(1) if 'KUBECONFIG' not in os.environ: warning("KUBECONFIG not set...Using .kube/config instead") elif not os.path.isabs(os.environ['KUBECONFIG']): os.environ['KUBECONFIG'] = f"{os.getcwd()}/{os.environ['KUBECONFIG']}" baseconfig = Kbaseconfig(client=args.client, debug=args.debug, offline=True) - available_apps = baseconfig.list_apps_generic(quiet=True) for app in apps: - if app not in available_apps: - error(f"app {app} not available. Skipping...") - continue pprint(f"Deleting app {app}") - overrides[f'{app}_version'] = overrides[f'{app}_version'] if f'{app}_version' in overrides else 'latest' - baseconfig.delete_app_generic(app, overrides) - - -def delete_app_openshift(args): - yes = args.yes - yes_top = args.yes_top - if not yes and not yes_top: - common.confirm("Are you sure?") - apps = args.apps - overrides = handle_parameters(args.param, args.paramfile, cluster=True) - if which('oc') is None: - error("You need oc to install apps") - sys.exit(1) - if 'KUBECONFIG' not in os.environ: - warning("KUBECONFIG not set...Using .kube/config instead") - elif not os.path.isabs(os.environ['KUBECONFIG']): - os.environ['KUBECONFIG'] = f"{os.getcwd()}/{os.environ['KUBECONFIG']}" - baseconfig = Kbaseconfig(client=args.client, debug=args.debug, offline=True) - for app in apps: - if app in LOCAL_OPENSHIFT_APPS: - name = app - app_data = overrides.copy() - if app == 'users' and args.subcommand_delete_app == 'hypershift': - app_data['hypershift'] = True - else: - name, catalog, channel, csv, description, namespace, channels, crds = common.olm_app(app, app_data) - if name is None: - error(f"Couldn't find any app matching {app}. Skipping...") - continue - app_data = {'catalog': catalog, 'channel': channel, 'namespace': namespace, 'crds': crds} - app_data.update(overrides) - pprint(f"Deleting app {name}") - baseconfig.delete_app_openshift(app, app_data) + baseconfig.delete_app(app, overrides) -def list_apps_generic(args): - baseconfig = Kbaseconfig(client=args.client, debug=args.debug, offline=True) - apps = baseconfig.list_apps_generic(quiet=True) - output = args.global_output or args.output - if output is not None: - _list_output(apps, output) - appstable = PrettyTable(["Name"]) - for app in apps: - appstable.add_row([app]) - print(appstable) - - -def list_apps_openshift(args): - if which('oc') is None: - error("You need oc to list apps") +def list_apps(args): + kubectl = which('kubectl') or which('oc') + if kubectl is None: + error("You need kubectl/oc to list apps") sys.exit(1) if 'KUBECONFIG' not in os.environ: warning("KUBECONFIG not set...Using .kube/config instead") elif not os.path.isabs(os.environ['KUBECONFIG']): os.environ['KUBECONFIG'] = f"{os.getcwd()}/{os.environ['KUBECONFIG']}" baseconfig = Kbaseconfig(client=args.client, debug=args.debug, offline=True) - apps = baseconfig.list_apps_openshift(quiet=True, installed=args.installed) + apps = baseconfig.list_apps(quiet=True, installed=args.installed) output = args.global_output or args.output if output is not None: _list_output(apps, output) @@ -2276,9 +2179,9 @@ def restart_plan(args): sys.exit(4 if 4 in codes else 0) -def info_generic_app(args): +def info_app(args): baseconfig = Kbaseconfig(client=args.client, debug=args.debug, offline=True) - baseconfig.info_app_generic(args.app) + baseconfig.info_app(args.app) def info_openshift_disconnected(args): @@ -2286,12 +2189,6 @@ def info_openshift_disconnected(args): baseconfig.info_openshift_disconnected() -def info_app_openshift(args): - overrides = handle_parameters(args.param, args.paramfile, cluster=True) - baseconfig = Kbaseconfig(client=args.client, debug=args.debug, offline=True) - baseconfig.info_app_openshift(args.app, overrides) - - def info_plan(args): overrides = handle_parameters(args.param, args.paramfile) output = args.global_output or args.output @@ -3488,29 +3385,11 @@ def cli(): createapp_desc = 'Create Kube Apps' createapp_parser = create_subparsers.add_parser('app', description=createapp_desc, - help=createapp_desc, aliases=['apps', 'operator', 'operators']) - createapp_subparsers = createapp_parser.add_subparsers(metavar='', dest='subcommand_create_app') - - appgenericcreate_desc = 'Create Kube Generic App' - appgenericcreate_epilog = None - appgenericcreate_parser = createapp_subparsers.add_parser('generic', description=appgenericcreate_desc, - parents=[parent_parser], - help=appgenericcreate_desc, - epilog=appgenericcreate_epilog, formatter_class=rawhelp) - appgenericcreate_parser.add_argument('--outputdir', '-o', help='Output directory', metavar='OUTPUTDIR') - appgenericcreate_parser.add_argument('apps', metavar='APPS', nargs='*') - appgenericcreate_parser.set_defaults(func=create_app_generic) - - appopenshiftcreate_desc = 'Create Openshift App' - appopenshiftcreate_epilog = f"Examples:\n\n{examples.appopenshiftcreate}" - appopenshiftcreate_parser = createapp_subparsers.add_parser('openshift', description=appopenshiftcreate_desc, - help=appopenshiftcreate_desc, - parents=[parent_parser], - epilog=appopenshiftcreate_epilog, - formatter_class=rawhelp, aliases=['hypershift']) - appopenshiftcreate_parser.add_argument('--outputdir', '-o', help='Output directory', metavar='OUTPUTDIR') - appopenshiftcreate_parser.add_argument('apps', metavar='APPS', nargs='*') - appopenshiftcreate_parser.set_defaults(func=create_app_openshift) + help=createapp_desc, aliases=['apps', 'operator', 'operators'], + parents=[parent_parser]) + createapp_parser.add_argument('--outputdir', '-o', help='Output directory', metavar='OUTPUTDIR') + createapp_parser.add_argument('apps', metavar='APPS', nargs='*') + createapp_parser.set_defaults(func=create_app) bucketcreate_desc = 'Create Bucket' bucketcreate_epilog = None @@ -4178,27 +4057,11 @@ def cli(): deleteapp_desc = 'Delete Kube App' deleteapp_parser = delete_subparsers.add_parser('app', description=deleteapp_desc, - help=deleteapp_desc, aliases=['apps', 'operator', 'operators']) - deleteapp_subparsers = deleteapp_parser.add_subparsers(metavar='', dest='subcommand_delete_app') - - appgenericdelete_desc = 'Delete Kube App Generic' - appgenericdelete_epilog = None - appgenericdelete_parser = deleteapp_subparsers.add_parser('generic', description=appgenericdelete_desc, - help=appgenericdelete_desc, parents=[parent_parser], - epilog=appgenericdelete_epilog, formatter_class=rawhelp) - appgenericdelete_parser.add_argument('-y', '--yes', action='store_true', help='Dont ask for confirmation') - appgenericdelete_parser.add_argument('apps', metavar='APPS', nargs='*') - appgenericdelete_parser.set_defaults(func=delete_app_generic) - - appopenshiftdelete_desc = 'Delete Kube App Openshift' - appopenshiftdelete_epilog = None - appopenshiftdelete_parser = deleteapp_subparsers.add_parser('openshift', description=appopenshiftdelete_desc, - help=appopenshiftdelete_desc, parents=[parent_parser], - epilog=appopenshiftdelete_epilog, - formatter_class=rawhelp, aliases=['hypershift']) - appopenshiftdelete_parser.add_argument('-y', '--yes', action='store_true', help='Dont ask for confirmation') - appopenshiftdelete_parser.add_argument('apps', metavar='APPS', nargs='*') - appopenshiftdelete_parser.set_defaults(func=delete_app_openshift) + help=deleteapp_desc, aliases=['apps', 'operator', 'operators'], + parents=[parent_parser]) + deleteapp_parser.add_argument('-y', '--yes', action='store_true', help='Dont ask for confirmation') + deleteapp_parser.add_argument('apps', metavar='APPS', nargs='*') + deleteapp_parser.set_defaults(func=delete_app) bucketfiledelete_desc = 'Delete Bucket file' bucketfiledelete_parser = argparse.ArgumentParser(add_help=False) @@ -4546,21 +4409,9 @@ def cli(): appinfo_desc = 'Info App' appinfo_parser = info_subparsers.add_parser('app', description=appinfo_desc, help=appinfo_desc, - aliases=['operator']) - appinfo_subparsers = appinfo_parser.add_subparsers(metavar='', dest='subcommand_info_app') - - appgenericinfo_desc = 'Info Generic App' - appgenericinfo_parser = appinfo_subparsers.add_parser('generic', description=appgenericinfo_desc, - help=appgenericinfo_desc) - - appgenericinfo_parser.add_argument('app', metavar='APP') - appgenericinfo_parser.set_defaults(func=info_generic_app) - - appopenshiftinfo_desc = 'Info Openshift App' - appopenshiftinfo_parser = appinfo_subparsers.add_parser('openshift', description=appopenshiftinfo_desc, - help=appopenshiftinfo_desc, parents=[parent_parser]) - appopenshiftinfo_parser.add_argument('app', metavar='APP') - appopenshiftinfo_parser.set_defaults(func=info_app_openshift) + aliases=['operator'], parents=[parent_parser]) + appinfo_parser.add_argument('app', metavar='APP') + appinfo_parser.set_defaults(func=info_app) baremetalhostinfo_desc = 'Report info about Baremetal Host' baremetalhostinfo_epilog = f"Examples:\n\n{examples.infohost}" @@ -4734,20 +4585,10 @@ def cli(): listapp_desc = 'List Available Kube Apps' listapp_parser = list_subparsers.add_parser('app', description=listapp_desc, - help=listapp_desc, aliases=['apps', 'operator', 'operators']) - listapp_subparsers = listapp_parser.add_subparsers(metavar='', dest='subcommand_list_app') - - appgenericlist_desc = 'List Available Kube Apps Generic' - appgenericlist_parser = listapp_subparsers.add_parser('generic', description=appgenericlist_desc, - help=appgenericlist_desc, parents=[output_parser]) - appgenericlist_parser.set_defaults(func=list_apps_generic) - - appopenshiftlist_desc = 'List Available Kube Components Openshift' - appopenshiftlist_parser = listapp_subparsers.add_parser('openshift', description=appopenshiftlist_desc, - help=appopenshiftlist_desc, aliases=['hypershift'], - parents=[output_parser]) - appopenshiftlist_parser.add_argument('-i', '--installed', action='store_true', help='Show installed apps') - appopenshiftlist_parser.set_defaults(func=list_apps_openshift) + help=listapp_desc, aliases=['apps', 'operator', 'operators'], + parents=[output_parser]) + listapp_parser.add_argument('-i', '--installed', action='store_true', help='Show installed apps') + listapp_parser.set_defaults(func=list_apps) imagelist_desc = 'List Available Images' imagelist_parser = list_subparsers.add_parser('available-images', description=imagelist_desc, help=imagelist_desc, diff --git a/kvirt/common/__init__.py b/kvirt/common/__init__.py index 35b6e21d4..29ca04942 100644 --- a/kvirt/common/__init__.py +++ b/kvirt/common/__init__.py @@ -2560,3 +2560,26 @@ def sdn_ip(ip, kubetype, cluster_network): if cluster_network is None: cluster_network = '10.132.0.0/14' return kubetype is not None and kubetype == 'openshift' and ip_address(ip) in ip_network(cluster_network) + + +def get_kubetype(): + kubectl = which('kubectl') or which('oc') + openshift_command = f'{kubectl} get project 2>/dev/null' + cloud_command = f'{kubectl} cluster-info' + if os.popen(openshift_command).read().strip() != '': + kubetype = 'openshift' + elif 'eks.amazonaws.com' in os.popen(cloud_command).read().strip(): + kubetype = 'eks' + else: + kubetype = 'generic' + pprint(f"Detected kubetype {kubetype}") + return kubetype + # else: + # cloud_data = os.popen(cloud_command).read().strip() + # if 'gke.googleapis.com' in cloud_data: + # return 'gke' + # elif 'eks.amazonaws.com' in cloud_data: + # return 'eks' + # elif 'azmk8s.io' in cloud_data: + # return 'ake' + # return 'generic'