Merge pull request #61 from mesosphere/dcos-321-task-show
dcos-321 Implements showing tasks
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import abc
|
||||
import collections
|
||||
import json
|
||||
@@ -60,13 +62,16 @@ def print_handler(event):
|
||||
:type event: str, dict or dcos.api.errors.Error
|
||||
"""
|
||||
|
||||
if isinstance(event, basestring):
|
||||
if event is None:
|
||||
# Do nothing
|
||||
pass
|
||||
elif isinstance(event, basestring):
|
||||
print(event)
|
||||
elif isinstance(event, collections.Mapping) or isinstance(event, list):
|
||||
json.dump(event, sys.stdout, sort_keys=True, indent=2)
|
||||
print('')
|
||||
elif isinstance(event, errors.Error):
|
||||
print(event.error())
|
||||
print(event.error(), file=sys.stderr)
|
||||
else:
|
||||
logger.error(
|
||||
'Unable to print event. Type not supported: %s, %r.',
|
||||
|
||||
@@ -319,8 +319,8 @@ class Client(object):
|
||||
def get_deployment(self, deployment_id):
|
||||
"""Returns a deployment.
|
||||
|
||||
:param deployemnt_id: the id of the application to restart
|
||||
:type deployemnt_id: str
|
||||
:param deployment_id: the deployment id
|
||||
:type deployment_id: str
|
||||
:returns: a deployment
|
||||
:rtype: (dict, Error)
|
||||
"""
|
||||
@@ -343,7 +343,7 @@ class Client(object):
|
||||
def get_deployments(self, app_id=None):
|
||||
"""Returns a list of deployments, optionally limited to an app.
|
||||
|
||||
:param app_id: the id of the application to restart
|
||||
:param app_id: the id of the application
|
||||
:type app_id: str
|
||||
:returns: a list of deployments
|
||||
:rtype: list of dict
|
||||
@@ -422,13 +422,12 @@ class Client(object):
|
||||
:param app_id: the id of the application to restart
|
||||
:type app_id: str
|
||||
:returns: a list of tasks
|
||||
:rtype: list of dict
|
||||
:rtype: (list of dict, dcos.api.errors.Error)
|
||||
"""
|
||||
|
||||
url = self._create_url('v2/tasks')
|
||||
|
||||
response, error = http.get(url, response_to_error=_response_to_error)
|
||||
|
||||
if error is not None:
|
||||
return (None, error)
|
||||
|
||||
@@ -443,6 +442,28 @@ class Client(object):
|
||||
|
||||
return (tasks, None)
|
||||
|
||||
def get_task(self, task_id):
|
||||
"""Returns a task
|
||||
|
||||
:param task_id: the id of the task
|
||||
:type task_id: str
|
||||
:returns: a tasks
|
||||
:rtype: (dict, dcos.api.errors.Error)
|
||||
"""
|
||||
|
||||
url = self._create_url('v2/tasks')
|
||||
|
||||
response, error = http.get(url, response_to_error=_response_to_error)
|
||||
if error is not None:
|
||||
return (None, error)
|
||||
|
||||
task = next(
|
||||
(task for task in response.json()['tasks']
|
||||
if task_id == task['id']),
|
||||
None)
|
||||
|
||||
return (task, None)
|
||||
|
||||
|
||||
def normalize_app_id(app_id):
|
||||
"""Normalizes the application id.
|
||||
|
||||
@@ -14,6 +14,7 @@ Usage:
|
||||
dcos app start [--force] <app-id> [<instances>]
|
||||
dcos app stop [--force] <app-id>
|
||||
dcos app task list [<app-id>]
|
||||
dcos app task show <task-id>
|
||||
dcos app update [--force] <app-id> [<properties>...]
|
||||
dcos app version list [--max-count=<max-count>] <app-id>
|
||||
|
||||
@@ -45,6 +46,7 @@ Positional arguments:
|
||||
<properties> Optional key-value pairs to be included in the
|
||||
command. The separator between the key and value
|
||||
must be the '=' character. E.g. cpus=2.0
|
||||
<task-id> The task id
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
@@ -120,6 +122,11 @@ def _cmds():
|
||||
arg_keys=['<app-id>'],
|
||||
function=_task_list),
|
||||
|
||||
cmds.Command(
|
||||
hierarchy=['task', 'show'],
|
||||
arg_keys=['<task-id>'],
|
||||
function=_task_show),
|
||||
|
||||
cmds.Command(hierarchy=['info'], arg_keys=[], function=_info),
|
||||
|
||||
cmds.Command(
|
||||
@@ -683,6 +690,33 @@ def _task_list(app_id):
|
||||
return 0
|
||||
|
||||
|
||||
def _task_show(task_id):
|
||||
"""
|
||||
:param task_id: the task id
|
||||
:type task_id: str
|
||||
:returns: process status
|
||||
:rtype: int
|
||||
"""
|
||||
|
||||
client = marathon.create_client(
|
||||
config.load_from_path(
|
||||
os.environ[constants.DCOS_CONFIG_ENV]))
|
||||
|
||||
task, err = client.get_task(task_id)
|
||||
if err is not None:
|
||||
emitter.publish(err)
|
||||
return 1
|
||||
|
||||
if task is None:
|
||||
emitter.publish(
|
||||
errors.DefaultError("Task '{}' does not exist".format(task_id)))
|
||||
return 1
|
||||
|
||||
emitter.publish(task)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def _update_from_stdin(app_id, force):
|
||||
"""
|
||||
:param app_id: the id of the application
|
||||
|
||||
@@ -22,6 +22,7 @@ def test_help():
|
||||
dcos app start [--force] <app-id> [<instances>]
|
||||
dcos app stop [--force] <app-id>
|
||||
dcos app task list [<app-id>]
|
||||
dcos app task show <task-id>
|
||||
dcos app update [--force] <app-id> [<properties>...]
|
||||
dcos app version list [--max-count=<max-count>] <app-id>
|
||||
|
||||
@@ -53,6 +54,7 @@ Positional arguments:
|
||||
<properties> Optional key-value pairs to be included in the
|
||||
command. The separator between the key and value
|
||||
must be the '=' character. E.g. cpus=2.0
|
||||
<task-id> The task id
|
||||
"""
|
||||
assert stderr == b''
|
||||
|
||||
@@ -108,8 +110,8 @@ def test_add_bad_json_app():
|
||||
stdin=fd)
|
||||
|
||||
assert returncode == 1
|
||||
assert stdout == b'Error loading JSON.\n'
|
||||
assert stderr == b''
|
||||
assert stdout == b''
|
||||
assert stderr == b'Error loading JSON.\n'
|
||||
|
||||
|
||||
def test_add_existing_app():
|
||||
@@ -164,9 +166,9 @@ def test_show_missing_relative_app_version():
|
||||
['dcos', 'app', 'show', '--app-version=-2', 'zero-instance-app'])
|
||||
|
||||
assert returncode == 1
|
||||
assert (stdout ==
|
||||
assert stdout == b''
|
||||
assert (stderr ==
|
||||
b"Application 'zero-instance-app' only has 2 version(s).\n")
|
||||
assert stderr == b''
|
||||
|
||||
_remove_app('zero-instance-app')
|
||||
|
||||
@@ -182,9 +184,9 @@ def test_show_missing_absolute_app_version():
|
||||
'zero-instance-app'])
|
||||
|
||||
assert returncode == 1
|
||||
assert (stdout ==
|
||||
assert stdout == b''
|
||||
assert (stderr ==
|
||||
b"Error: App '/zero-instance-app' does not exist\n")
|
||||
assert stderr == b''
|
||||
|
||||
_remove_app('zero-instance-app')
|
||||
|
||||
@@ -200,10 +202,10 @@ def test_show_bad_app_version():
|
||||
'zero-instance-app'])
|
||||
|
||||
assert returncode == 1
|
||||
assert (stdout ==
|
||||
assert stdout == b''
|
||||
assert (stderr ==
|
||||
(b'Error: Invalid format: "20:39:32.972Z" is malformed at '
|
||||
b'":39:32.972Z"\n'))
|
||||
assert stderr == b''
|
||||
|
||||
_remove_app('zero-instance-app')
|
||||
|
||||
@@ -218,8 +220,8 @@ def test_show_bad_relative_app_version():
|
||||
['dcos', 'app', 'show', '--app-version=2', 'zero-instance-app'])
|
||||
|
||||
assert returncode == 1
|
||||
assert (stdout == b"Relative versions must be negative: 2\n")
|
||||
assert stderr == b''
|
||||
assert stdout == b''
|
||||
assert (stderr == b"Relative versions must be negative: 2\n")
|
||||
|
||||
_remove_app('zero-instance-app')
|
||||
|
||||
@@ -229,8 +231,8 @@ def test_start_missing_app():
|
||||
['dcos', 'app', 'start', 'missing-id'])
|
||||
|
||||
assert returncode == 1
|
||||
assert stdout == b"Error: App '/missing-id' does not exist\n"
|
||||
assert stderr == b''
|
||||
assert stdout == b''
|
||||
assert stderr == b"Error: App '/missing-id' does not exist\n"
|
||||
|
||||
|
||||
def test_start_app():
|
||||
@@ -259,8 +261,8 @@ def test_stop_missing_app():
|
||||
['dcos', 'app', 'stop', 'missing-id'])
|
||||
|
||||
assert returncode == 1
|
||||
assert stdout == b"Error: App '/missing-id' does not exist\n"
|
||||
assert stderr == b''
|
||||
assert stdout == b''
|
||||
assert stderr == b"Error: App '/missing-id' does not exist\n"
|
||||
|
||||
|
||||
def test_stop_app():
|
||||
@@ -298,8 +300,8 @@ def test_update_missing_app():
|
||||
['dcos', 'app', 'update', 'missing-id'])
|
||||
|
||||
assert returncode == 1
|
||||
assert stdout == b"Error: App '/missing-id' does not exist\n"
|
||||
assert stderr == b''
|
||||
assert stdout == b''
|
||||
assert stderr == b"Error: App '/missing-id' does not exist\n"
|
||||
|
||||
|
||||
def test_update_missing_field():
|
||||
@@ -309,10 +311,10 @@ def test_update_missing_field():
|
||||
['dcos', 'app', 'update', 'zero-instance-app', 'missing="a string"'])
|
||||
|
||||
assert returncode == 1
|
||||
assert stdout.decode('utf-8').startswith(
|
||||
assert stdout == b''
|
||||
assert stderr.decode('utf-8').startswith(
|
||||
"The property 'missing' does not conform to the expected format. "
|
||||
"Possible values are: ")
|
||||
assert stderr == b''
|
||||
|
||||
_remove_app('zero-instance-app')
|
||||
|
||||
@@ -324,10 +326,10 @@ def test_update_bad_type():
|
||||
['dcos', 'app', 'update', 'zero-instance-app', 'cpus="a string"'])
|
||||
|
||||
assert returncode == 1
|
||||
assert stdout.decode('utf-8').startswith(
|
||||
assert stderr.decode('utf-8').startswith(
|
||||
"Unable to parse 'a string' as a float: could not convert string to "
|
||||
"float: ")
|
||||
assert stderr == b''
|
||||
assert stdout == b''
|
||||
|
||||
_remove_app('zero-instance-app')
|
||||
|
||||
@@ -375,8 +377,8 @@ def test_restarting_missing_app():
|
||||
['dcos', 'app', 'restart', 'missing-id'])
|
||||
|
||||
assert returncode == 1
|
||||
assert stdout == b"Error: App '/missing-id' does not exist\n"
|
||||
assert stderr == b''
|
||||
assert stdout == b''
|
||||
assert stderr == b"Error: App '/missing-id' does not exist\n"
|
||||
|
||||
|
||||
def test_restarting_app():
|
||||
@@ -400,8 +402,8 @@ def test_list_version_missing_app():
|
||||
['dcos', 'app', 'version', 'list', 'missing-id'])
|
||||
|
||||
assert returncode == 1
|
||||
assert stdout == b"Error: App '/missing-id' does not exist\n"
|
||||
assert stderr == b''
|
||||
assert stdout == b''
|
||||
assert stderr == b"Error: App '/missing-id' does not exist\n"
|
||||
|
||||
|
||||
def test_list_version_negative_max_count():
|
||||
@@ -409,8 +411,8 @@ def test_list_version_negative_max_count():
|
||||
['dcos', 'app', 'version', 'list', 'missing-id', '--max-count=-1'])
|
||||
|
||||
assert returncode == 1
|
||||
assert stdout == b'Maximum count must be a positive number: -1\n'
|
||||
assert stderr == b''
|
||||
assert stdout == b''
|
||||
assert stderr == b'Maximum count must be a positive number: -1\n'
|
||||
|
||||
|
||||
def test_list_version_app():
|
||||
@@ -468,9 +470,9 @@ def test_rollback_missing_deployment():
|
||||
['dcos', 'app', 'deployment', 'rollback', 'missing-deployment'])
|
||||
|
||||
assert returncode == 1
|
||||
assert (stdout ==
|
||||
assert stdout == b''
|
||||
assert (stderr ==
|
||||
b'Error: DeploymentPlan missing-deployment does not exist\n')
|
||||
assert stderr == b''
|
||||
|
||||
|
||||
def test_rollback_deployment():
|
||||
@@ -557,6 +559,37 @@ def test_list_missing_app_tasks():
|
||||
_remove_app('zero-instance-app')
|
||||
|
||||
|
||||
def test_show_missing_task():
|
||||
returncode, stdout, stderr = exec_command(
|
||||
['dcos', 'app', 'task', 'show', 'missing-id'])
|
||||
|
||||
stderr = stderr.decode('utf-8')
|
||||
|
||||
assert returncode == 1
|
||||
assert stdout == b''
|
||||
assert stderr.startswith("Task '")
|
||||
assert stderr.endswith("' does not exist\n")
|
||||
|
||||
|
||||
def test_show_task():
|
||||
_add_app('tests/data/marathon/zero_instance_sleep.json')
|
||||
_start_app('zero-instance-app', 3)
|
||||
result = _list_deployments(1, 'zero-instance-app')
|
||||
_watch_deployment(result[0]['id'], 60)
|
||||
result = _list_tasks(3, 'zero-instance-app')
|
||||
|
||||
returncode, stdout, stderr = exec_command(
|
||||
['dcos', 'app', 'task', 'show', result[0]['id']])
|
||||
|
||||
result = json.loads(stdout.decode('utf-8'))
|
||||
|
||||
assert returncode == 0
|
||||
assert result['appId'] == '/zero-instance-app'
|
||||
assert stderr == b''
|
||||
|
||||
_remove_app('zero-instance-app')
|
||||
|
||||
|
||||
def _list_apps(app_id=None):
|
||||
returncode, stdout, stderr = exec_command(['dcos', 'app', 'list'])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user