From 657989a2991cba380c4503dba6a5101b4fcebb2d Mon Sep 17 00:00:00 2001 From: Michael Gummelt Date: Fri, 28 Aug 2015 11:43:06 -0700 Subject: [PATCH] dcos service --completed --- cli/dcoscli/service/main.py | 30 ++++---- cli/tests/data/help/service.txt | 20 ++++-- cli/tests/fixtures/service.py | 1 + cli/tests/integrations/common.py | 22 ++++-- cli/tests/integrations/test_service.py | 97 ++++++++++++-------------- dcos/mesos.py | 7 +- 6 files changed, 95 insertions(+), 82 deletions(-) diff --git a/cli/dcoscli/service/main.py b/cli/dcoscli/service/main.py index 0e159aa..6b8b24d 100644 --- a/cli/dcoscli/service/main.py +++ b/cli/dcoscli/service/main.py @@ -2,7 +2,7 @@ Usage: dcos service --info - dcos service [--inactive --json] + dcos service [--completed --inactive --json] dcos service log [--follow --lines=N --ssh-config-file=] [] dcos service shutdown @@ -10,22 +10,28 @@ Usage: Options: -h, --help Show this screen - --info Show a short description of this subcommand - - --ssh-config-file= Path to SSH config file. Used to access - marathon logs. - - --follow Print data as the file grows + --completed Show completed services in addition to active + ones. Completed services are those that have + been disconnected from master, and have reached + their failover timeout, or have been explicitly + shutdown via the /shutdown endpoint. --inactive Show inactive services in addition to active ones. Inactive services are those that have been disconnected from master, but haven't yet reached their failover timeout. + --info Show a short description of this subcommand + + --follow Print data as the file grows + --json Print json-formatted services --lines=N Print the last N lines [default: 10] + --ssh-config-file= Path to SSH config file. Used to access + marathon logs. + --version Show version Positional Arguments: @@ -94,7 +100,7 @@ def _cmds(): cmds.Command( hierarchy=['service'], - arg_keys=['--inactive', '--json'], + arg_keys=['--inactive', '--completed', '--json'], function=_service), ] @@ -110,9 +116,7 @@ def _info(): return 0 -# TODO (mgummelt): support listing completed services as well. -# blocked on framework shutdown. -def _service(inactive, is_json): +def _service(inactive, completed, is_json): """List dcos services :param inactive: If True, include completed tasks @@ -124,7 +128,9 @@ def _service(inactive, is_json): :rtype: int """ - services = mesos.get_master().frameworks(inactive=inactive) + services = mesos.get_master().frameworks( + inactive=inactive, + completed=completed) if is_json: emitter.publish([service.dict() for service in services]) diff --git a/cli/tests/data/help/service.txt b/cli/tests/data/help/service.txt index 7551ab2..0b11118 100644 --- a/cli/tests/data/help/service.txt +++ b/cli/tests/data/help/service.txt @@ -2,7 +2,7 @@ Manage DCOS services Usage: dcos service --info - dcos service [--inactive --json] + dcos service [--completed --inactive --json] dcos service log [--follow --lines=N --ssh-config-file=] [] dcos service shutdown @@ -10,22 +10,28 @@ Usage: Options: -h, --help Show this screen - --info Show a short description of this subcommand - - --ssh-config-file= Path to SSH config file. Used to access - marathon logs. - - --follow Print data as the file grows + --completed Show completed services in addition to active + ones. Completed services are those that have + been disconnected from master, and have reached + their failover timeout, or have been explicitly + shutdown via the /shutdown endpoint. --inactive Show inactive services in addition to active ones. Inactive services are those that have been disconnected from master, but haven't yet reached their failover timeout. + --info Show a short description of this subcommand + + --follow Print data as the file grows + --json Print json-formatted services --lines=N Print the last N lines [default: 10] + --ssh-config-file= Path to SSH config file. Used to access + marathon logs. + --version Show version Positional Arguments: diff --git a/cli/tests/fixtures/service.py b/cli/tests/fixtures/service.py index d752df4..2906d72 100644 --- a/cli/tests/fixtures/service.py +++ b/cli/tests/fixtures/service.py @@ -11,6 +11,7 @@ def framework_fixture(): "active": True, "checkpoint": True, "completed_tasks": [], + "executors": [], "failover_timeout": 604800, "hostname": "mesos.vm", "id": "20150502-231327-16842879-5050-3889-0000", diff --git a/cli/tests/integrations/common.py b/cli/tests/integrations/common.py index 1b3d437..0f041db 100644 --- a/cli/tests/integrations/common.py +++ b/cli/tests/integrations/common.py @@ -377,15 +377,23 @@ def delete_zk_nodes(): :rtype: None """ - base_url = os.environ['EXHIBITOR_URL'] - base_path = 'exhibitor/v1/explorer/znode/{}' - for znode in ['universe', 'cassandra-mesos', 'chronos']: - znode_url = urllib.parse.urljoin( - base_url, - base_path.format(znode)) + delete_zk_node(znode) - requests.delete(znode_url) + +def delete_zk_node(znode): + """Delete Zookeeper node + + :param znode: znode to delete + :type znode: str + :rtype: None + """ + + dcos_url = util.get_config_vals(['core.dcos_url'])[0] + znode_url = urllib.parse.urljoin( + dcos_url, + '/exhibitor/exhibitor/v1/explorer/znode/{}'.format(znode)) + requests.delete(znode_url) def assert_lines(cmd, num_lines): diff --git a/cli/tests/integrations/test_service.py b/cli/tests/integrations/test_service.py index 91ec615..838bd0e 100644 --- a/cli/tests/integrations/test_service.py +++ b/cli/tests/integrations/test_service.py @@ -5,19 +5,11 @@ import time import dcos.util as util from dcos.util import create_schema -import pytest - from ..fixtures.service import framework_fixture -from .common import (assert_command, assert_lines, delete_zk_nodes, - exec_command, get_services, package_install, - package_uninstall, service_shutdown, ssh_output, - watch_all_deployments) - - -@pytest.fixture(scope="module") -def zk_znode(request): - request.addfinalizer(delete_zk_nodes) - return request +from .common import (assert_command, assert_lines, delete_zk_node, + delete_zk_nodes, exec_command, get_services, + package_install, package_uninstall, service_shutdown, + ssh_output, watch_all_deployments) def setup_module(module): @@ -30,44 +22,9 @@ def teardown_module(module): def test_help(): - stdout = b"""Manage DCOS services - -Usage: - dcos service --info - dcos service [--inactive --json] - dcos service log [--follow --lines=N --ssh-config-file=] - [] - dcos service shutdown - -Options: - -h, --help Show this screen - - --info Show a short description of this subcommand - - --ssh-config-file= Path to SSH config file. Used to access - marathon logs. - - --follow Print data as the file grows - - --inactive Show inactive services in addition to active - ones. Inactive services are those that have - been disconnected from master, but haven't yet - reached their failover timeout. - - --json Print json-formatted services - - --lines=N Print the last N lines [default: 10] - - --version Show version - -Positional Arguments: - Output this file. [default: stdout] - - The DCOS Service name. - - The DCOS Service ID -""" - assert_command(['dcos', 'service', '--help'], stdout=stdout) + with open('tests/data/help/service.txt') as content: + assert_command(['dcos', 'service', '--help'], + stdout=content.read().encode('utf-8')) def test_info(): @@ -87,7 +44,7 @@ def test_service_table(): assert_lines(['dcos', 'service'], 3) -def test_service_inactive(zk_znode): +def test_service_inactive(): package_install('cassandra', True) # wait long enough for it to register @@ -108,7 +65,8 @@ def test_service_inactive(zk_znode): # assert only marathon and chronos are active get_services(2) - # assert marathon, chronos, and cassandra are listed with --inactive + + # assert marathon, chronos, and cassandra are inactive services = get_services(args=['--inactive']) assert len(services) >= 3 @@ -120,6 +78,38 @@ def test_service_inactive(zk_znode): # assert marathon, chronos are only listed with --inactive get_services(2, ['--inactive']) + delete_zk_node('cassandra-mesos') + + +def test_service_completed(): + package_install('cassandra', True) + + time.sleep(5) + + services = get_services(3) + + # get cassandra's framework ID + cassandra_id = None + for service in services: + if service['name'] == 'cassandra.dcos': + cassandra_id = service['id'] + break + + assert cassandra_id is not None + + assert_command(['dcos', 'marathon', 'group', 'remove', '/cassandra']) + service_shutdown(cassandra_id) + delete_zk_node('cassandra-mesos') + + # assert cassandra is not running + services = get_services(2) + assert not any(service['id'] == cassandra_id for service in services) + + # assert cassandra is completed + services = get_services(args=['--completed']) + assert len(services) >= 3 + assert any(service['id'] == cassandra_id for service in services) + def test_log(): returncode, stdout, stderr = exec_command( @@ -194,7 +184,7 @@ def test_log_lines(): assert_lines(['dcos', 'service', 'log', 'chronos', '--lines=4'], 4) -def test_log_multiple_apps(zk_znode): +def test_log_multiple_apps(): package_install('marathon', True) package_install('marathon', True, ['--options=tests/data/service/marathon-user2.json', @@ -219,6 +209,7 @@ def _get_schema(service): schema = create_schema(service.dict()) schema['required'].remove('reregistered_time') schema['required'].remove('pid') + schema['required'].remove('executors') schema['properties']['offered_resources']['required'].remove('ports') schema['properties']['resources']['required'].remove('ports') schema['properties']['used_resources']['required'].remove('ports') diff --git a/dcos/mesos.py b/dcos/mesos.py index 02c7f2b..86e90e5 100644 --- a/dcos/mesos.py +++ b/dcos/mesos.py @@ -491,10 +491,11 @@ class Master(object): :returns: a list of frameworks """ - keys = ['frameworks'] if completed: - keys.append('completed_frameworks') - for framework in _merge(self.state(), keys): + for framework in self.state()['completed_frameworks']: + yield framework + + for framework in self.state()['frameworks']: if inactive or framework['active']: yield framework