Merge pull request #46 from mesosphere/dcos-329-cancel-deployment
dcos-329 implements canceling deployments
This commit is contained in:
@@ -101,7 +101,7 @@ class Client(object):
|
||||
|
||||
logger.info('Getting %r', url)
|
||||
response = requests.get(url)
|
||||
logger.info('Got (%r): %r', response.status_code, response.json())
|
||||
logger.info('Got (%r): %r', response.status_code, response.text)
|
||||
|
||||
if _success(response.status_code):
|
||||
# Looks like Marathon return different JSON for versions
|
||||
@@ -138,7 +138,7 @@ class Client(object):
|
||||
|
||||
logger.info('Getting %r', url)
|
||||
response = requests.get(url)
|
||||
logger.info('Got (%r): %r', response.status_code, response.json())
|
||||
logger.info('Got (%r): %r', response.status_code, response.text)
|
||||
|
||||
if _success(response.status_code):
|
||||
if max_count is None:
|
||||
@@ -159,7 +159,7 @@ class Client(object):
|
||||
|
||||
logger.info('Getting %r', url)
|
||||
response = requests.get(url)
|
||||
logger.info('Got (%r): %r', response.status_code, response.json())
|
||||
logger.info('Got (%r): %r', response.status_code, response.text)
|
||||
|
||||
if _success(response.status_code):
|
||||
apps = response.json()['apps']
|
||||
@@ -186,7 +186,7 @@ class Client(object):
|
||||
|
||||
logger.info('Posting %r to %r', app_json, url)
|
||||
response = requests.post(url, json=app_json)
|
||||
logger.info('Got (%r): %r', response.status_code, response.json())
|
||||
logger.info('Got (%r): %r', response.status_code, response.text)
|
||||
|
||||
if _success(response.status_code):
|
||||
return (response.json(), None)
|
||||
@@ -217,7 +217,7 @@ class Client(object):
|
||||
|
||||
logger.info('Putting %r to %r', payload, url)
|
||||
response = requests.put(url, json=payload)
|
||||
logger.info('Got (%r): %r', response.status_code, response.json())
|
||||
logger.info('Got (%r): %r', response.status_code, response.text)
|
||||
|
||||
if _success(response.status_code):
|
||||
return (response.json().get('deploymentId'), None)
|
||||
@@ -248,7 +248,7 @@ class Client(object):
|
||||
|
||||
logger.info('Putting to %r', url)
|
||||
response = requests.put(url, json={'instances': int(instances)})
|
||||
logger.info('Got (%r): %r', response.status_code, response.json())
|
||||
logger.info('Got (%r): %r', response.status_code, response.text)
|
||||
|
||||
if _success(response.status_code):
|
||||
deployment = response.json()['deploymentId']
|
||||
@@ -320,7 +320,7 @@ class Client(object):
|
||||
|
||||
logger.info('Posting %r', url)
|
||||
response = requests.post(url)
|
||||
logger.info('Got (%r): %r', response.status_code, response.json())
|
||||
logger.info('Got (%r): %r', response.status_code, response.text)
|
||||
|
||||
if _success(response.status_code):
|
||||
return (response.json(), None)
|
||||
@@ -340,7 +340,7 @@ class Client(object):
|
||||
|
||||
logger.info('Getting %r', url)
|
||||
response = requests.get(url)
|
||||
logger.info('Got (%r): %r', response.status_code, response.json())
|
||||
logger.info('Got (%r): %r', response.status_code, response.text)
|
||||
|
||||
if _success(response.status_code):
|
||||
if app_id is not None:
|
||||
@@ -356,6 +356,60 @@ class Client(object):
|
||||
else:
|
||||
return (None, self._response_to_error(response))
|
||||
|
||||
def _cancel_deployment(self, deployment_id, force):
|
||||
"""Cancels an application deployment.
|
||||
|
||||
:param deployment_id: the deployment id
|
||||
:type deployment_id: str
|
||||
:param force: if set to `False`, stop the deployment and
|
||||
create a new rollback deployment to reinstate the
|
||||
previous configuration. If set to `True`, simply stop the
|
||||
deployment.
|
||||
:type force: bool
|
||||
:returns: an error if unable to rollback the deployment; None otherwise
|
||||
:rtype: Error
|
||||
"""
|
||||
|
||||
if not force:
|
||||
params = None
|
||||
else:
|
||||
params = {'force': 'true'}
|
||||
|
||||
url = self._create_url(
|
||||
'v2/deployments/{}'.format(deployment_id),
|
||||
params)
|
||||
|
||||
logger.info('Deleting %r', url)
|
||||
response = requests.delete(url)
|
||||
logger.info('Got (%r): %r', response.status_code, response.text)
|
||||
|
||||
if _success(response.status_code):
|
||||
return None
|
||||
else:
|
||||
return self._response_to_error(response)
|
||||
|
||||
def rollback_deployment(self, deployment_id):
|
||||
"""Rolls back an application deployment.
|
||||
|
||||
:param deployment_id: the deployment id
|
||||
:type deployment_id: str
|
||||
:returns: an error if unable to rollback the deployment; None otherwise
|
||||
:rtype: Error
|
||||
"""
|
||||
|
||||
return self._cancel_deployment(deployment_id, False)
|
||||
|
||||
def stop_deployment(self, deployment_id):
|
||||
"""Stops an application deployment.
|
||||
|
||||
:param deployment_id: the deployment id
|
||||
:type deployment_id: str
|
||||
:returns: an error if unable to stop the deployment; None otherwise
|
||||
:rtype: Error
|
||||
"""
|
||||
|
||||
return self._cancel_deployment(deployment_id, True)
|
||||
|
||||
|
||||
class Error(errors.Error):
|
||||
""" Class for describing errors while talking to the Marathon server.
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
Usage:
|
||||
dcos app add
|
||||
dcos app deployment list [<app-id>]
|
||||
dcos app deployment rollback <deployment-id>
|
||||
dcos app deployment stop <deployment-id>
|
||||
dcos app info
|
||||
dcos app list
|
||||
dcos app remove [--force] <app-id>
|
||||
@@ -31,6 +33,7 @@ Options:
|
||||
|
||||
Positional arguments:
|
||||
<app-id> The application id
|
||||
<deployment-id> The deployment id
|
||||
<instances> The number of instances to start
|
||||
<properties> Optional key-value pairs to be included in the
|
||||
command. The separator between the key and value
|
||||
@@ -79,10 +82,6 @@ def _cmds():
|
||||
"""
|
||||
|
||||
return [
|
||||
cmds.Command(hierarchy=['info'], arg_keys=[], function=_info),
|
||||
|
||||
cmds.Command(hierarchy=['add'], arg_keys=[], function=_add),
|
||||
|
||||
cmds.Command(
|
||||
hierarchy=['version', 'list'],
|
||||
arg_keys=['<app-id>', '--max-count'],
|
||||
@@ -93,6 +92,20 @@ def _cmds():
|
||||
arg_keys=['<app-id>'],
|
||||
function=_deployment_list),
|
||||
|
||||
cmds.Command(
|
||||
hierarchy=['deployment', 'rollback'],
|
||||
arg_keys=['<deployment-id>'],
|
||||
function=_deployment_rollback),
|
||||
|
||||
cmds.Command(
|
||||
hierarchy=['deployment', 'stop'],
|
||||
arg_keys=['<deployment-id>'],
|
||||
function=_deployment_stop),
|
||||
|
||||
cmds.Command(hierarchy=['info'], arg_keys=[], function=_info),
|
||||
|
||||
cmds.Command(hierarchy=['add'], arg_keys=[], function=_add),
|
||||
|
||||
cmds.Command(hierarchy=['list'], arg_keys=[], function=_list),
|
||||
|
||||
cmds.Command(
|
||||
@@ -536,6 +549,46 @@ def _deployment_list(app_id):
|
||||
return 0
|
||||
|
||||
|
||||
def _deployment_stop(deployment_id):
|
||||
"""
|
||||
:param deployment_id: the application id
|
||||
:type deployment_di: str
|
||||
:returns: process status
|
||||
:rtype: int
|
||||
"""
|
||||
|
||||
client = marathon.create_client(
|
||||
config.load_from_path(
|
||||
os.environ[constants.DCOS_CONFIG_ENV]))
|
||||
|
||||
err = client.stop_deployment(deployment_id)
|
||||
if err is not None:
|
||||
emitter.publish(err)
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def _deployment_rollback(deployment_id):
|
||||
"""
|
||||
:param deployment_id: the application id
|
||||
:type deployment_di: str
|
||||
:returns: process status
|
||||
:rtype: int
|
||||
"""
|
||||
|
||||
client = marathon.create_client(
|
||||
config.load_from_path(
|
||||
os.environ[constants.DCOS_CONFIG_ENV]))
|
||||
|
||||
err = client.rollback_deployment(deployment_id)
|
||||
if err is not None:
|
||||
emitter.publish(err)
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def _update_from_stdin(app_id, force):
|
||||
"""
|
||||
:param app_id: the id of the application
|
||||
|
||||
@@ -11,6 +11,8 @@ def test_help():
|
||||
assert stdout == b"""Usage:
|
||||
dcos app add
|
||||
dcos app deployment list [<app-id>]
|
||||
dcos app deployment rollback <deployment-id>
|
||||
dcos app deployment stop <deployment-id>
|
||||
dcos app info
|
||||
dcos app list
|
||||
dcos app remove [--force] <app-id>
|
||||
@@ -40,6 +42,7 @@ Options:
|
||||
|
||||
Positional arguments:
|
||||
<app-id> The application id
|
||||
<deployment-id> The deployment id
|
||||
<instances> The number of instances to start
|
||||
<properties> Optional key-value pairs to be included in the
|
||||
command. The separator between the key and value
|
||||
@@ -416,59 +419,71 @@ def test_list_version_max_count():
|
||||
|
||||
|
||||
def test_list_empty_deployment():
|
||||
returncode, stdout, stderr = exec_command(
|
||||
['dcos', 'app', 'deployment', 'list'])
|
||||
|
||||
assert returncode == 0
|
||||
assert stdout == b'[]\n'
|
||||
assert stderr == b''
|
||||
_list_deployments(0)
|
||||
|
||||
|
||||
def test_list_deployment():
|
||||
_add_app('tests/data/marathon/zero_instance_sleep.json')
|
||||
_start_app('zero-instance-app')
|
||||
|
||||
returncode, stdout, stderr = exec_command(
|
||||
['dcos', 'app', 'deployment', 'list'])
|
||||
|
||||
result = json.loads(stdout.decode('utf-8'))
|
||||
|
||||
assert returncode == 0
|
||||
assert len(result) == 1
|
||||
assert stderr == b''
|
||||
|
||||
_start_app('zero-instance-app', 3)
|
||||
_list_deployments(1)
|
||||
_remove_app('zero-instance-app')
|
||||
|
||||
|
||||
def test_list_deployment_missing_app():
|
||||
_add_app('tests/data/marathon/zero_instance_sleep.json')
|
||||
_start_app('zero-instance-app')
|
||||
|
||||
returncode, stdout, stderr = exec_command(
|
||||
['dcos', 'app', 'deployment', 'list', 'missing-id'])
|
||||
|
||||
result = json.loads(stdout.decode('utf-8'))
|
||||
|
||||
assert returncode == 0
|
||||
assert len(result) == 0
|
||||
assert stderr == b''
|
||||
|
||||
_list_deployments(0, 'missing-id')
|
||||
_remove_app('zero-instance-app')
|
||||
|
||||
|
||||
def test_list_deployment_app():
|
||||
_add_app('tests/data/marathon/zero_instance_sleep.json')
|
||||
_start_app('zero-instance-app')
|
||||
_start_app('zero-instance-app', 3)
|
||||
_list_deployments(1, 'zero-instance-app')
|
||||
_remove_app('zero-instance-app')
|
||||
|
||||
|
||||
def test_rollback_missing_deployment():
|
||||
returncode, stdout, stderr = exec_command(
|
||||
['dcos', 'app', 'deployment', 'rollback', 'missing-deployment'])
|
||||
|
||||
assert returncode == 1
|
||||
assert (stdout ==
|
||||
b'Error: DeploymentPlan missing-deployment does not exist\n')
|
||||
assert stderr == b''
|
||||
|
||||
|
||||
def test_rollback_deployment():
|
||||
_add_app('tests/data/marathon/zero_instance_sleep.json')
|
||||
_start_app('zero-instance-app', 3)
|
||||
result = _list_deployments(1, 'zero-instance-app')
|
||||
|
||||
returncode, stdout, stderr = exec_command(
|
||||
['dcos', 'app', 'deployment', 'list', 'zero-instance-app'])
|
||||
|
||||
result = json.loads(stdout.decode('utf-8'))
|
||||
['dcos', 'app', 'deployment', 'rollback', result[0]['id']])
|
||||
|
||||
assert returncode == 0
|
||||
assert len(result) == 1
|
||||
assert stdout == b''
|
||||
assert stderr == b''
|
||||
|
||||
_list_deployments(0)
|
||||
|
||||
_remove_app('zero-instance-app')
|
||||
|
||||
|
||||
def test_stop_deployment():
|
||||
_add_app('tests/data/marathon/zero_instance_sleep.json')
|
||||
_start_app('zero-instance-app', 3)
|
||||
result = _list_deployments(1, 'zero-instance-app')
|
||||
|
||||
returncode, stdout, stderr = exec_command(
|
||||
['dcos', 'app', 'deployment', 'stop', result[0]['id']])
|
||||
|
||||
assert returncode == 0
|
||||
assert stdout == b''
|
||||
assert stderr == b''
|
||||
|
||||
_list_deployments(0)
|
||||
|
||||
_remove_app('zero-instance-app')
|
||||
|
||||
|
||||
@@ -526,9 +541,12 @@ def _show_app(app_id, version=None):
|
||||
return result
|
||||
|
||||
|
||||
def _start_app(app_id):
|
||||
returncode, stdout, stderr = exec_command(
|
||||
['dcos', 'app', 'start', app_id])
|
||||
def _start_app(app_id, instances=None):
|
||||
cmd = ['dcos', 'app', 'start', app_id]
|
||||
if instances is not None:
|
||||
cmd.append(str(instances))
|
||||
|
||||
returncode, stdout, stderr = exec_command(cmd)
|
||||
|
||||
assert returncode == 0
|
||||
assert stdout.decode().startswith('Created deployment ')
|
||||
@@ -560,4 +578,18 @@ def _list_versions(app_id, expected_count, max_count=None):
|
||||
assert len(result) == expected_count
|
||||
assert stderr == b''
|
||||
|
||||
|
||||
def _list_deployments(expected_count, app_id=None):
|
||||
cmd = ['dcos', 'app', 'deployment', 'list']
|
||||
if app_id is not None:
|
||||
cmd.append(app_id)
|
||||
|
||||
returncode, stdout, stderr = exec_command(cmd)
|
||||
|
||||
result = json.loads(stdout.decode('utf-8'))
|
||||
|
||||
assert returncode == 0
|
||||
assert len(result) == expected_count
|
||||
assert stderr == b''
|
||||
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user