diff --git a/cli/dcoscli/analytics.py b/cli/dcoscli/analytics.py index 46d792f..db76555 100644 --- a/cli/dcoscli/analytics.py +++ b/cli/dcoscli/analytics.py @@ -6,7 +6,7 @@ import uuid import dcoscli import requests import rollbar -from dcos.api import config, constants, util +from dcos.api import util from dcoscli.constants import (ROLLBAR_SERVER_POST_KEY, SEGMENT_IO_CLI_ERROR_EVENT, SEGMENT_IO_CLI_EVENT, SEGMENT_IO_WRITE_KEY_DEV, @@ -31,7 +31,7 @@ def wait_and_track(subproc): rollbar.init(ROLLBAR_SERVER_POST_KEY, 'prod' if _is_prod() else 'dev') - conf = _conf() + conf = util.get_config() report = conf.get('core.reporting', True) with ThreadPoolExecutor(max_workers=2) as pool: if report: @@ -81,17 +81,6 @@ def _is_prod(): return os.environ.get('DCOS_PRODUCTION', 'true') != 'false' -def _conf(): - """ - Get config file. - - :rtype: Toml - """ - - return config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV]) - - def _wait_and_capture(subproc): """ Run a subprocess and capture its stderr. @@ -202,7 +191,7 @@ def _base_properties(conf=None): """ if not conf: - conf = _conf() + conf = util.get_config() cmd = 'dcos' + (' {}'.format(sys.argv[1]) if len(sys.argv) > 1 else '') return { diff --git a/cli/dcoscli/marathon/main.py b/cli/dcoscli/marathon/main.py index 74e5b8d..47f972d 100644 --- a/cli/dcoscli/marathon/main.py +++ b/cli/dcoscli/marathon/main.py @@ -54,15 +54,13 @@ Positional arguments: The task id """ import json -import os import sys import time import dcoscli import docopt import pkg_resources -from dcos.api import (cmds, config, constants, emitting, errors, jsonitem, - marathon, options, util) +from dcos.api import cmds, emitting, errors, jsonitem, marathon, options, util logger = util.get_logger(__name__) emitter = emitting.FlatEmitter() @@ -250,9 +248,7 @@ def _add(app_resource): return 1 # Add application to marathon - client = marathon.create_client( - config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV])) + client = marathon.create_client() # Check that the application doesn't exist app_id = client.normalize_app_id(application_resource['id']) @@ -275,9 +271,7 @@ def _list(): :rtype: int """ - client = marathon.create_client( - config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV])) + client = marathon.create_client() apps, err = client.get_apps() if err is not None: @@ -299,9 +293,7 @@ def _remove(app_id, force): :rtype: int """ - client = marathon.create_client( - config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV])) + client = marathon.create_client() err = client.remove_app(app_id, force) if err is not None: @@ -322,9 +314,7 @@ def _show(app_id, version): :rtype: int """ - client = marathon.create_client( - config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV])) + client = marathon.create_client() if version is not None: version, err = _calculate_version(client, app_id, version) @@ -356,9 +346,7 @@ def _start(app_id, instances, force): """ # Check that the application exists - client = marathon.create_client( - config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV])) + client = marathon.create_client() desc, err = client.get_app(app_id) if err is not None: @@ -426,9 +414,7 @@ def _stop(app_id, force): """ # Check that the application exists - client = marathon.create_client( - config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV])) + client = marathon.create_client() desc, err = client.get_app(app_id) if err is not None: @@ -465,9 +451,7 @@ def _update(app_id, json_items, force): """ # Check that the application exists - client = marathon.create_client( - config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV])) + client = marathon.create_client() _, err = client.get_app(app_id) if err is not None: @@ -535,9 +519,7 @@ def _restart(app_id, force): :rtype: int """ - client = marathon.create_client( - config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV])) + client = marathon.create_client() desc, err = client.get_app(app_id) if err is not None: @@ -579,9 +561,7 @@ def _version_list(app_id, max_count): emitter.publish(err) return 1 - client = marathon.create_client( - config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV])) + client = marathon.create_client() versions, err = client.get_app_versions(app_id, max_count) if err is not None: @@ -601,9 +581,7 @@ def _deployment_list(app_id): :rtype: int """ - client = marathon.create_client( - config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV])) + client = marathon.create_client() deployments, err = client.get_deployments(app_id) if err is not None: @@ -623,9 +601,7 @@ def _deployment_stop(deployment_id): :rtype: int """ - client = marathon.create_client( - config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV])) + client = marathon.create_client() err = client.stop_deployment(deployment_id) if err is not None: @@ -643,9 +619,7 @@ def _deployment_rollback(deployment_id): :rtype: int """ - client = marathon.create_client( - config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV])) + client = marathon.create_client() deployment, err = client.rollback_deployment(deployment_id) if err is not None: @@ -683,9 +657,7 @@ def _deployment_watch(deployment_id, max_count, interval): else: interval = 1 - client = marathon.create_client( - config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV])) + client = marathon.create_client() count = 0 while max_count is None or count < max_count: @@ -712,9 +684,7 @@ def _task_list(app_id): :rtype: int """ - client = marathon.create_client( - config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV])) + client = marathon.create_client() tasks, err = client.get_tasks(app_id) if err is not None: @@ -734,9 +704,7 @@ def _task_show(task_id): :rtype: int """ - client = marathon.create_client( - config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV])) + client = marathon.create_client() task, err = client.get_task(task_id) if err is not None: @@ -771,9 +739,7 @@ def _update_from_stdin(app_id, force): return 1 # Add application to marathon - client = marathon.create_client( - config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV])) + client = marathon.create_client() _, err = client.update_app(app_id, application_resource, force) if err is not None: diff --git a/cli/dcoscli/package/main.py b/cli/dcoscli/package/main.py index 1eedd52..df9a14c 100644 --- a/cli/dcoscli/package/main.py +++ b/cli/dcoscli/package/main.py @@ -5,12 +5,14 @@ Usage: dcos package --info dcos package describe dcos package info - dcos package install [--options= --app-id= --cli --app] + dcos package install [--cli | [--app --app-id=] dcos package list-installed [--endpoints --app-id= ] dcos package search [] dcos package sources - dcos package uninstall [--all | --app-id=] + dcos package uninstall [--cli | [--app --app-id= --all]] + dcos package update [--validate] Options: @@ -44,13 +46,12 @@ Configuration: """ import json -import os import dcoscli import docopt import pkg_resources -from dcos.api import (cmds, config, constants, emitting, errors, marathon, - options, package, subcommand, util) +from dcos.api import (cmds, emitting, errors, marathon, options, package, + subcommand, util) logger = util.get_logger(__name__) @@ -116,7 +117,7 @@ def _cmds(): cmds.Command( hierarchy=['package', 'uninstall'], - arg_keys=['', '--all', '--app-id'], + arg_keys=['', '--all', '--app-id', '--cli', '--app'], function=_uninstall), cmds.Command( @@ -151,15 +152,6 @@ def _package(config_schema, info): return 0 -def _load_config(): - """ - :returns: Configuration object - :rtype: Toml - """ - return config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV]) - - def _info(): """Print package cli information. @@ -178,7 +170,7 @@ def _list_sources(): :rtype: int """ - config = _load_config() + config = util.get_config() sources, err = package.list_sources(config) @@ -201,7 +193,7 @@ def _update(validate): :rtype: int """ - config = _load_config() + config = util.get_config() errs = package.update_sources(config, validate) @@ -222,7 +214,7 @@ def _describe(package_name): :rtype: int """ - config = _load_config() + config = util.get_config() pkg, err = package.resolve_package(package_name, config) if err is not None: @@ -282,7 +274,7 @@ def _install(package_name, options_path, app_id, cli, app): cli = True app = True - config = _load_config() + config = util.get_config() pkg, err = package.resolve_package(package_name, config) if err is not None: @@ -381,7 +373,7 @@ def _list(endpoints, app_id, package_name): :rtype: int """ - config = _load_config() + config = util.get_config() init_client = marathon.create_client(config) @@ -417,7 +409,7 @@ def _search(query): if not query: query = '' - config = _load_config() + config = util.get_config() results, error = package.search(query, config) @@ -430,7 +422,7 @@ def _search(query): return 0 -def _uninstall(package_name, remove_all, app_id): +def _uninstall(package_name, remove_all, app_id, cli, app): """Uninstall the specified package. :param package_name: The package to uninstall @@ -443,18 +435,9 @@ def _uninstall(package_name, remove_all, app_id): :rtype: int """ - config = _load_config() - - init_client = marathon.create_client(config) - - uninstall_error = package.uninstall( - package_name, - remove_all, - app_id, - init_client) - - if uninstall_error is not None: - emitter.publish(uninstall_error) + err = package.uninstall(package_name, remove_all, app_id, cli, app) + if err is not None: + emitter.publish(err) return 1 return 0 diff --git a/cli/dcoscli/subcommand/main.py b/cli/dcoscli/subcommand/main.py index 2a2f37b..5e35ddc 100644 --- a/cli/dcoscli/subcommand/main.py +++ b/cli/dcoscli/subcommand/main.py @@ -161,7 +161,7 @@ def _uninstall(package_name): :rtype: int """ - subcommand.uninstall(package_name, util.dcos_path()) + subcommand.uninstall(package_name) return 0 diff --git a/cli/tests/integrations/cli/test_package.py b/cli/tests/integrations/cli/test_package.py index 3737296..1f2cf13 100644 --- a/cli/tests/integrations/cli/test_package.py +++ b/cli/tests/integrations/cli/test_package.py @@ -15,12 +15,14 @@ Usage: dcos package --info dcos package describe dcos package info - dcos package install [--options= --app-id= --cli --app] + dcos package install [--cli | [--app --app-id=] dcos package list-installed [--endpoints --app-id= ] dcos package search [] dcos package sources - dcos package uninstall [--all | --app-id=] + dcos package uninstall [--cli | [--app --app-id= --all]] + dcos package update [--validate] Options: @@ -151,11 +153,7 @@ def test_install(): def test_package_metadata(): - stdout = b"""Installing package [helloworld] version [0.1.0] -Installing CLI subcommand for package [helloworld] -""" - assert_command(['dcos', 'package', 'install', 'helloworld'], - stdout=stdout) + _install_helloworld() # test marathon labels expected_metadata = b"""eyJkZXNjcmlwdGlvbiI6ICJFeGFtcGxlIERDT1MgYXBwbGljYX\ @@ -214,7 +212,7 @@ wLjEuMCJdfQ==""" assert six.b(f.read()) == b'0' # uninstall helloworld - assert_command(['dcos', 'package', 'uninstall', 'helloworld']) + _uninstall_helloworld() def test_install_with_id(): @@ -257,18 +255,43 @@ def test_uninstall_missing(): def test_uninstall_subcommand(): - stdout = b"""Installing package [helloworld] version [0.1.0] -Installing CLI subcommand for package [helloworld] -""" - assert_command(['dcos', 'package', 'install', 'helloworld'], - stdout=stdout) - - assert_command(['dcos', 'package', 'uninstall', 'helloworld']) + _install_helloworld() + _uninstall_helloworld() assert_command(['dcos', 'subcommand', 'list'], stdout=b'[]\n') +def test_uninstall_cli(): + _install_helloworld() + _uninstall_helloworld(args=['--cli']) + + stdout = b"""[ + { + "app": { + "appId": "/helloworld" + }, + "description": "Example DCOS application package", + "maintainer": "support@mesosphere.io", + "name": "helloworld", + "packageSource": "git://github.com/mesosphere/universe.git", + "releaseVersion": "0", + "tags": [ + "mesosphere", + "example", + "subcommand" + ], + "version": "0.1.0", + "website": "https://github.com/mesosphere/dcos-helloworld" + } +] +""" + assert_command(['dcos', 'package', 'list-installed'], + stdout=stdout) + + _uninstall_helloworld() + + def test_list_installed(): assert_command(['dcos', 'package', 'list-installed'], stdout=b'[]\n') @@ -327,11 +350,7 @@ further setup requirements: http://mesosphere.github.io/mesos-dns/docs\ def test_list_installed_cli(): - stdout = b"""Installing package [helloworld] version [0.1.0] -Installing CLI subcommand for package [helloworld] -""" - assert_command(['dcos', 'package', 'install', 'helloworld'], - stdout=stdout) + _install_helloworld() stdout = b"""\ [ @@ -360,11 +379,10 @@ Installing CLI subcommand for package [helloworld] assert_command(['dcos', 'package', 'list-installed'], stdout=stdout) - assert_command(['dcos', 'package', 'uninstall', 'helloworld']) + _uninstall_helloworld() stdout = b"Installing CLI subcommand for package [helloworld]\n" - assert_command(['dcos', 'package', 'install', 'helloworld', '--cli'], - stdout=stdout) + _install_helloworld(args=['--cli'], stdout=stdout) stdout = b"""\ [ @@ -390,7 +408,7 @@ Installing CLI subcommand for package [helloworld] assert_command(['dcos', 'package', 'list-installed'], stdout=stdout) - assert_command(['dcos', 'package', 'uninstall', 'helloworld']) + _uninstall_helloworld() def test_search(): @@ -441,6 +459,19 @@ def get_app_labels(app_id): return app_json.get('labels') +def _install_helloworld( + args=[], + stdout=b"""Installing package [helloworld] version [0.1.0] +Installing CLI subcommand for package [helloworld] +"""): + assert_command(['dcos', 'package', 'install', 'helloworld'] + args, + stdout=stdout) + + +def _uninstall_helloworld(args=[]): + assert_command(['dcos', 'package', 'uninstall', 'helloworld'] + args) + + def _uninstall_mesos_dns(args=[], returncode=0, stdout=b'', diff --git a/dcos/api/auth.py b/dcos/api/auth.py index 2e1a71f..f74dfac 100644 --- a/dcos/api/auth.py +++ b/dcos/api/auth.py @@ -4,7 +4,7 @@ import uuid import pkg_resources import toml -from dcos.api import config, constants, emitting, errors, http, jsonitem +from dcos.api import config, constants, emitting, errors, http, jsonitem, util from six import iteritems, moves from oauth2client import client @@ -98,8 +98,7 @@ def check_if_user_authenticated(): :rtype: boolean """ - dcos_config = config.load_from_path( - os.environ[constants.DCOS_CONFIG_ENV]) + dcos_config = util.get_config() return dcos_config.get('core.email', '') != '' diff --git a/dcos/api/marathon.py b/dcos/api/marathon.py index 4f74b7f..26e878b 100644 --- a/dcos/api/marathon.py +++ b/dcos/api/marathon.py @@ -11,7 +11,7 @@ except ImportError: logger = util.get_logger(__name__) -def create_client(config): +def create_client(config=None): """Creates a Marathon client with the supplied configuration. :param config: configuration dictionary @@ -19,6 +19,9 @@ def create_client(config): :returns: Marathon client :rtype: dcos.api.marathon.Client """ + if config is None: + config = util.get_config() + return Client(config['marathon.host'], config['marathon.port']) diff --git a/dcos/api/package.py b/dcos/api/package.py index e7e5b3f..2152cac 100644 --- a/dcos/api/package.py +++ b/dcos/api/package.py @@ -14,7 +14,7 @@ import git import portalocker import pystache import six -from dcos.api import constants, emitting, errors, subcommand, util +from dcos.api import constants, emitting, errors, marathon, subcommand, util from six.moves import urllib @@ -138,7 +138,7 @@ def _base64_encode(dictionary): return base64.b64encode(str_bytes).decode('utf-8') -def uninstall(package_name, remove_all, app_id, init_client): +def uninstall(package_name, remove_all, app_id, cli, app): """Uninstalls a package. :param package_name: The package to uninstall @@ -152,17 +152,32 @@ def uninstall(package_name, remove_all, app_id, init_client): :rtype: None or Error """ - num_apps, err = uninstall_app(package_name, - remove_all, - app_id, - init_client) - if err: - return err + if cli is False and app is False: + cli = app = True - cmd_uninstalled = uninstall_subcommand(package_name) + uninstalled = False + if cli: + if subcommand.uninstall(package_name): + uninstalled = True - if num_apps == 0 and not cmd_uninstalled: - msg = "Package [{}]".format(package_name) + if app: + init_client = marathon.create_client() + + num_apps, err = uninstall_app(package_name, + remove_all, + app_id, + init_client) + + if err is not None: + return err + + if num_apps > 0: + uninstalled = True + + if uninstalled: + return None + else: + msg = 'Package [{}]'.format(package_name) if app_id is not None: msg += " with id [{}]".format(app_id) msg += " is not installed." @@ -178,7 +193,7 @@ def uninstall_subcommand(distribution_name): :rtype: bool """ - return subcommand.uninstall(distribution_name, util.dcos_path()) + return subcommand.uninstall(distribution_name) def uninstall_app(app_name, remove_all, app_id, init_client): diff --git a/dcos/api/subcommand.py b/dcos/api/subcommand.py index 66756ad..dd54a9b 100644 --- a/dcos/api/subcommand.py +++ b/dcos/api/subcommand.py @@ -304,13 +304,11 @@ def package_dir(name): name) -def uninstall(package_name, dcos_path): +def uninstall(package_name): """Uninstall the dcos cli subcommand :param package_name: the name of the package :type package_name: str - :param dcos_path: the path to the dcos cli directory - :type dcos_path: str :returns: True if the subcommand was uninstalled :rtype: bool """ diff --git a/dcos/api/util.py b/dcos/api/util.py index 91b5d95..94752d6 100644 --- a/dcos/api/util.py +++ b/dcos/api/util.py @@ -11,7 +11,7 @@ import tempfile import jsonschema import pystache import six -from dcos.api import constants, errors +from dcos.api import config, constants, errors @contextlib.contextmanager @@ -90,6 +90,16 @@ def read_file(path): 'Unable to open file [{}]'.format(path))) +def get_config(): + """ + :returns: Configuration object + :rtype: Toml + """ + + return config.load_from_path( + os.environ[constants.DCOS_CONFIG_ENV]) + + def which(program): """Returns the path to the named executable program.