Merge pull request #183 from mesosphere/pre-install-confirmation

Output pre-install notes on package install.
This commit is contained in:
José Armando García Sancio
2015-05-27 14:13:08 -07:00
8 changed files with 110 additions and 40 deletions

View File

@@ -6,8 +6,7 @@ Usage:
dcos package describe [--app --options=<file> --cli] <package_name>
dcos package info
dcos package install [--cli | [--app --app-id=<app_id>]]
[--options=<file>]
<package_name>
[--options=<file> --yes] <package_name>
dcos package list-installed [--endpoints --app-id=<app-id> <package_name>]
dcos package search [<query>]
dcos package sources
@@ -19,6 +18,8 @@ Options:
-h, --help Show this screen
--info Show a short description of this subcommand
--version Show version
--yes Assume "yes" is the answer to all prompts and run
non-interactively
--all Apply the operation to all matching packages
--app Apply the operation only to the package's application
--app-id=<app-id> The application id
@@ -52,6 +53,8 @@ import pkg_resources
from dcos import cmds, emitting, marathon, options, package, subcommand, util
from dcos.errors import DCOSException
from six.moves import input as user_input
logger = util.get_logger(__name__)
emitter = emitting.FlatEmitter()
@@ -100,7 +103,7 @@ def _cmds():
cmds.Command(
hierarchy=['package', 'install'],
arg_keys=['<package_name>', '--options', '--app-id', '--cli',
'--app'],
'--app', '--yes'],
function=_install),
cmds.Command(
@@ -240,7 +243,32 @@ def _user_options(path):
return util.load_json(options_file)
def _install(package_name, options_path, app_id, cli, app):
def _confirm(prompt, yes):
"""
:param prompt: message to display to the terminal
:type prompt: str
:param yes: whether to assume that the user responded with yes
:type yes: bool
:returns: True if the user responded with yes; False otherwise
:rtype: bool
"""
if yes:
return True
else:
while True:
emitter.publish('{} [yes/no]'.format(prompt))
response = user_input().lower()
if response == 'yes' or response == 'y':
return True
elif response == 'no' or response == 'n':
return False
else:
emitter.publish(
"'{}' is not a valid response.".format(response))
def _install(package_name, options_path, app_id, cli, app, yes):
"""Install the specified package.
:param package_name: the package to install
@@ -253,6 +281,8 @@ def _install(package_name, options_path, app_id, cli, app):
:type cli: bool
:param app: indicate if the application should be installed
:type app: bool
:param yes: automatically assume yes to all prompts
:type yes: bool
:returns: process status
:rtype: int
"""
@@ -273,6 +303,13 @@ def _install(package_name, options_path, app_id, cli, app):
# TODO(CD): Make package version to install configurable
pkg_version = pkg.latest_version()
pre_install_notes = pkg.package_json(pkg_version).get('preInstallNotes')
if pre_install_notes:
emitter.publish(pre_install_notes)
if not _confirm('Continue installing?', yes):
emitter.publish('Exiting installation.')
return 0
user_options = _user_options(options_path)
options = pkg.options(pkg_version, user_options)

View File

@@ -1,7 +1,7 @@
[core]
dcos_url = "http://localhost:5080"
dcos_url = "http://172.17.8.101"
email = "test@mail.com"
reporting = false
[package]
cache = "tmp/cache"
sources = [ "git://github.com/mesosphere/universe.git", "https://github.com/mesosphere/universe/archive/master.zip",]
cache = "tmp/cache"

View File

@@ -1,6 +1,6 @@
[core]
reporting = false
email = "test@mail.com"
reporting = false
[package]
cache = "true"
sources = [ "git://github.com/mesosphere/universe.git", "https://github.com/mesosphere/universe/archive/master.zip",]
cache = "true"

View File

@@ -0,0 +1,2 @@
no

View File

@@ -0,0 +1,2 @@
yes

View File

@@ -33,12 +33,13 @@ def exec_command(cmd, env=None, stdin=None):
return (process.returncode, stdout, stderr)
def assert_command(cmd,
returncode=0,
stdout=b'',
stderr=b'',
env=None,
stdin=None):
def assert_command(
cmd,
returncode=0,
stdout=b'',
stderr=b'',
env=None,
stdin=None):
"""Execute CLI command and assert expected behavior.
:param cmd: Program and arguments

View File

@@ -67,7 +67,7 @@ def test_version():
def test_list_property(env):
stdout = b"""core.dcos_url=http://localhost:5080
stdout = b"""core.dcos_url=http://172.17.8.101
core.email=test@mail.com
core.reporting=False
package.cache=tmp/cache
@@ -80,7 +80,7 @@ package.sources=['git://github.com/mesosphere/universe.git', \
def test_get_existing_string_property(env):
_get_value('core.dcos_url', 'http://localhost:5080', env)
_get_value('core.dcos_url', 'http://172.17.8.101', env)
def test_get_existing_boolean_property(env):
@@ -105,9 +105,9 @@ def test_get_top_property(env):
def test_set_existing_string_property(env):
_set_value('core.dcos_url', 'http://localhost:5081', env)
_get_value('core.dcos_url', 'http://localhost:5081', env)
_set_value('core.dcos_url', 'http://localhost:5080', env)
_set_value('core.dcos_url', 'http://172.17.8.101:5081', env)
_get_value('core.dcos_url', 'http://172.17.8.101:5081', env)
_set_value('core.dcos_url', 'http://172.17.8.101', env)
def test_set_existing_boolean_property(env):

View File

@@ -16,8 +16,7 @@ Usage:
dcos package describe [--app --options=<file> --cli] <package_name>
dcos package info
dcos package install [--cli | [--app --app-id=<app_id>]]
[--options=<file>]
<package_name>
[--options=<file> --yes] <package_name>
dcos package list-installed [--endpoints --app-id=<app-id> <package_name>]
dcos package search [<query>]
dcos package sources
@@ -29,6 +28,8 @@ Options:
-h, --help Show this screen
--info Show a short description of this subcommand
--version Show version
--yes Assume "yes" is the answer to all prompts and run
non-interactively
--all Apply the operation to all matching packages
--app Apply the operation only to the package's application
--app-id=<app-id> The application id
@@ -241,7 +242,7 @@ icon-service-marathon-small.png"
def test_bad_install():
args = ['--options=tests/data/package/chronos-bad.json']
args = ['--options=tests/data/package/chronos-bad.json', '--yes']
stderr = b"""Error: False is not of type 'string'
Path: chronos.zk-hosts
Value: false
@@ -328,12 +329,12 @@ CJdfQ=="""
def test_install_with_id():
args = ['--app-id=chronos-1']
args = ['--app-id=chronos-1', '--yes']
stdout = (b"""Installing package [chronos] version [2.3.4] with app """
b"""id [chronos-1]\n""")
_install_chronos(args=args, stdout=stdout)
args = ['--app-id=chronos-2']
args = ['--app-id=chronos-2', '--yes']
stdout = (b"""Installing package [chronos] version [2.3.4] with app """
b"""id [chronos-2]\n""")
_install_chronos(args=args, stdout=stdout)
@@ -440,8 +441,9 @@ service-chronos-small.png"
"packageSource": "git://github.com/mesosphere/universe.git",
"postInstallNotes": "Chronos DCOS Service has been successfully installed!\
\\nWe recommend a minimum of one node with at least 1 CPU and 2GB of RAM \
available for the Chronos Service.\\n\\n\\tDocumentation: https://github.com/\
mesos/chronos\\n\\tIssues: https:/github.com/mesos/chronos/issues",
available for the Chronos Service.\\n\\n\\tDocumentation: \
http://mesos.github.io/chronos\\n\\tIssues: https://github.com/mesos/\
chronos/issues",
"releaseVersion": "0",
"scm": "https://github.com/mesos/chronos.git",
"tags": [
@@ -474,6 +476,22 @@ mesos/chronos\\n\\tIssues: https:/github.com/mesos/chronos/issues",
_uninstall_chronos()
def test_install_yes():
with open('tests/data/package/assume_yes.txt') as yes_file:
_install_helloworld(stdin=yes_file)
_uninstall_helloworld()
def test_install_no():
with open('tests/data/package/assume_no.txt') as no_file:
_install_helloworld(
args=[],
stdin=no_file,
stdout=b'A sample pre-installation message\n'
b'Continue installing? [yes/no]\n'
b'Exiting installation.\n')
def test_list_installed_cli():
_install_helloworld()
@@ -508,9 +526,10 @@ def test_list_installed_cli():
_uninstall_helloworld()
stdout = (b"Installing CLI subcommand for package [helloworld]\n"
stdout = (b"A sample pre-installation message\n"
b"Installing CLI subcommand for package [helloworld]\n"
b"A sample post-installation message\n")
_install_helloworld(args=['--cli'], stdout=stdout)
_install_helloworld(args=['--cli', '--yes'], stdout=stdout)
stdout = b"""\
[
@@ -572,7 +591,7 @@ def test_search():
for registry in registries:
# assert the number of packages is gte the number at the time
# this test was written
assert len(registry['packages']) >= 8
assert len(registry['packages']) >= 7
assert returncode == 0
assert stderr == b''
@@ -590,13 +609,16 @@ def get_app_labels(app_id):
def _install_helloworld(
args=[],
stdout=b"""Installing package [helloworld] version [0.1.0]
Installing CLI subcommand for package [helloworld]
A sample post-installation message
"""):
assert_command(['dcos', 'package', 'install', 'helloworld'] + args,
stdout=stdout)
args=['--yes'],
stdout=b'A sample pre-installation message\n'
b'Installing package [helloworld] version [0.1.0]\n'
b'Installing CLI subcommand for package [helloworld]\n'
b'A sample post-installation message\n',
stdin=None):
assert_command(
['dcos', 'package', 'install', 'helloworld'] + args,
stdout=stdout,
stdin=stdin)
def _uninstall_helloworld(args=[]):
@@ -609,7 +631,7 @@ def _uninstall_chronos(args=[], returncode=0, stdout=b'', stderr=b''):
def _install_chronos(
args=[],
args=['--yes'],
returncode=0,
stdout=b'Installing package [chronos] version [2.3.4]\n',
stderr=b'',
@@ -618,8 +640,14 @@ def _install_chronos(
b'with at least 1 CPU and 2GB of RAM available for '
b'''the Chronos Service.
\tDocumentation: https://github.com/mesos/chronos
\tIssues: https:/github.com/mesos/chronos/issues\n'''):
\tDocumentation: http://mesos.github.io/chronos
\tIssues: https://github.com/mesos/chronos/issues\n''',
stdin=None):
cmd = ['dcos', 'package', 'install', 'chronos'] + args
assert_command(cmd, returncode, stdout + postInstallNotes, stderr)
assert_command(
cmd,
returncode,
stdout + postInstallNotes,
stderr,
stdin=stdin)