Merge pull request #145 from mesosphere/uninstall-cli

dcos package uninstall --cli
This commit is contained in:
mgummelt
2015-04-30 13:35:02 -07:00
10 changed files with 138 additions and 144 deletions

View File

@@ -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 {

View File

@@ -54,15 +54,13 @@ Positional arguments:
<task-id> 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:

View File

@@ -5,12 +5,14 @@ Usage:
dcos package --info
dcos package describe <package_name>
dcos package info
dcos package install [--options=<file> --app-id=<app_id> --cli --app]
dcos package install [--cli | [--app --app-id=<app_id]]
[--options=<file>]
<package_name>
dcos package list-installed [--endpoints --app-id=<app-id> <package_name>]
dcos package search [<query>]
dcos package sources
dcos package uninstall [--all | --app-id=<app-id>] <package_name>
dcos package uninstall [--cli | [--app --app-id=<app-id> --all]]
<package_name>
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=['<package_name>', '--all', '--app-id'],
arg_keys=['<package_name>', '--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

View File

@@ -161,7 +161,7 @@ def _uninstall(package_name):
:rtype: int
"""
subcommand.uninstall(package_name, util.dcos_path())
subcommand.uninstall(package_name)
return 0

View File

@@ -15,12 +15,14 @@ Usage:
dcos package --info
dcos package describe <package_name>
dcos package info
dcos package install [--options=<file> --app-id=<app_id> --cli --app]
dcos package install [--cli | [--app --app-id=<app_id]]
[--options=<file>]
<package_name>
dcos package list-installed [--endpoints --app-id=<app-id> <package_name>]
dcos package search [<query>]
dcos package sources
dcos package uninstall [--all | --app-id=<app-id>] <package_name>
dcos package uninstall [--cli | [--app --app-id=<app-id> --all]]
<package_name>
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'',

View File

@@ -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', '') != ''

View File

@@ -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'])

View File

@@ -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):

View File

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

View File

@@ -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.