* Added warning message about the planned deprecation for appid * Fixed broken test cases * Updated tests adhering to contributing guidelines
1130 lines
36 KiB
Python
1130 lines
36 KiB
Python
import base64
|
|
import contextlib
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
import pytest
|
|
import six
|
|
|
|
from dcos import config, constants, subcommand
|
|
|
|
from .helpers.common import (assert_command, assert_lines, base64_to_dict,
|
|
delete_zk_node, delete_zk_nodes, exec_command,
|
|
file_json, update_config)
|
|
from .helpers.marathon import watch_all_deployments
|
|
from .helpers.package import (package_install, package_uninstall,
|
|
setup_universe_server, teardown_universe_server,
|
|
UNIVERSE_REPO, UNIVERSE_TEST_REPOS)
|
|
from .helpers.service import get_services, service_shutdown
|
|
from ..common import file_bytes
|
|
|
|
|
|
@pytest.fixture
|
|
def env():
|
|
r = os.environ.copy()
|
|
r.update({constants.PATH_ENV: os.environ[constants.PATH_ENV]})
|
|
|
|
return r
|
|
|
|
|
|
def setup_module(module):
|
|
setup_universe_server()
|
|
|
|
|
|
def teardown_module(module):
|
|
services = get_services()
|
|
for framework in services:
|
|
if framework['name'] == 'chronos':
|
|
service_shutdown(framework['id'])
|
|
|
|
teardown_universe_server()
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def zk_znode(request):
|
|
request.addfinalizer(delete_zk_nodes)
|
|
return request
|
|
|
|
|
|
def test_package():
|
|
with open('dcoscli/data/help/package.txt') as content:
|
|
assert_command(['dcos', 'package', '--help'],
|
|
stdout=content.read().encode('utf-8'))
|
|
|
|
|
|
def test_info():
|
|
info = b"Install and manage DC/OS software packages\n"
|
|
assert_command(['dcos', 'package', '--info'],
|
|
stdout=info)
|
|
|
|
|
|
def test_version():
|
|
assert_command(['dcos', 'package', '--version'],
|
|
stdout=b'dcos-package version SNAPSHOT\n')
|
|
|
|
|
|
def test_repo_list():
|
|
repo_list = bytes(
|
|
(
|
|
"test-universe: {test-universe}\n"
|
|
"helloworld-universe: {helloworld-universe}\n"
|
|
).format(**UNIVERSE_TEST_REPOS),
|
|
'utf-8'
|
|
)
|
|
|
|
assert_command(['dcos', 'package', 'repo', 'list'], stdout=repo_list)
|
|
|
|
# test again, but override the dcos_url with a cosmos_url config
|
|
dcos_url = config.get_config_val("core.dcos_url")
|
|
with update_config('package.cosmos_url', dcos_url):
|
|
assert_command(['dcos', 'package', 'repo', 'list'], stdout=repo_list)
|
|
|
|
|
|
def test_repo_add():
|
|
repo_list = bytes(
|
|
(
|
|
"test-universe: {test-universe}\n"
|
|
"helloworld-universe: {helloworld-universe}\n"
|
|
"Universe: {0}\n"
|
|
).format(UNIVERSE_REPO, **UNIVERSE_TEST_REPOS),
|
|
'utf-8'
|
|
)
|
|
|
|
args = ["Universe", UNIVERSE_REPO]
|
|
_repo_add(args, repo_list)
|
|
|
|
|
|
def test_repo_add_index():
|
|
repo17 = "http://universe.mesosphere.com/repo-1.7"
|
|
repo_list = bytes(
|
|
(
|
|
"test-universe: {test-universe}\n"
|
|
"1.7-universe: {0}\n"
|
|
"helloworld-universe: {helloworld-universe}\n"
|
|
"Universe: {1}\n"
|
|
).format(repo17, UNIVERSE_REPO, **UNIVERSE_TEST_REPOS),
|
|
'utf-8'
|
|
)
|
|
|
|
args = ["1.7-universe", repo17, '--index=1']
|
|
_repo_add(args, repo_list)
|
|
|
|
|
|
def test_repo_remove():
|
|
repo_list = bytes(
|
|
(
|
|
"test-universe: {test-universe}\n"
|
|
"helloworld-universe: {helloworld-universe}\n"
|
|
"Universe: {0}\n"
|
|
).format(UNIVERSE_REPO, **UNIVERSE_TEST_REPOS),
|
|
'utf-8'
|
|
)
|
|
_repo_remove(['1.7-universe'], repo_list)
|
|
|
|
repo_list = bytes(
|
|
(
|
|
"test-universe: {test-universe}\n"
|
|
"helloworld-universe: {helloworld-universe}\n"
|
|
).format(**UNIVERSE_TEST_REPOS),
|
|
'utf-8'
|
|
)
|
|
_repo_remove(['Universe'], repo_list)
|
|
|
|
|
|
def test_repo_remove_multi():
|
|
# Add "Universe" repo so we can test removing it
|
|
repo_list = bytes(
|
|
(
|
|
"test-universe: {test-universe}\n"
|
|
"helloworld-universe: {helloworld-universe}\n"
|
|
"Universe: {0}\n"
|
|
).format(UNIVERSE_REPO, **UNIVERSE_TEST_REPOS),
|
|
'utf-8'
|
|
)
|
|
args = ["Universe", UNIVERSE_REPO]
|
|
_repo_add(args, repo_list)
|
|
|
|
# Add "1.7-universe" repo so we can test removing it
|
|
repo17 = "http://universe.mesosphere.com/repo-1.7"
|
|
repo_list = bytes(
|
|
(
|
|
"test-universe: {test-universe}\n"
|
|
"1.7-universe: {1}\n"
|
|
"helloworld-universe: {helloworld-universe}\n"
|
|
"Universe: {0}\n"
|
|
).format(UNIVERSE_REPO, repo17, **UNIVERSE_TEST_REPOS),
|
|
'utf-8'
|
|
)
|
|
args = ["1.7-universe", repo17, '--index=1']
|
|
_repo_add(args, repo_list)
|
|
|
|
repo_list = bytes(
|
|
(
|
|
"test-universe: {test-universe}\n"
|
|
"helloworld-universe: {helloworld-universe}\n"
|
|
).format(**UNIVERSE_TEST_REPOS),
|
|
'utf-8'
|
|
)
|
|
_repo_remove(['1.7-universe', 'Universe'], repo_list)
|
|
|
|
|
|
def test_repo_empty():
|
|
for name in UNIVERSE_TEST_REPOS.keys():
|
|
assert_command(['dcos', 'package', 'repo', 'remove', name])
|
|
|
|
returncode, stdout, stderr = exec_command(
|
|
['dcos', 'package', 'repo', 'list'])
|
|
stderr_msg = (b"There are currently no repos configured. "
|
|
b"Please use `dcos package repo add` to add a repo\n")
|
|
assert returncode == 1
|
|
assert stdout == b''
|
|
assert stderr == stderr_msg
|
|
|
|
for name, url in UNIVERSE_TEST_REPOS.items():
|
|
assert_command(['dcos', 'package', 'repo', 'add', name, url])
|
|
|
|
|
|
def test_describe_nonexistent():
|
|
assert_command(['dcos', 'package', 'describe', 'xyzzy'],
|
|
stderr=b'Package [xyzzy] not found\n',
|
|
returncode=1)
|
|
|
|
|
|
def test_describe_nonexistent_version():
|
|
stderr = b'Version [a.b.c] of package [marathon] not found\n'
|
|
assert_command(['dcos', 'package', 'describe', 'marathon',
|
|
'--package-version=a.b.c'],
|
|
stderr=stderr,
|
|
returncode=1)
|
|
|
|
|
|
def test_describe_cli():
|
|
stdout = file_json(
|
|
'tests/data/package/json/test_describe_cli_kafka.json')
|
|
assert_command(['dcos', 'package', 'describe', 'kafka', '--cli'],
|
|
stdout=stdout)
|
|
|
|
|
|
def test_describe_app():
|
|
stdout = file_bytes(
|
|
'tests/data/package/json/test_describe_app_marathon.json')
|
|
assert_command(['dcos', 'package', 'describe', 'marathon', '--app'],
|
|
stdout=stdout)
|
|
|
|
|
|
def test_describe_config():
|
|
stdout = file_json(
|
|
'tests/data/package/json/test_describe_marathon_config.json')
|
|
assert_command(['dcos', 'package', 'describe', 'marathon', '--config'],
|
|
stdout=stdout)
|
|
|
|
|
|
def test_describe_render():
|
|
stdout = file_json(
|
|
'tests/data/package/json/test_describe_marathon_app_render.json')
|
|
stdout = json.loads(stdout.decode('utf-8'))
|
|
expected_labels = stdout.pop("labels", None)
|
|
|
|
returncode, stdout_, stderr = exec_command(
|
|
['dcos', 'package', 'describe', 'marathon', '--app', '--render'])
|
|
|
|
stdout_ = json.loads(stdout_.decode('utf-8'))
|
|
actual_labels = stdout_.pop("labels", None)
|
|
|
|
for label, value in expected_labels.items():
|
|
if label == "DCOS_PACKAGE_METADATA":
|
|
# We covert the metadata into a dictionary
|
|
# so that failures in equality are more descriptive
|
|
assert base64_to_dict(value) == \
|
|
base64_to_dict(actual_labels.get(label))
|
|
else:
|
|
assert value == actual_labels.get(label)
|
|
|
|
assert stdout == stdout_
|
|
assert stderr == b''
|
|
assert returncode == 0
|
|
|
|
|
|
def test_describe_package_version():
|
|
stdout = file_json(
|
|
'tests/data/package/json/test_describe_marathon_package_version.json')
|
|
|
|
returncode_, stdout_, stderr_ = exec_command(
|
|
['dcos', 'package', 'describe', 'marathon',
|
|
'--package-version=1.3.10'])
|
|
|
|
assert returncode_ == 0
|
|
output = json.loads(stdout_.decode('utf-8'))
|
|
assert output == json.loads(stdout.decode('utf-8'))
|
|
assert stderr_ == b''
|
|
|
|
|
|
def test_describe_package_version_missing():
|
|
stderr = b'Version [bogus] of package [marathon] not found\n'
|
|
assert_command(
|
|
['dcos', 'package', 'describe', 'marathon', '--package-version=bogus'],
|
|
returncode=1,
|
|
stderr=stderr)
|
|
|
|
|
|
def test_describe_package_versions():
|
|
stdout = file_bytes(
|
|
'tests/data/package/json/test_describe_kafka_package_versions.json')
|
|
assert_command(
|
|
['dcos', 'package', 'describe', 'kafka', '--package-versions'],
|
|
stdout=stdout)
|
|
|
|
|
|
def test_describe_options():
|
|
stdout = file_json(
|
|
'tests/data/package/json/test_describe_app_options.json')
|
|
stdout = json.loads(stdout.decode('utf-8'))
|
|
expected_labels = stdout.pop("labels", None)
|
|
|
|
returncode, stdout_, stderr = exec_command(
|
|
['dcos', 'package', 'describe', '--app', '--options',
|
|
'tests/data/package/marathon.json', 'marathon'])
|
|
|
|
stdout_ = json.loads(stdout_.decode('utf-8'))
|
|
actual_labels = stdout_.pop("labels", None)
|
|
|
|
for label, value in expected_labels.items():
|
|
if label == "DCOS_PACKAGE_METADATA":
|
|
# We covert the metadata into a dictionary
|
|
# so that failures in equality are more descriptive
|
|
assert base64_to_dict(value) == \
|
|
base64_to_dict(actual_labels.get(label))
|
|
else:
|
|
assert value == actual_labels.get(label)
|
|
|
|
assert stdout == stdout_
|
|
assert stderr == b''
|
|
assert returncode == 0
|
|
|
|
|
|
def test_describe_app_cli():
|
|
stdout = file_bytes(
|
|
'tests/data/package/json/test_describe_app_cli.json')
|
|
assert_command(
|
|
['dcos', 'package', 'describe', 'kafka', '--app', '--cli'],
|
|
stdout=stdout)
|
|
|
|
|
|
def test_bad_install():
|
|
args = ['--options=tests/data/package/chronos-bad.json', '--yes']
|
|
stderr = """\
|
|
Please create a JSON file with the appropriate options, and pass the \
|
|
/path/to/file as an --options argument.
|
|
"""
|
|
_install_bad_chronos(args=args,
|
|
stderr=stderr)
|
|
|
|
|
|
def test_bad_install_helloworld_msg():
|
|
stdout = (
|
|
b'By Deploying, you agree to the Terms '
|
|
b'and Conditions https://mesosphere.com/'
|
|
b'catalog-terms-conditions/#community-services\n'
|
|
b'A sample pre-installation message\n'
|
|
b'Installing Marathon app for package [helloworld] version '
|
|
b'[0.1.0] with app id [/foo]\n'
|
|
b'Usage of --app-id is deprecated. Use --options instead and specify '
|
|
b'a file that contains [service.name] property\n'
|
|
b'Installing CLI subcommand for package [helloworld] '
|
|
b'version [0.1.0]\n'
|
|
b'New command available: dcos ' +
|
|
_executable_name(b'helloworld') +
|
|
b'\nA sample post-installation message\n'
|
|
)
|
|
|
|
_install_helloworld(['--yes', '--app-id=/foo'],
|
|
stdout=stdout)
|
|
|
|
stdout2 = (
|
|
b'By Deploying, you agree to the Terms '
|
|
b'and Conditions https://mesosphere.com/'
|
|
b'catalog-terms-conditions/#community-services\n'
|
|
b'A sample pre-installation message\n'
|
|
b'Installing Marathon app for package [helloworld] version '
|
|
b'[0.1.0] with app id [/foo/bar]\n'
|
|
b'Usage of --app-id is deprecated. Use --options instead and specify '
|
|
b'a file that contains [service.name] property\n'
|
|
)
|
|
|
|
stderr = (b'Object is not valid\n'
|
|
b'Groups and Applications may not have the same '
|
|
b'identifier.\n')
|
|
|
|
_install_helloworld(['--yes', '--app-id=/foo/bar'],
|
|
stdout=stdout2,
|
|
stderr=stderr,
|
|
returncode=1)
|
|
_uninstall_helloworld()
|
|
|
|
|
|
def test_install_missing_options_file():
|
|
"""Test that a missing options file results in the expected stderr
|
|
message."""
|
|
assert_command(
|
|
['dcos', 'package', 'install', 'chronos', '--yes',
|
|
'--options=asdf.json'],
|
|
returncode=1,
|
|
stderr=b"Error opening file [asdf.json]: No such file or directory\n")
|
|
|
|
|
|
def test_install_specific_version():
|
|
stdout = (
|
|
b'By Deploying, you agree to the Terms '
|
|
b'and Conditions https://mesosphere.com/'
|
|
b'catalog-terms-conditions/#certified-services\n'
|
|
b'We recommend a minimum of one node with at least 2 '
|
|
b'CPU\'s and 1GB of RAM available for the Marathon Service.\n'
|
|
b'Installing Marathon app for package [marathon] '
|
|
b'version [0.11.1]\n'
|
|
b'Marathon DCOS Service has been successfully installed!\n\n'
|
|
b'\tDocumentation: https://mesosphere.github.io/marathon\n'
|
|
b'\tIssues: https://github.com/mesosphere/marathon/issues\n\n'
|
|
)
|
|
|
|
uninstall_stderr = (
|
|
b'Uninstalled package [marathon] version [0.11.1]\n'
|
|
b'The Marathon DCOS Service has been uninstalled and will no '
|
|
b'longer run.\nPlease follow the instructions at http://docs.'
|
|
b'mesosphere.com/services/marathon/#uninstall to clean up any '
|
|
b'persisted state\n'
|
|
)
|
|
|
|
with _package(name='marathon',
|
|
args=['--yes', '--package-version=0.11.1'],
|
|
stdout=stdout,
|
|
uninstall_stderr=uninstall_stderr):
|
|
|
|
returncode, stdout, stderr = exec_command(
|
|
['dcos', 'package', 'list', 'marathon', '--json'])
|
|
assert returncode == 0
|
|
assert stderr == b''
|
|
assert json.loads(stdout.decode('utf-8'))[0]['version'] == "0.11.1"
|
|
|
|
|
|
def test_install_bad_package_version():
|
|
stderr = b'Version [a.b.c] of package [cassandra] not found\n'
|
|
assert_command(
|
|
['dcos', 'package', 'install', 'cassandra',
|
|
'--package-version=a.b.c'],
|
|
returncode=1,
|
|
stderr=stderr)
|
|
|
|
|
|
def test_package_metadata():
|
|
_install_helloworld()
|
|
|
|
# test marathon labels
|
|
expected_metadata = {
|
|
'maintainer': 'support@mesosphere.io',
|
|
'framework': False,
|
|
'name': 'helloworld',
|
|
'version': '0.1.0',
|
|
'packagingVersion': '3.0',
|
|
'preInstallNotes': 'A sample pre-installation message',
|
|
'selected': False,
|
|
'website': 'https://github.com/mesosphere/dcos-helloworld',
|
|
'description': 'Example DCOS application package',
|
|
'tags': ['mesosphere', 'example', 'subcommand'],
|
|
'postInstallNotes': 'A sample post-installation message'
|
|
}
|
|
|
|
expected_source = bytes(
|
|
UNIVERSE_TEST_REPOS['helloworld-universe'],
|
|
'utf-8'
|
|
)
|
|
|
|
expected_labels = {
|
|
'DCOS_PACKAGE_NAME': b'helloworld',
|
|
'DCOS_PACKAGE_VERSION': b'0.1.0',
|
|
'DCOS_PACKAGE_SOURCE': expected_source,
|
|
}
|
|
|
|
app_labels = _get_app_labels('helloworld')
|
|
for label, value in expected_labels.items():
|
|
assert value == six.b(app_labels.get(label))
|
|
|
|
assert expected_metadata == base64_to_dict(six.b(
|
|
app_labels.get('DCOS_PACKAGE_METADATA')))
|
|
|
|
# test local package.json
|
|
package = file_json(
|
|
'tests/data/package/json/test_package_metadata.json')
|
|
package = json.loads(package.decode("UTF-8"))
|
|
|
|
helloworld_subcommand = subcommand.InstalledSubcommand("helloworld")
|
|
|
|
# test local package.json
|
|
assert helloworld_subcommand.package_json() == package
|
|
|
|
# uninstall helloworld
|
|
_uninstall_helloworld()
|
|
|
|
|
|
def test_images_in_metadata():
|
|
package_install('cassandra')
|
|
|
|
labels = _get_app_labels('/cassandra')
|
|
dcos_package_metadata = labels.get("DCOS_PACKAGE_METADATA")
|
|
images = json.loads(
|
|
base64.b64decode(dcos_package_metadata).decode('utf-8'))["images"]
|
|
assert images.get("icon-small") is not None
|
|
assert images.get("icon-medium") is not None
|
|
assert images.get("icon-large") is not None
|
|
|
|
# uninstall
|
|
stderr = (
|
|
b'Uninstalled package [cassandra] version [1.0.25-3.0.10]\n'
|
|
b'DC/OS Apache Cassandra service has been uninstalled.\n'
|
|
b'Please follow the instructions at https://docs.mesosphere.com/'
|
|
b'current/usage/service-guides/cassandra/uninstall to remove any '
|
|
b'persistent state if required.\n'
|
|
)
|
|
|
|
package_uninstall('cassandra', stderr=stderr)
|
|
delete_zk_node('dcos-service-cassandra')
|
|
|
|
|
|
def test_install_with_id(zk_znode):
|
|
args = ['--app-id=chronos-1', '--yes']
|
|
stdout = (
|
|
b'Installing Marathon app for package [chronos] version [3.0.1] with '
|
|
b'app id [chronos-1]\n'
|
|
b'Usage of --app-id is deprecated. Use --options instead and specify '
|
|
b'a file that contains [service.name] property\n'
|
|
|
|
)
|
|
|
|
_install_chronos(args=args, stdout=stdout)
|
|
|
|
args = ['--app-id=chronos-2', '--yes']
|
|
stdout = (
|
|
b'Installing Marathon app for package [chronos] version [3.0.1] with '
|
|
b'app id [chronos-2]\n'
|
|
b'Usage of --app-id is deprecated. Use --options instead and specify '
|
|
b'a file that contains [service.name] property\n'
|
|
)
|
|
_install_chronos(args=args, stdout=stdout)
|
|
|
|
|
|
def test_install_missing_package():
|
|
stderr = b'Package [missing-package] not found\n'
|
|
assert_command(['dcos', 'package', 'install', 'missing-package'],
|
|
returncode=1,
|
|
stderr=stderr)
|
|
|
|
|
|
def test_uninstall_with_id(zk_znode):
|
|
_uninstall_chronos(args=['--app-id=chronos-1'])
|
|
|
|
|
|
def test_uninstall_all(zk_znode):
|
|
_uninstall_chronos(args=['--all'])
|
|
|
|
|
|
def test_uninstall_missing():
|
|
stderr = 'Package [chronos] is not installed\n'
|
|
_uninstall_chronos(returncode=1, stderr=stderr)
|
|
|
|
stderr = 'Package [chronos] with id [/chronos-1] is not installed\n'
|
|
_uninstall_chronos(
|
|
args=['--app-id=chronos-1'],
|
|
returncode=1,
|
|
stderr=stderr)
|
|
|
|
|
|
def test_uninstall_subcommand():
|
|
_install_helloworld()
|
|
_uninstall_helloworld()
|
|
_list(args=['--json'], stdout=b'[]\n')
|
|
|
|
|
|
def test_uninstall_cli():
|
|
_install_helloworld()
|
|
_uninstall_cli_helloworld()
|
|
|
|
stdout_json = {
|
|
"apps": [
|
|
"/helloworld"
|
|
],
|
|
"description": "Example DCOS application package",
|
|
"framework": False,
|
|
"maintainer": "support@mesosphere.io",
|
|
"name": "helloworld",
|
|
"packagingVersion": "3.0",
|
|
"postInstallNotes": "A sample post-installation message",
|
|
"preInstallNotes": "A sample pre-installation message",
|
|
"selected": False,
|
|
"tags": [
|
|
"mesosphere",
|
|
"example",
|
|
"subcommand"
|
|
],
|
|
"version": "0.1.0",
|
|
"website": "https://github.com/mesosphere/dcos-helloworld"
|
|
}
|
|
|
|
returncode_, stdout_, stderr_ = exec_command(
|
|
['dcos', 'package', 'list', '--json'])
|
|
assert stderr_ == b''
|
|
assert returncode_ == 0
|
|
output = json.loads(stdout_.decode('utf-8'))[0]
|
|
assert output == stdout_json
|
|
_uninstall_helloworld()
|
|
|
|
|
|
def test_uninstall_multiple_apps():
|
|
stdout = (
|
|
b'By Deploying, you agree to the Terms '
|
|
b'and Conditions https://mesosphere.com/'
|
|
b'catalog-terms-conditions/#community-services\n'
|
|
b'A sample pre-installation message\n'
|
|
b'Installing Marathon app for package [helloworld] version '
|
|
b'[0.1.0] with app id [/helloworld-1]\n'
|
|
b'Usage of --app-id is deprecated. Use --options instead and specify '
|
|
b'a file that contains [service.name] property\n'
|
|
b'A sample post-installation message\n'
|
|
)
|
|
|
|
_install_helloworld(['--yes', '--app-id=/helloworld-1', '--app'],
|
|
stdout=stdout)
|
|
|
|
stdout = (
|
|
b'By Deploying, you agree to the Terms '
|
|
b'and Conditions https://mesosphere.com/'
|
|
b'catalog-terms-conditions/#community-services\n'
|
|
b'A sample pre-installation message\n'
|
|
b'Installing Marathon app for package [helloworld] version '
|
|
b'[0.1.0] with app id [/helloworld-2]\n'
|
|
b'Usage of --app-id is deprecated. Use --options instead and specify '
|
|
b'a file that contains [service.name] property\n'
|
|
b'A sample post-installation message\n'
|
|
)
|
|
|
|
_install_helloworld(['--yes', '--app-id=/helloworld-2', '--app'],
|
|
stdout=stdout)
|
|
|
|
stderr = (b"Multiple apps named [helloworld] are installed: "
|
|
b"[/helloworld-1, /helloworld-2].\n"
|
|
b"Please use --app-id to specify the ID of the app "
|
|
b"to uninstall, or use --all to uninstall all apps.\n")
|
|
returncode = 1
|
|
|
|
_uninstall_helloworld(stderr=stderr,
|
|
returncode=returncode,
|
|
uninstalled=b'')
|
|
|
|
_uninstall_helloworld(args=['--all'], stdout=b'', stderr=b'', returncode=0)
|
|
|
|
watch_all_deployments()
|
|
|
|
|
|
def test_list(zk_znode):
|
|
empty = b'[]\n'
|
|
|
|
_list(args=['--json'], stdout=empty)
|
|
_list(args=['xyzzy', '--json'], stdout=empty)
|
|
_list(args=['--app-id=/xyzzy', '--json'], stdout=empty)
|
|
|
|
with _chronos_package():
|
|
|
|
expected_output = file_json(
|
|
'tests/data/package/json/test_list_chronos.json')
|
|
_list(args=['--json'], stdout=expected_output)
|
|
_list(args=['--json', 'chronos'], stdout=expected_output)
|
|
_list(args=['--json', '--app-id=/chronos'], stdout=expected_output)
|
|
|
|
le_package = 'ceci-nest-pas-une-package'
|
|
_list(args=['--json', le_package], stdout=empty)
|
|
_list(args=['--json', '--app-id=/' + le_package], stdout=empty)
|
|
|
|
|
|
def test_list_table():
|
|
with _helloworld():
|
|
assert_lines(['dcos', 'package', 'list'], 2)
|
|
|
|
|
|
def test_install_yes():
|
|
with open('tests/data/package/assume_yes.txt') as yes_file:
|
|
_install_helloworld(
|
|
args=[],
|
|
stdin=yes_file,
|
|
stdout=(
|
|
b'By Deploying, you agree to the Terms '
|
|
b'and Conditions https://mesosphere.com/'
|
|
b'catalog-terms-conditions/#community-services\n'
|
|
b'A sample pre-installation message\n'
|
|
b'Continue installing? [yes/no] '
|
|
b'Installing Marathon app for package [helloworld] version '
|
|
b'[0.1.0]\n'
|
|
b'Installing CLI subcommand for package [helloworld] '
|
|
b'version [0.1.0]\n'
|
|
b'New command available: dcos ' +
|
|
_executable_name(b'helloworld') +
|
|
b'\nA sample post-installation message\n'
|
|
)
|
|
)
|
|
_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'By Deploying, you agree to the Terms '
|
|
b'and Conditions https://mesosphere.com/'
|
|
b'catalog-terms-conditions/#community-services\n'
|
|
b'A sample pre-installation message\n'
|
|
b'Continue installing? [yes/no] Exiting installation.\n'
|
|
)
|
|
)
|
|
|
|
|
|
def test_list_cli():
|
|
_install_helloworld()
|
|
stdout = file_json(
|
|
'tests/data/package/json/test_list_helloworld.json')
|
|
_list(args=['--json'], stdout=stdout)
|
|
_uninstall_helloworld()
|
|
|
|
stdout = (
|
|
b'By Deploying, you agree to the Terms '
|
|
b'and Conditions https://mesosphere.com/'
|
|
b'catalog-terms-conditions/#community-services\n'
|
|
b"Installing CLI subcommand for package [helloworld] " +
|
|
b"version [0.1.0]\n"
|
|
b"New command available: dcos " +
|
|
_executable_name(b'helloworld') +
|
|
b"\n"
|
|
)
|
|
_install_helloworld(args=['--cli', '--yes'], stdout=stdout)
|
|
|
|
stdout = file_json(
|
|
'tests/data/package/json/test_list_helloworld_cli.json')
|
|
_list(args=['--json'], stdout=stdout)
|
|
|
|
_uninstall_cli_helloworld()
|
|
|
|
|
|
def test_list_cli_only(env):
|
|
helloworld_path = 'tests/data/package/json/test_list_helloworld_cli.json'
|
|
helloworld_json = file_json(helloworld_path)
|
|
|
|
with _helloworld_cli(), \
|
|
update_config('package.cosmos_url', 'http://nohost', env):
|
|
assert_command(
|
|
cmd=['dcos', 'package', 'list', '--json', '--cli'],
|
|
stdout=helloworld_json)
|
|
|
|
assert_command(
|
|
cmd=['dcos', 'package', 'list', '--json', '--cli',
|
|
'--app-id=/helloworld'],
|
|
stdout=b'[]\n')
|
|
|
|
assert_command(
|
|
cmd=['dcos', 'package', 'list', '--json', '--cli', 'helloworld'],
|
|
stdout=helloworld_json)
|
|
|
|
|
|
def test_cli_global():
|
|
helloworld_path = 'tests/data/package/json/test_list_helloworld_cli.json'
|
|
helloworld_json = file_json(helloworld_path)
|
|
|
|
with _helloworld_cli(global_=True):
|
|
assert os.path.exists(subcommand.global_package_dir("helloworld"))
|
|
|
|
assert_command(
|
|
cmd=['dcos', 'package', 'list', '--json', '--cli'],
|
|
stdout=helloworld_json)
|
|
|
|
|
|
def test_uninstall_multiple_frameworknames(zk_znode):
|
|
_install_chronos(
|
|
args=['--yes', '--options=tests/data/package/chronos-1.json'])
|
|
_install_chronos(
|
|
args=['--yes', '--options=tests/data/package/chronos-2.json'])
|
|
|
|
watch_all_deployments()
|
|
|
|
expected_output = file_json(
|
|
'tests/data/package/json/test_list_chronos_two_users.json')
|
|
|
|
_list(args=['--json'], stdout=expected_output)
|
|
_list(args=['--json', 'chronos'], stdout=expected_output)
|
|
_list(args=['--json', '--app-id=/chronos-user-1'], stdout=file_json(
|
|
'tests/data/package/json/test_list_chronos_user_1.json'))
|
|
|
|
_list(args=['--json', '--app-id=/chronos-user-2'], stdout=file_json(
|
|
'tests/data/package/json/test_list_chronos_user_2.json'))
|
|
|
|
_uninstall_chronos(
|
|
args=['--app-id=chronos-user-1'],
|
|
returncode=1,
|
|
stderr='Uninstalled package [chronos] version [3.0.1]\n'
|
|
'Unable to shutdown [chronos] service framework with name '
|
|
'[chronos-user] because there are multiple framework ids '
|
|
'matching this name: ')
|
|
|
|
_uninstall_chronos(
|
|
args=['--app-id=chronos-user-2'],
|
|
returncode=1,
|
|
stderr='Uninstalled package [chronos] version [3.0.1]\n'
|
|
'Unable to shutdown [chronos] service framework with name '
|
|
'[chronos-user] because there are multiple framework ids '
|
|
'matching this name: ')
|
|
|
|
for framework in get_services(args=['--inactive']):
|
|
if framework['name'] == 'chronos-user':
|
|
service_shutdown(framework['id'])
|
|
|
|
|
|
def test_search():
|
|
returncode, stdout, stderr = exec_command(
|
|
['dcos', 'package', 'search', 'cron', '--json'])
|
|
|
|
assert returncode == 0
|
|
assert b'chronos' in stdout
|
|
assert stderr == b''
|
|
|
|
returncode, stdout, stderr = exec_command(
|
|
['dcos', 'package', 'search', 'xyzzy', '--json'])
|
|
|
|
assert returncode == 0
|
|
assert b'"packages": []' in stdout
|
|
assert stderr == b''
|
|
|
|
returncode, stdout, stderr = exec_command(
|
|
['dcos', 'package', 'search', 'xyzzy'])
|
|
|
|
assert returncode == 1
|
|
assert b'' == stdout
|
|
assert stderr == b'No packages found.\n'
|
|
|
|
returncode, stdout, stderr = exec_command(
|
|
['dcos', 'package', 'search', '--json'])
|
|
|
|
registries = json.loads(stdout.decode('utf-8'))
|
|
# assert the number of packages is gte the number at the time
|
|
# this test was written
|
|
assert len(registries['packages']) >= 5
|
|
|
|
assert returncode == 0
|
|
assert stderr == b''
|
|
|
|
|
|
def test_search_table():
|
|
returncode, stdout, stderr = exec_command(
|
|
['dcos', 'package', 'search'])
|
|
|
|
assert returncode == 0
|
|
assert b'chronos' in stdout
|
|
assert len(stdout.decode('utf-8').split('\n')) > 5
|
|
assert stderr == b''
|
|
|
|
|
|
def test_search_ends_with_wildcard():
|
|
returncode, stdout, stderr = exec_command(
|
|
['dcos', 'package', 'search', 'c*', '--json'])
|
|
|
|
assert returncode == 0
|
|
assert b'chronos' in stdout
|
|
assert b'cassandra' in stdout
|
|
assert stderr == b''
|
|
|
|
registries = json.loads(stdout.decode('utf-8'))
|
|
# cosmos matches wildcards in name/description/tags
|
|
# so will find more results (3 instead of 2)
|
|
assert len(registries['packages']) >= 2
|
|
|
|
|
|
def test_search_start_with_wildcard():
|
|
returncode, stdout, stderr = exec_command(
|
|
['dcos', 'package', 'search', '*nos', '--json'])
|
|
|
|
assert returncode == 0
|
|
assert b'chronos' in stdout
|
|
assert stderr == b''
|
|
|
|
registries = json.loads(stdout.decode('utf-8'))
|
|
assert len(registries['packages']) == 1
|
|
|
|
|
|
def test_search_middle_with_wildcard():
|
|
returncode, stdout, stderr = exec_command(
|
|
['dcos', 'package', 'search', 'c*s', '--json'])
|
|
|
|
assert returncode == 0
|
|
assert b'chronos' in stdout
|
|
assert stderr == b''
|
|
|
|
registries = json.loads(stdout.decode('utf-8'))
|
|
assert len(registries['packages']) == 4
|
|
|
|
|
|
def _get_app_labels(app_id):
|
|
returncode, stdout, stderr = exec_command(
|
|
['dcos', 'marathon', 'app', 'show', app_id])
|
|
|
|
assert returncode == 0
|
|
assert stderr == b''
|
|
|
|
app_json = json.loads(stdout.decode('utf-8'))
|
|
return app_json.get('labels')
|
|
|
|
|
|
def _executable_name(name):
|
|
if sys.platform == 'win32':
|
|
return name + b'.exe'
|
|
else:
|
|
return name
|
|
|
|
|
|
def _install_helloworld(
|
|
args=['--yes'],
|
|
stdout=(
|
|
b'By Deploying, you agree to the Terms '
|
|
b'and Conditions https://mesosphere.com/'
|
|
b'catalog-terms-conditions/#community-services\n'
|
|
b'A sample pre-installation message\n'
|
|
b'Installing Marathon app for package [helloworld] '
|
|
b'version [0.1.0]\n'
|
|
b'Installing CLI subcommand for package [helloworld] '
|
|
b'version [0.1.0]\n'
|
|
b'New command available: dcos ' +
|
|
_executable_name(b'helloworld') +
|
|
b'\nA sample post-installation message\n'
|
|
),
|
|
stderr=b'',
|
|
returncode=0,
|
|
stdin=None):
|
|
assert_command(
|
|
['dcos', 'package', 'install', 'helloworld'] + args,
|
|
stdout=stdout,
|
|
returncode=returncode,
|
|
stdin=stdin,
|
|
stderr=stderr)
|
|
|
|
|
|
def _uninstall_helloworld(
|
|
args=[],
|
|
stdout=b'',
|
|
stderr=b'',
|
|
returncode=0,
|
|
uninstalled=b'Uninstalled package [helloworld] version [0.1.0]\n'):
|
|
assert_command(['dcos', 'package', 'uninstall', 'helloworld',
|
|
'--yes'] + args,
|
|
stdout=stdout,
|
|
stderr=uninstalled+stderr,
|
|
returncode=returncode)
|
|
|
|
watch_all_deployments()
|
|
|
|
|
|
def _uninstall_cli_helloworld(
|
|
args=[],
|
|
stdout=b'',
|
|
stderr=b'',
|
|
returncode=0):
|
|
assert_command(['dcos', 'package', 'uninstall', 'helloworld',
|
|
'--cli'] + args,
|
|
stdout=stdout,
|
|
stderr=stderr,
|
|
returncode=returncode)
|
|
|
|
|
|
def _uninstall_chronos(args=[], returncode=0, stdout=b'', stderr=''):
|
|
result_returncode, result_stdout, result_stderr = exec_command(
|
|
['dcos', 'package', 'uninstall', 'chronos', '--yes'] + args)
|
|
|
|
assert result_returncode == returncode
|
|
assert result_stdout == stdout
|
|
assert result_stderr.decode('utf-8').startswith(stderr)
|
|
|
|
|
|
def _install_bad_chronos(
|
|
args=['--yes'],
|
|
stdout=(
|
|
b'By Deploying, you agree to the Terms '
|
|
b'and Conditions https://mesosphere.com/'
|
|
b'catalog-terms-conditions/#community-services\n'
|
|
b'We recommend a minimum of one node with at least 2 '
|
|
b'CPUs and 2.5GiB of RAM available for the Chronos '
|
|
b'Service.\n'
|
|
),
|
|
stderr=''):
|
|
cmd = ['dcos', 'package', 'install', 'chronos'] + args
|
|
returncode_, stdout_, stderr_ = exec_command(cmd)
|
|
assert returncode_ == 1
|
|
assert stderr in stderr_.decode('utf-8')
|
|
assert stdout_ == stdout
|
|
|
|
|
|
def _install_chronos(
|
|
args=['--yes'],
|
|
returncode=0,
|
|
stdout=b'Installing Marathon app for package [chronos] '
|
|
b'version [3.0.1]\n',
|
|
stderr=b'',
|
|
pre_install_notes=(
|
|
b'By Deploying, you agree to the Terms '
|
|
b'and Conditions https://mesosphere.com/'
|
|
b'catalog-terms-conditions/#community-services\n'
|
|
b'We recommend a minimum of one node with at least '
|
|
b'2 CPUs and 2.5GiB of RAM available for the '
|
|
b'Chronos Service.\n'
|
|
),
|
|
post_install_notes=b'Chronos DCOS Service has been successfully '
|
|
b'installed!\n\n'
|
|
b'\tDocumentation: http://mesos.github.io/'
|
|
b'chronos\n'
|
|
b'\tIssues: https://github.com/mesos/chronos/'
|
|
b'issues\n',
|
|
stdin=None):
|
|
|
|
cmd = ['dcos', 'package', 'install', 'chronos'] + args
|
|
assert_command(
|
|
cmd,
|
|
returncode,
|
|
pre_install_notes + stdout + post_install_notes,
|
|
stderr,
|
|
stdin=stdin)
|
|
|
|
|
|
@contextlib.contextmanager
|
|
def _chronos_package(
|
|
args=['--yes'],
|
|
returncode=0,
|
|
stdout=b'Installing Marathon app for package [chronos] '
|
|
b'version [3.0.1]\n',
|
|
stderr=b'',
|
|
pre_install_notes=(
|
|
b'By Deploying, you agree to the Terms '
|
|
b'and Conditions https://mesosphere.com/'
|
|
b'catalog-terms-conditions/#community-services\n'
|
|
b'We recommend a minimum of one node with at least '
|
|
b'2 CPUs and 2.5GiB of RAM available for the '
|
|
b'Chronos Service.\n'
|
|
),
|
|
post_install_notes=b'Chronos DCOS Service has been successfully '
|
|
b'installed!\n\n'
|
|
b'\tDocumentation: http://mesos.github.io/'
|
|
b'chronos\n'
|
|
b'\tIssues: https://github.com/mesos/chronos/'
|
|
b'issues\n',
|
|
stdin=None):
|
|
|
|
_install_chronos(
|
|
args,
|
|
returncode,
|
|
stdout,
|
|
stderr,
|
|
pre_install_notes,
|
|
post_install_notes,
|
|
stdin)
|
|
try:
|
|
yield
|
|
finally:
|
|
_uninstall_chronos()
|
|
delete_zk_node('chronos')
|
|
watch_all_deployments()
|
|
|
|
|
|
def _list(args, stdout):
|
|
assert_command(['dcos', 'package', 'list'] + args, stdout=stdout)
|
|
|
|
|
|
HELLOWORLD_CLI_STDOUT = (
|
|
b'Installing CLI subcommand for package [helloworld] '
|
|
b'version [0.1.0]\n'
|
|
b'New command available: dcos ' +
|
|
_executable_name(b'helloworld') + b'\n'
|
|
)
|
|
|
|
|
|
def _helloworld():
|
|
stdout = (
|
|
b'By Deploying, you agree to the Terms '
|
|
b'and Conditions https://mesosphere.com/'
|
|
b'catalog-terms-conditions/#community-services\n'
|
|
b'A sample pre-installation message\n'
|
|
b'Installing Marathon app for package [helloworld] version '
|
|
b'[0.1.0]\n' + HELLOWORLD_CLI_STDOUT +
|
|
b'A sample post-installation message\n'
|
|
)
|
|
|
|
stderr = b'Uninstalled package [helloworld] version [0.1.0]\n'
|
|
return _package(name='helloworld',
|
|
args=['--yes'],
|
|
stdout=stdout,
|
|
uninstall_stderr=stderr)
|
|
|
|
|
|
def _helloworld_cli(global_=False):
|
|
args = ['--yes', '--cli']
|
|
if global_:
|
|
args += ['--global']
|
|
return _package(name='helloworld',
|
|
args=args,
|
|
stdout=(
|
|
b'By Deploying, you agree to the Terms '
|
|
b'and Conditions https://mesosphere.com/'
|
|
b'catalog-terms-conditions/#community-services\n' +
|
|
HELLOWORLD_CLI_STDOUT
|
|
),
|
|
uninstall_stderr=b'')
|
|
|
|
|
|
@contextlib.contextmanager
|
|
def _package(name,
|
|
args,
|
|
stdout=b'',
|
|
uninstall_stderr=b''):
|
|
"""Context manager that installs a package on entrance, and uninstalls it on
|
|
exit.
|
|
|
|
:param name: package name
|
|
:type name: str
|
|
:param args: extra CLI args
|
|
:type args: [str]
|
|
:param stdout: Expected stdout
|
|
:type stdout: bytes
|
|
:param uninstall_stderr: Expected stderr
|
|
:type uninstall_stderr: bytes
|
|
:rtype: None
|
|
"""
|
|
|
|
command = ['dcos', 'package', 'install', name] + args
|
|
|
|
installed = False
|
|
try:
|
|
returncode_, stdout_, stderr_ = exec_command(command)
|
|
installed = (returncode_ == 0)
|
|
|
|
assert installed
|
|
assert stdout_ == stdout
|
|
|
|
yield
|
|
finally:
|
|
if installed:
|
|
assert_command(
|
|
['dcos', 'package', 'uninstall', name, '--yes'],
|
|
stderr=uninstall_stderr)
|
|
watch_all_deployments()
|
|
|
|
|
|
def _repo_add(args=[], repo_list=[]):
|
|
assert_command(['dcos', 'package', 'repo', 'add'] + args)
|
|
assert_command(['dcos', 'package', 'repo', 'list'], stdout=repo_list)
|
|
|
|
|
|
def _repo_remove(args=[], repo_list=[]):
|
|
assert_command(['dcos', 'package', 'repo', 'remove'] + args)
|
|
assert_command(['dcos', 'package', 'repo', 'list'], stdout=repo_list)
|