Hello community,
here is the log from the commit of package python-marathon for openSUSE:Factory
checked in at 2019-03-27 16:22:59
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-marathon (Old)
and /work/SRC/openSUSE:Factory/.python-marathon.new.25356 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-marathon"
Wed Mar 27 16:22:59 2019 rev:4 rq:689090 version:0.11.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-marathon/python-marathon.changes
2018-12-24 11:39:34.601548754 +0100
+++
/work/SRC/openSUSE:Factory/.python-marathon.new.25356/python-marathon.changes
2019-03-27 16:23:14.839506710 +0100
@@ -1,0 +2,25 @@
+Wed Mar 27 12:00:32 UTC 2019 - [email protected]
+
+- version update to 0.11.0
+ * Added region and zone members to task model. #260
+ * Exception #259
+ * SSE SSL authentication not supported #247
+ * Lack of support for container.networks #243
+ * __init__() got an unexpected keyword argument 'port_mappings' #237
+ * Wrong health check object generated for COMMAND protocol #222
+ * Add support for mesos 1.6 #255
+ * Possibility for send the full json object on create #252
+ * events: add a few attributes #251
+ * install-marathon.sh: do not remove oracle-java7-installer #250
+ * MarathonClient: set verify when using sse_session #248
+ * add reset delay api #246
+ * fixes for issue 244 #245
+ * Test against 1.4.11 #240
+ * fix isuuse-238 #239 (yudong2015)
+ * Test against 1.4.10 instead of 1.4.9 #236
+ * make models.info compatible with 1.4.9 #233
+ * Fix health check 'command' #231
+ * Feature/marathon constraint model improvements #229
+ * Removes id validation from MarathonGroup() #228
+
+-------------------------------------------------------------------
Old:
----
marathon-0.9.0.tar.gz
New:
----
marathon-0.11.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-marathon.spec ++++++
--- /var/tmp/diff_new_pack.WK7uSA/_old 2019-03-27 16:23:15.463506580 +0100
+++ /var/tmp/diff_new_pack.WK7uSA/_new 2019-03-27 16:23:15.467506579 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-marathon
#
-# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-marathon
-Version: 0.9.0
+Version: 0.11.0
Release: 0
Summary: Marathon Client Library
License: MIT
@@ -27,7 +27,8 @@
Source:
https://files.pythonhosted.org/packages/source/m/marathon/marathon-%{version}.tar.gz
BuildRequires: %{python_module setuptools}
BuildRequires: python-rpm-macros
-Requires: python-requests >= 2.0.0
+Requires: python-requests >= 2.4.0
+Requires: python-requests-toolbelt >= 0.4.0
BuildArch: noarch
%python_subpackages
@@ -43,6 +44,9 @@
%install
%python_install
+%check
+# requires Docker and Marathon server installed there
+
%files %{python_files}
%license LICENSE
%{python_sitelib}/*
++++++ marathon-0.9.0.tar.gz -> marathon-0.11.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/PKG-INFO new/marathon-0.11.0/PKG-INFO
--- old/marathon-0.9.0/PKG-INFO 2017-06-21 18:55:35.000000000 +0200
+++ new/marathon-0.11.0/PKG-INFO 2019-01-15 20:14:09.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: marathon
-Version: 0.9.0
+Version: 0.11.0
Summary: Marathon Client Library
Home-page: https://github.com/thefactory/marathon-python
Author: Mike Babineau
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/README.md
new/marathon-0.11.0/README.md
--- old/marathon-0.9.0/README.md 1970-01-01 01:00:00.000000000 +0100
+++ new/marathon-0.11.0/README.md 2019-01-15 20:07:53.000000000 +0100
@@ -0,0 +1,132 @@
+# marathon-python
+
+[](https://travis-ci.org/thefactory/marathon-python)
+
+This is a Python library for interfacing with
[Marathon](https://github.com/mesosphere/marathon) servers via Marathon's [REST
API](https://mesosphere.github.io/marathon/docs/rest-api.html).
+
+#### Compatibility
+
+* For Marathon 1.6.x, use at least 0.10.0
+* For Marathon 1.4.1, use at least 0.8.13
+* For Marathon 1.1.1, use at least 0.8.1
+* For all version changes, please see `CHANGELOG.md`
+
+If you find a feature that is broken, please submit a PR that adds a test for
+it so it will be fixed and will continue to stay fixed as Marathon changes over
+time.
+
+Just because this library is tested against a specific version of Marathon,
+doesn't necessarily mean that it supports every feature and API Marathon
+provides.
+
+## Installation
+
+#### From PyPi (recommended)
+```bash
+pip install marathon
+```
+
+#### From GitHub
+```bash
+pip install -e [email protected]:thefactory/marathon-python.git#egg=marathon
+```
+
+#### From source
+```bash
+git clone [email protected]:thefactory/marathon-python
+python marathon-python/setup.py install
+```
+
+## Testing
+
+`marathon-python` uses Travis to test the code against different versions of
Marathon.
+You can run the tests locally on a Linux machine that has docker on it:
+
+### Running The Tests
+
+```bash
+make itests
+```
+
+### Running The Tests Against a Specific Version of Marathon
+
+```bash
+MARATHONVERSION=1.6.322 make itests
+```
+
+## Documentation
+
+API documentation is [here](http://thefactory.github.io/marathon-python).
+
+Or you can build the documentation yourself:
+```bash
+pip install sphinx
+pip install sphinx_rtd_theme
+cd docs/
+make html
+```
+
+The documentation will be in `<project-root>/gh-pages/html`:
+```bash
+open gh-pages/html/index.html
+```
+
+## Basic Usage
+
+Create a `MarathonClient()` instance pointing at your Marathon server(s):
+```python
+>>> from marathon import MarathonClient
+>>> c = MarathonClient('http://localhost:8080')
+
+>>> # or multiple servers:
+>>> c = MarathonClient(['http://host1:8080', 'http://host2:8080'])
+```
+
+Then try calling some methods:
+```python
+>>> c.list_apps()
+[MarathonApp::myapp1, MarathonApp::myapp2]
+```
+
+```python
+>>> from marathon.models import MarathonApp
+>>> c.create_app('myapp3', MarathonApp(cmd='sleep 100', mem=16, cpus=1))
+MarathonApp::myapp3
+```
+
+```python
+>>> app = c.get_app('myapp3')
+>>> app.ports
+[19671]
+>>> app.mem = 32
+>>> c.update_app('myapp3', app)
+{'deploymentId': '83b215a6-4e26-4e44-9333-5c385eda6438', 'version':
'2014-08-26T07:37:50.462Z'}
+>>> c.get_app('myapp3').mem
+32.0
+```
+
+```python
+>>> c.get_app('myapp3').instances
+1
+>>> c.scale_app('myapp3', instances=3)
+{'deploymentId': '611b89e3-99f2-4d8a-afe1-ec0b83fdbb88', 'version':
'2014-08-26T07:40:20.121Z'}
+>>> c.get_app('myapp3').instances
+3
+>>> c.scale_app('myapp3', delta=-1)
+{'deploymentId': '1081a99c-55e8-4404-907b-4a3697059848', 'version':
'2014-08-26T07:43:30.232Z'}
+>>> c.get_app('myapp3').instances
+2
+```
+
+```python
+>>> c.list_tasks('myapp1')
+[MarathonTask:myapp1-1398201790254]
+>>> c.kill_tasks('myapp1', scale=True)
+[MarathonTask:myapp1-1398201790254]
+>>> c.list_tasks('myapp1')
+[]
+```
+
+## License
+
+Open source under the MIT License. See [LICENSE](LICENSE).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/marathon/__init__.py
new/marathon-0.11.0/marathon/__init__.py
--- old/marathon-0.9.0/marathon/__init__.py 2017-02-22 22:17:07.000000000
+0100
+++ new/marathon-0.11.0/marathon/__init__.py 2018-08-08 19:24:04.000000000
+0200
@@ -1,7 +1,6 @@
-import logging
-
from .client import MarathonClient
from .models import MarathonResource, MarathonApp, MarathonTask,
MarathonConstraint
from .exceptions import MarathonError, MarathonHttpError, NotFoundError,
InvalidChoiceError
+from .util import get_log
-log = logging.getLogger(__name__)
+log = get_log()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/marathon/client.py
new/marathon-0.11.0/marathon/client.py
--- old/marathon-0.9.0/marathon/client.py 2017-06-21 18:48:06.000000000
+0200
+++ new/marathon-0.11.0/marathon/client.py 2019-01-15 20:07:53.000000000
+0100
@@ -8,11 +8,12 @@
import requests
import requests.exceptions
+from requests_toolbelt.adapters import socket_options
import marathon
from .models import MarathonApp, MarathonDeployment, MarathonGroup,
MarathonInfo, MarathonTask, MarathonEndpoint, MarathonQueueItem
-from .exceptions import InternalServerError, NotFoundError, MarathonHttpError,
MarathonError
-from .models.events import EventFactory
+from .exceptions import ConflictError, InternalServerError, NotFoundError,
MarathonHttpError, MarathonError, NoResponseError
+from .models.events import EventFactory, MarathonEvent
from .util import MarathonJsonEncoder, MarathonMinimalJsonEncoder
@@ -21,7 +22,7 @@
"""Client interface for the Marathon REST API."""
def __init__(self, servers, username=None, password=None, timeout=10,
session=None,
- auth_token=None, verify=True):
+ auth_token=None, verify=True, sse_session=None):
"""Create a MarathonClient instance.
If multiple servers are specified, each will be tried in succession
until a non-"Connection Error"-type
@@ -36,11 +37,19 @@
:param int timeout: Timeout (in seconds) for requests to Marathon
:param str auth_token: Token-based auth token, used with DCOS + Oauth
:param bool verify: Enable SSL certificate verification
+ :param requests.session sse_session: requests.session for event stream
connections, which by default enables tcp keepalive
"""
if session is None:
self.session = requests.Session()
else:
self.session = session
+ if sse_session is None:
+ self.sse_session = requests.Session()
+ keep_alive = socket_options.TCPKeepAliveAdapter()
+ self.sse_session.mount('http://', keep_alive)
+ self.sse_session.mount('https://', keep_alive)
+ else:
+ self.sse_session = sse_session
self.servers = servers if isinstance(servers, list) else [servers]
self.auth = (username, password) if username and password else None
self.verify = verify
@@ -87,7 +96,7 @@
'Error while calling %s: %s', url, str(e))
if response is None:
- raise MarathonError('No remaining Marathon servers to try')
+ raise NoResponseError('No remaining Marathon servers to try')
if response.status_code >= 500:
marathon.log.error('Got HTTP {code}: {body}'.format(
@@ -98,6 +107,8 @@
code=response.status_code, body=response.text.encode('utf-8')))
if response.status_code == 404:
raise NotFoundError(response)
+ elif response.status_code == 409:
+ raise ConflictError(response)
else:
raise MarathonHttpError(response)
elif response.status_code >= 300:
@@ -109,19 +120,22 @@
return response
- def _do_sse_request(self, path):
+ def _do_sse_request(self, path, params=None):
"""Query Marathon server for events."""
for server in list(self.servers):
url = ''.join([server.rstrip('/'), path])
try:
- response = requests.get(
+ response = self.sse_session.get(
url,
+ params=params,
stream=True,
headers={'Accept': 'text/event-stream'},
- auth=self.auth
+ auth=self.auth,
+ verify=self.verify,
)
except Exception as e:
- marathon.log.error('Error while calling %s: %s', url,
e.message)
+ marathon.log.error(
+ 'Error while calling %s: %s', url, e.message)
else:
if response.ok:
return response.iter_lines()
@@ -136,17 +150,18 @@
"""
return MarathonEndpoint.from_tasks(self.list_tasks())
- def create_app(self, app_id, app):
+ def create_app(self, app_id, app, minimal=True):
"""Create and start an app.
:param str app_id: application ID
:param :class:`marathon.models.app.MarathonApp` app: the application
to create
+ :param bool minimal: ignore nulls and empty collections
:returns: the created app (on success)
:rtype: :class:`marathon.models.app.MarathonApp` or False
"""
app.id = app_id
- data = app.to_json()
+ data = app.to_json(minimal=minimal)
response = self._do_request('POST', '/v2/apps', data=data)
if response.status_code == 201:
return self._parse_response(response, MarathonApp)
@@ -156,7 +171,7 @@
def list_apps(self, cmd=None, embed_tasks=False, embed_counts=False,
embed_deployments=False, embed_readiness=False,
embed_last_task_failure=False, embed_failures=False,
- embed_task_stats=False, app_id=None, **kwargs):
+ embed_task_stats=False, app_id=None, label=None, **kwargs):
"""List all apps.
:param str cmd: if passed, only show apps with a matching `cmd`
@@ -168,6 +183,7 @@
:param bool embed_failures: shorthand for embed_last_task_failure
:param bool embed_task_stats: embed task stats in result
:param str app_id: if passed, only show apps with an 'id' that matches
or contains this value
+ :param str label: if passed, only show apps with the selected labels
:param kwargs: arbitrary search filters
:returns: list of applications
@@ -178,6 +194,8 @@
params['cmd'] = cmd
if app_id:
params['id'] = app_id
+ if label:
+ params['label'] = label
embed_params = {
'app.tasks': embed_tasks,
@@ -437,7 +455,8 @@
params = {'force': force}
response = self._do_request(
'PUT',
-
'/v2/groups/{group_id}/versions/{version}'.format(group_id=group_id,
version=version),
+ '/v2/groups/{group_id}/versions/{version}'.format(
+ group_id=group_id, version=version),
params=params)
return response.json()
@@ -464,9 +483,9 @@
:returns: a dict containing the deployment id and version
:rtype: dict
"""
- params = {'scaleBy': scale_by}
+ data = {'scaleBy': scale_by}
response = self._do_request(
- 'PUT', '/v2/groups/{group_id}'.format(group_id=group_id),
params=params)
+ 'PUT', '/v2/groups/{group_id}'.format(group_id=group_id),
data=json.dumps(data))
return response.json()
def list_tasks(self, app_id=None, **kwargs):
@@ -658,13 +677,17 @@
response = self._do_request('GET', '/v2/deployments')
return self._parse_response(response, MarathonDeployment, is_list=True)
- def list_queue(self):
+ def list_queue(self, embed_last_unused_offers=False):
"""List all the tasks queued up or waiting to be scheduled.
:returns: list of queue items
:rtype: list[:class:`marathon.models.queue.MarathonQueueItem`]
"""
- response = self._do_request('GET', '/v2/queue')
+ if embed_last_unused_offers:
+ params = {'embed': 'lastUnusedOffers'}
+ else:
+ params = {}
+ response = self._do_request('GET', '/v2/queue', params=params)
return self._parse_response(response, MarathonQueueItem, is_list=True,
resource_name='queue')
def delete_deployment(self, deployment_id, force=False):
@@ -688,6 +711,11 @@
'DELETE',
'/v2/deployments/{deployment}'.format(deployment=deployment_id))
return response.json()
+ def reset_delay(self, app_id):
+ self._do_request(
+ "DELETE", '/v2/queue/{app_id}/delay'.format(app_id=app_id)
+ )
+
def get_info(self):
"""Get server configuration information.
@@ -733,17 +761,27 @@
response = self._do_request('GET', '/metrics')
return response.json()
- def event_stream(self, raw=False):
+ def event_stream(self, raw=False, event_types=None):
"""Polls event bus using /v2/events
:param bool raw: if true, yield raw event text, else yield
MarathonEvent object
+ :param event_types: a list of event types to consume
+ :type event_types: list[type] or list[str]
:returns: iterator with events
:rtype: iterator
"""
ef = EventFactory()
- for raw_message in self._do_sse_request('/v2/events'):
+ params = {
+ 'event_type': [
+ EventFactory.class_to_event[et] if isinstance(
+ et, type) and issubclass(et, MarathonEvent) else et
+ for et in event_types or []
+ ]
+ }
+
+ for raw_message in self._do_sse_request('/v2/events', params=params):
try:
_data = raw_message.decode('utf8').split(':', 1)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/marathon/exceptions.py
new/marathon-0.11.0/marathon/exceptions.py
--- old/marathon-0.9.0/marathon/exceptions.py 2017-03-24 17:33:20.000000000
+0100
+++ new/marathon-0.11.0/marathon/exceptions.py 2019-01-15 20:07:53.000000000
+0100
@@ -32,6 +32,10 @@
pass
+class ConflictError(MarathonHttpError):
+ pass
+
+
class InvalidChoiceError(MarathonError):
def __init__(self, param, value, options):
@@ -40,3 +44,7 @@
param=param, value=value, options=options
)
)
+
+
+class NoResponseError(MarathonError):
+ pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/marathon/models/app.py
new/marathon-0.11.0/marathon/models/app.py
--- old/marathon-0.9.0/marathon/models/app.py 2017-06-21 18:48:06.000000000
+0200
+++ new/marathon-0.11.0/marathon/models/app.py 2018-08-08 19:24:04.000000000
+0200
@@ -6,6 +6,9 @@
from .container import MarathonContainer
from .deployment import MarathonDeployment
from .task import MarathonTask
+from ..util import is_stringy, get_log
+
+log = get_log()
class MarathonApp(MarathonResource):
@@ -210,7 +213,21 @@
def __init__(self, command=None, grace_period_seconds=None,
interval_seconds=None, max_consecutive_failures=None,
path=None, port_index=None, protocol=None,
timeout_seconds=None, ignore_http1xx=None, **kwargs):
- self.command = command
+
+ if command is None:
+ self.command = None
+ elif is_stringy(command):
+ self.command = {
+ "value": command
+ }
+ elif type(command) is dict and 'value' in command:
+ log.warn('Deprecated: Using command as dict instead of string is
deprecated')
+ self.command = {
+ "value": command['value']
+ }
+ else:
+ raise ValueError('Invalid command format: {}'.format(command))
+
self.grace_period_seconds = grace_period_seconds
self.interval_seconds = interval_seconds
self.max_consecutive_failures = max_consecutive_failures
@@ -317,7 +334,10 @@
:param str host: mesos slave running the task
"""
- DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ'
+ DATETIME_FORMATS = [
+ '%Y-%m-%dT%H:%M:%S.%fZ',
+ '%Y-%m-%dT%H:%M:%SZ',
+ ]
def __init__(self, last_scaling_at=None, last_config_change_at=None):
self.last_scaling_at = self._to_datetime(last_scaling_at)
@@ -327,7 +347,12 @@
if (timestamp is None or isinstance(timestamp, datetime)):
return timestamp
else:
- return datetime.strptime(timestamp, self.DATETIME_FORMAT)
+ for fmt in self.DATETIME_FORMATS:
+ try:
+ return datetime.strptime(timestamp, fmt)
+ except ValueError:
+ pass
+ raise ValueError('Unrecognized datetime format:
{}'.format(timestamp))
class MarathonTaskStats(MarathonObject):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/marathon/models/base.py
new/marathon-0.11.0/marathon/models/base.py
--- old/marathon-0.9.0/marathon/models/base.py 2017-06-21 18:48:06.000000000
+0200
+++ new/marathon-0.11.0/marathon/models/base.py 2018-08-08 19:24:04.000000000
+0200
@@ -13,7 +13,7 @@
def __eq__(self, other):
try:
return self.__dict__ == other.__dict__
- except:
+ except Exception:
return False
def __hash__(self):
@@ -68,9 +68,15 @@
def __eq__(self, other):
try:
return self.__dict__ == other.__dict__
- except:
+ except Exception:
return False
+ def __hash__(self):
+ # Technically this class shouldn't be hashable because it often
+ # contains mutable fields, but in practice this class is used more
+ # like a record or namedtuple.
+ return hash(self.to_json())
+
def __str__(self):
return "{clazz}::".format(clazz=self.__class__.__name__) +
str(self.__dict__)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/marathon/models/constraint.py
new/marathon-0.11.0/marathon/models/constraint.py
--- old/marathon-0.9.0/marathon/models/constraint.py 2017-04-19
23:55:42.000000000 +0200
+++ new/marathon-0.11.0/marathon/models/constraint.py 2018-08-08
19:24:04.000000000 +0200
@@ -55,3 +55,19 @@
if len(obj) > 2:
(field, operator, value) = obj
return cls(field, operator, value)
+
+ @classmethod
+ def from_string(cls, constraint):
+ """
+ :param str constraint: The string representation of a constraint
+
+ :rtype: :class:`MarathonConstraint`
+ """
+ obj = constraint.split(':')
+ marathon_constraint = cls.from_json(obj)
+
+ if marathon_constraint:
+ return marathon_constraint
+
+ raise ValueError("Invalid string format. "
+ "Expected `field:operator:value`")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/marathon/models/container.py
new/marathon-0.11.0/marathon/models/container.py
--- old/marathon-0.9.0/marathon/models/container.py 2017-02-22
22:17:07.000000000 +0100
+++ new/marathon-0.11.0/marathon/models/container.py 2018-08-08
19:24:04.000000000 +0200
@@ -11,6 +11,8 @@
:param docker: docker field (e.g., {"image": "mygroup/myimage"})'
:type docker: :class:`marathon.models.container.MarathonDockerContainer`
or dict
:param str type:
+ :param port_mappings: New in Marathon v1.5. container.docker.port_mappings
moved here.
+ :type port_mappings:
list[:class:`marathon.models.container.MarathonContainerPortMapping`] or
list[dict]
:param volumes:
:type volumes:
list[:class:`marathon.models.container.MarathonContainerVolume`] or list[dict]
"""
@@ -18,11 +20,20 @@
TYPES = ['DOCKER', 'MESOS']
"""Valid container types"""
- def __init__(self, docker=None, type='DOCKER', volumes=None):
+ def __init__(self, docker=None, type='DOCKER', port_mappings=None,
volumes=None):
if type not in self.TYPES:
raise InvalidChoiceError('type', type, self.TYPES)
self.type = type
+ # Marathon v1.5 moved portMappings from within container.docker object
directly
+ # under the container object
+ if port_mappings:
+ self.port_mappings = [
+ pm if isinstance(
+ pm, MarathonContainerPortMapping) else
MarathonContainerPortMapping().from_json(pm)
+ for pm in (port_mappings or [])
+ ]
+
if docker:
self.docker = docker if isinstance(docker,
MarathonDockerContainer) \
else MarathonDockerContainer().from_json(docker)
@@ -49,10 +60,10 @@
:param bool force_pull_image: Force a docker pull before launching
"""
- NETWORK_MODES = ['BRIDGE', 'HOST', 'NONE']
+ NETWORK_MODES = ['BRIDGE', 'HOST', 'USER', 'NONE']
"""Valid network modes"""
- def __init__(self, image=None, network='HOST', port_mappings=None,
parameters=None, privileged=None,
+ def __init__(self, image=None, network=None, port_mappings=None,
parameters=None, privileged=None,
force_pull_image=None, **kwargs):
self.image = image
if network:
@@ -83,10 +94,10 @@
:param object labels:
"""
- PROTOCOLS = ['tcp', 'udp']
+ PROTOCOLS = ['tcp', 'udp', 'udp,tcp']
"""Valid protocols"""
- def __init__(self, name=None, container_port=None, host_port=0,
service_port=None, protocol='tcp', labels=None):
+ def __init__(self, name=None, container_port=None, host_port=None,
service_port=None, protocol='tcp', labels=None):
self.name = name
self.container_port = container_port
self.host_port = host_port
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/marathon/models/events.py
new/marathon-0.11.0/marathon/models/events.py
--- old/marathon-0.9.0/marathon/models/events.py 2017-06-21
18:48:06.000000000 +0200
+++ new/marathon-0.11.0/marathon/models/events.py 2018-08-08
19:24:04.000000000 +0200
@@ -1,10 +1,13 @@
"""
This module is used to translate Events from Marathon's EventBus system.
-See: https://mesosphere.github.io/marathon/docs/event-bus.html
+See:
+* https://mesosphere.github.io/marathon/docs/event-bus.html
+*
https://github.com/mesosphere/marathon/blob/master/src/main/scala/mesosphere/marathon/core/event/Events.scala
"""
from marathon.models.base import MarathonObject
from marathon.models.app import MarathonHealthCheck
+from marathon.models.task import MarathonIpAddress
from marathon.models.deployment import MarathonDeploymentPlan
from marathon.exceptions import MarathonError
@@ -19,7 +22,11 @@
KNOWN_ATTRIBUTES = []
attribute_name_to_marathon_object = { # Allows embedding of
MarathonObjects inside events.
'health_check': MarathonHealthCheck,
- 'plan': MarathonDeploymentPlan
+ 'plan': MarathonDeploymentPlan,
+ 'ip_address': MarathonIpAddress,
+ }
+ seq_name_to_singular = {
+ 'ip_addresses': 'ip_address',
}
def __init__(self, event_type, timestamp, **kwargs):
@@ -28,13 +35,25 @@
for attribute in self.KNOWN_ATTRIBUTES:
self._set(attribute, kwargs.get(attribute))
+ def __to_marathon_object(self, attribute_name, attribute):
+ if attribute_name in self.attribute_name_to_marathon_object:
+ clazz = self.attribute_name_to_marathon_object[attribute_name]
+ # If this attribute already has a Marathon object instantiate it.
+ attribute = clazz.from_json(attribute)
+ return attribute
+
def _set(self, attribute_name, attribute):
if not attribute:
return
- if attribute_name in self.attribute_name_to_marathon_object:
- clazz = self.attribute_name_to_marathon_object[attribute_name]
- attribute = clazz.from_json(
- attribute) # If this attribute already has a Marathon object
instantiate it.
+ # Special handling for lists...
+ if isinstance(attribute, list):
+ name = self.seq_name_to_singular.get(attribute_name)
+ attribute = [
+ self.__to_marathon_object(name, v)
+ for v in attribute
+ ]
+ else:
+ attribute = self.__to_marathon_object(attribute_name, attribute)
setattr(self, attribute_name, attribute)
@@ -44,7 +63,7 @@
class MarathonStatusUpdateEvent(MarathonEvent):
KNOWN_ATTRIBUTES = [
- 'slave_id', 'task_id', 'task_status', 'app_id', 'host', 'ports',
'version', 'message']
+ 'slave_id', 'task_id', 'task_status', 'app_id', 'host', 'ports',
'version', 'message', 'ip_addresses']
class MarathonFrameworkMessageEvent(MarathonEvent):
@@ -68,11 +87,11 @@
class MarathonFailedHealthCheckEvent(MarathonEvent):
- KNOWN_ATTRIBUTES = ['app_id', 'health_check', 'task_id']
+ KNOWN_ATTRIBUTES = ['app_id', 'health_check', 'task_id', 'instance_id']
class MarathonHealthStatusChangedEvent(MarathonEvent):
- KNOWN_ATTRIBUTES = ['app_id', 'health_check', 'task_id', 'alive']
+ KNOWN_ATTRIBUTES = ['app_id', 'health_check', 'task_id', 'instance_id',
'alive']
class MarathonGroupChangeSuccess(MarathonEvent):
@@ -92,7 +111,7 @@
class MarathonDeploymentInfo(MarathonEvent):
- KNOWN_ATTRIBUTES = ['plan']
+ KNOWN_ATTRIBUTES = ['plan', 'current_step']
class MarathonDeploymentStepSuccess(MarathonEvent):
@@ -112,7 +131,7 @@
class MarathonUnhealthyTaskKillEvent(MarathonEvent):
- KNOWN_ATTRIBUTES = ['app_id', 'task_id', 'version', 'reason']
+ KNOWN_ATTRIBUTES = ['app_id', 'task_id', 'instance_id', 'version',
'reason']
class MarathonAppTerminatedEvent(MarathonEvent):
@@ -127,7 +146,7 @@
KNOWN_ATTRIBUTES = ['instance_id', 'run_spec_id', 'condition']
-class MarathonInstanceHealthChanged(MarathonEvent):
+class MarathonInstanceHealthChangedEvent(MarathonEvent):
KNOWN_ATTRIBUTES = ['instance_id', 'run_spec_id', 'run_spec_version',
'healthy']
@@ -181,12 +200,14 @@
'instance_changed_event': MarathonInstanceChangedEvent,
'unknown_instance_terminated_event': MarathonUnknownInstanceTerminated,
'unhealthy_instance_kill_event': MarathonUnhealthyInstanceKillEvent,
- 'instance_health_changed_event': MarathonInstanceChangedEvent,
+ 'instance_health_changed_event': MarathonInstanceHealthChangedEvent,
'pod_created_event': MarathonPodCreatedEvent,
'pod_updated_event': MarathonPodUpdatedEvent,
'pod_deleted_event': MarathonPodDeletedEvent,
}
+ class_to_event = dict((v, k) for k, v in event_to_class.items())
+
def process(self, event):
event_type = event['eventType']
if event_type in self.event_to_class:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/marathon/models/group.py
new/marathon-0.11.0/marathon/models/group.py
--- old/marathon-0.9.0/marathon/models/group.py 2017-02-22 22:17:07.000000000
+0100
+++ new/marathon-0.11.0/marathon/models/group.py 2018-08-08
19:24:04.000000000 +0200
@@ -1,4 +1,4 @@
-from .base import MarathonResource, assert_valid_id
+from .base import MarathonResource
from .app import MarathonApp
@@ -36,5 +36,5 @@
# p if isinstance(p, MarathonPod) else MarathonPod().from_json(p)
# for p in (pods or [])
# ]
- self.id = assert_valid_id(id)
+ self.id = id
self.version = version
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/marathon/models/info.py
new/marathon-0.11.0/marathon/models/info.py
--- old/marathon-0.9.0/marathon/models/info.py 2017-02-22 22:17:07.000000000
+0100
+++ new/marathon-0.11.0/marathon/models/info.py 2018-08-08 19:24:04.000000000
+0200
@@ -74,13 +74,48 @@
:param int task_launch_timeout:
:param int task_reservation_timeout:
:param int marathon_store_timeout:
+ :param str access_control_allow_origin:
+ :param int decline_offer_duration:
+ :param str default_network_name:
+ :param str env_vars_prefix:
+ :param int launch_token:
+ :param int launch_token_refresh_interval:
+ :param int max_instances_per_offer:
+ :param str mesos_bridge_name:
+ :param int mesos_heartbeat_failure_threshold:
+ :param int mesos_heartbeat_interval:
+ :param int min_revive_offers_interval:
+ :param int offer_matching_timeout:
+ :param int on_elected_prepare_timeout:
+ :param bool revive_offers_for_new_apps:
+ :param int revive_offers_repetitions:
+ :param int scale_apps_initial_delay:
+ :param int scale_apps_interval:
+ :param bool store_cache:
+ :param int task_launch_confirm_timeout:
+ :param int task_lost_expunge_initial_delay:
+ :param int task_lost_expunge_interval:
"""
def __init__(self, checkpoint=None, executor=None, failover_timeout=None,
framework_name=None, ha=None,
hostname=None, leader_proxy_connection_timeout_ms=None,
leader_proxy_read_timeout_ms=None,
local_port_min=None, local_port_max=None, master=None,
mesos_leader_ui_url=None, mesos_role=None, mesos_user=None,
webui_url=None, reconciliation_initial_delay=None,
reconciliation_interval=None,
- task_launch_timeout=None, marathon_store_timeout=None,
task_reservation_timeout=None, features=None):
+ task_launch_timeout=None, marathon_store_timeout=None,
task_reservation_timeout=None, features=None,
+ access_control_allow_origin=None, decline_offer_duration=None,
+ default_network_name=None, env_vars_prefix=None,
+ launch_token=None, launch_token_refresh_interval=None,
+ max_instances_per_offer=None, mesos_bridge_name=None,
+ mesos_heartbeat_failure_threshold=None,
+ mesos_heartbeat_interval=None,
min_revive_offers_interval=None,
+ offer_matching_timeout=None, on_elected_prepare_timeout=None,
+ revive_offers_for_new_apps=None,
+ revive_offers_repetitions=None, scale_apps_initial_delay=None,
+ scale_apps_interval=None, store_cache=None,
+ task_launch_confirm_timeout=None,
+ task_lost_expunge_initial_delay=None,
+ task_lost_expunge_interval=None
+ ):
self.checkpoint = checkpoint
self.executor = executor
self.failover_timeout = failover_timeout
@@ -99,6 +134,27 @@
self.task_launch_timeout = task_launch_timeout
self.task_reservation_timeout = task_reservation_timeout
self.marathon_store_timeout = marathon_store_timeout
+ self.access_control_allow_origin = access_control_allow_origin
+ self.decline_offer_duration = decline_offer_duration
+ self.default_network_name = default_network_name
+ self.env_vars_prefix = env_vars_prefix
+ self.launch_token = launch_token
+ self.launch_token_refresh_interval = launch_token_refresh_interval
+ self.max_instances_per_offer = max_instances_per_offer
+ self.mesos_bridge_name = mesos_bridge_name
+ self.mesos_heartbeat_failure_threshold =
mesos_heartbeat_failure_threshold
+ self.mesos_heartbeat_interval = mesos_heartbeat_interval
+ self.min_revive_offers_interval = min_revive_offers_interval
+ self.offer_matching_timeout = offer_matching_timeout
+ self.on_elected_prepare_timeout = on_elected_prepare_timeout
+ self.revive_offers_for_new_apps = revive_offers_for_new_apps
+ self.revive_offers_repetitions = revive_offers_repetitions
+ self.scale_apps_initial_delay = scale_apps_initial_delay
+ self.scale_apps_interval = scale_apps_interval
+ self.store_cache = store_cache
+ self.task_launch_confirm_timeout = task_launch_confirm_timeout
+ self.task_lost_expunge_initial_delay = task_lost_expunge_initial_delay
+ self.task_lost_expunge_interval = task_lost_expunge_interval
class MarathonZooKeeperConfig(MarathonObject):
@@ -115,16 +171,28 @@
:param str zk_session_timeout:
:param str zk_state:
:param int zk_timeout:
+ :param int zk_connection_timeout:
+ :param bool zk_compression:
+ :param int zk_compression_threshold:
+ :param int zk_max_node_size:
"""
def __init__(self, zk=None, zk_future_timeout=None, zk_hosts=None,
zk_max_versions=None, zk_path=None,
- zk_session_timeout=None, zk_state=None, zk_timeout=None):
+ zk_session_timeout=None, zk_state=None, zk_timeout=None,
zk_connection_timeout=None,
+ zk_compression=None, zk_compression_threshold=None,
+ zk_max_node_size=None):
self.zk = zk
- self.zk_future_timeout = zk_future_timeout
self.zk_hosts = zk_hosts
self.zk_path = zk_path
self.zk_state = zk_state
+ self.zk_max_versions = zk_max_versions
self.zk_timeout = zk_timeout
+ self.zk_connection_timeout = zk_connection_timeout
+ self.zk_future_timeout = zk_future_timeout
+ self.zk_session_timeout = zk_session_timeout
+ self.zk_compression = zk_compression
+ self.zk_compression_threshold = zk_compression_threshold
+ self.zk_max_node_size = zk_max_node_size
class MarathonHttpConfig(MarathonObject):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/marathon/models/queue.py
new/marathon-0.11.0/marathon/models/queue.py
--- old/marathon-0.9.0/marathon/models/queue.py 2017-03-17 18:33:14.000000000
+0100
+++ new/marathon-0.11.0/marathon/models/queue.py 2017-10-16
20:29:15.000000000 +0200
@@ -26,7 +26,7 @@
"""
def __init__(self, app=None, overdue=None, count=None, delay=None,
since=None,
- processed_offers_summary=None):
+ processed_offers_summary=None, last_unused_offers=None):
self.app = app if isinstance(
app, MarathonApp) else MarathonApp().from_json(app)
self.overdue = overdue
@@ -35,6 +35,7 @@
delay, MarathonQueueItemDelay) else
MarathonQueueItemDelay().from_json(delay)
self.since = since
self.processed_offers_summary = processed_offers_summary
+ self.last_unused_offers = last_unused_offers
class MarathonQueueItemDelay(MarathonResource):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/marathon/models/task.py
new/marathon-0.11.0/marathon/models/task.py
--- old/marathon-0.9.0/marathon/models/task.py 2017-02-22 22:17:07.000000000
+0100
+++ new/marathon-0.11.0/marathon/models/task.py 2019-01-15 20:07:53.000000000
+0100
@@ -21,12 +21,17 @@
:param started_at: when this task was started
:type started_at: datetime or str
:param str version: app version with which this task was started
+ :type region: str
+ :param region: fault domain region support in DCOS EE
+ :type zone: str
+ :param zone: fault domain zone support in DCOS EE
"""
DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ'
def __init__(self, app_id=None, health_check_results=None, host=None,
id=None, ports=None, service_ports=None,
- slave_id=None, staged_at=None, started_at=None, version=None,
ip_addresses=[], state=None, local_volumes=None):
+ slave_id=None, staged_at=None, started_at=None, version=None,
ip_addresses=[], state=None, local_volumes=None,
+ region=None, zone=None):
self.app_id = app_id
self.health_check_results = health_check_results or []
self.health_check_results = [
@@ -50,6 +55,8 @@
ip_addresses, MarathonIpAddress) else
MarathonIpAddress().from_json(ipaddr)
for ipaddr in (ip_addresses or [])]
self.local_volumes = local_volumes or []
+ self.region = region
+ self.zone = zone
class MarathonIpAddress(MarathonObject):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/marathon/util.py
new/marathon-0.11.0/marathon/util.py
--- old/marathon-0.9.0/marathon/util.py 2017-03-24 17:33:20.000000000 +0100
+++ new/marathon-0.11.0/marathon/util.py 2018-08-08 19:24:04.000000000
+0200
@@ -1,5 +1,6 @@
import collections
import datetime
+import logging
try:
import json
@@ -10,6 +11,10 @@
from ._compat import string_types
+def get_log():
+ return logging.getLogger(__name__.split('.')[0])
+
+
def is_stringy(obj):
return isinstance(obj, string_types)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/marathon.egg-info/PKG-INFO
new/marathon-0.11.0/marathon.egg-info/PKG-INFO
--- old/marathon-0.9.0/marathon.egg-info/PKG-INFO 2017-06-21
18:55:35.000000000 +0200
+++ new/marathon-0.11.0/marathon.egg-info/PKG-INFO 2019-01-15
20:14:09.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: marathon
-Version: 0.9.0
+Version: 0.11.0
Summary: Marathon Client Library
Home-page: https://github.com/thefactory/marathon-python
Author: Mike Babineau
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/marathon.egg-info/SOURCES.txt
new/marathon-0.11.0/marathon.egg-info/SOURCES.txt
--- old/marathon-0.9.0/marathon.egg-info/SOURCES.txt 2017-06-21
18:55:35.000000000 +0200
+++ new/marathon-0.11.0/marathon.egg-info/SOURCES.txt 2019-01-15
20:14:09.000000000 +0100
@@ -1,5 +1,6 @@
LICENSE
MANIFEST.in
+README.md
setup.cfg
setup.py
marathon/__init__.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/marathon.egg-info/requires.txt
new/marathon-0.11.0/marathon.egg-info/requires.txt
--- old/marathon-0.9.0/marathon.egg-info/requires.txt 2017-06-21
18:55:35.000000000 +0200
+++ new/marathon-0.11.0/marathon.egg-info/requires.txt 2019-01-15
20:14:09.000000000 +0100
@@ -1 +1,2 @@
-requests>=2.0.0
+requests>=2.4.0
+requests-toolbelt>=0.4.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/setup.cfg
new/marathon-0.11.0/setup.cfg
--- old/marathon-0.9.0/setup.cfg 2017-06-21 18:55:35.000000000 +0200
+++ new/marathon-0.11.0/setup.cfg 2019-01-15 20:14:09.000000000 +0100
@@ -4,5 +4,4 @@
[egg_info]
tag_build =
tag_date = 0
-tag_svn_revision = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/marathon-0.9.0/setup.py new/marathon-0.11.0/setup.py
--- old/marathon-0.9.0/setup.py 2017-06-21 18:52:13.000000000 +0200
+++ new/marathon-0.11.0/setup.py 2019-01-15 20:10:03.000000000 +0100
@@ -8,12 +8,12 @@
setup(
name='marathon',
- version='0.9.0',
+ version='0.11.0',
description='Marathon Client Library',
long_description="""Python interface to the Mesos Marathon REST API.""",
author='Mike Babineau',
author_email='[email protected]',
- install_requires=['requests>=2.0.0'],
+ install_requires=['requests>=2.4.0', 'requests-toolbelt>=0.4.0'],
url='https://github.com/thefactory/marathon-python',
packages=['marathon', 'marathon.models'],
license='MIT',