From 08f8b6cf4cf2d707814711e3a3c2d2a10855ff7d Mon Sep 17 00:00:00 2001 From: Diego Date: Tue, 10 Oct 2017 11:57:55 +0100 Subject: [PATCH 01/73] Remove default container.docker.network to allow using MESOS container engine and .networks on newer versions of marathon --- marathon/models/container.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/marathon/models/container.py b/marathon/models/container.py index 586bf40..9cadedd 100644 --- a/marathon/models/container.py +++ b/marathon/models/container.py @@ -63,7 +63,7 @@ class MarathonDockerContainer(MarathonObject): 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: From 2cdc850fc667904b2e4680c2c55459e12ac9a8ae Mon Sep 17 00:00:00 2001 From: Dmitriy Samovskiy Date: Tue, 10 Oct 2017 14:34:01 -0700 Subject: [PATCH 02/73] support more datetime formats in MarathonAppVersionInfo --- marathon/models/app.py | 12 ++++++++++-- tests/test_model_app.py | 10 +++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/marathon/models/app.py b/marathon/models/app.py index 0a8eafc..021d580 100644 --- a/marathon/models/app.py +++ b/marathon/models/app.py @@ -317,7 +317,10 @@ class MarathonAppVersionInfo(MarathonObject): :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 +330,12 @@ def _to_datetime(self, timestamp): 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 --git a/tests/test_model_app.py b/tests/test_model_app.py index adddb4d..345aa8c 100644 --- a/tests/test_model_app.py +++ b/tests/test_model_app.py @@ -1,6 +1,7 @@ # encoding: utf-8 -from marathon.models.app import MarathonApp +from marathon.models.app import MarathonApp, MarathonAppVersionInfo +from datetime import datetime import unittest @@ -24,3 +25,10 @@ def test_add_env_non_empty_dict(self): app.add_env("MY_ENV", "my-value") self.assertDictEqual({"MY_ENV": "my-value", "OTHER_ENV": "other-value"}, app.env) + + def test_version_info_datetime(self): + app_ver_info = MarathonAppVersionInfo() + self.assertEquals(app_ver_info._to_datetime("2017-09-28T00:31:55Z"), datetime(2017, 9, 28, 0, 31, 55)) + self.assertEquals(app_ver_info._to_datetime("2017-09-28T00:31:55.4Z"), datetime(2017, 9, 28, 0, 31, 55, 400000)) + self.assertEquals(app_ver_info._to_datetime("2017-09-28T00:31:55.004Z"), datetime(2017, 9, 28, 0, 31, 55, 4000)) + self.assertEquals(app_ver_info._to_datetime("2017-09-28T00:31:55.00042Z"), datetime(2017, 9, 28, 0, 31, 55, 420)) From 89d076a8f12024edf54fff442b5a8ccb49c2b3fc Mon Sep 17 00:00:00 2001 From: Matthew Bentley Date: Thu, 12 Oct 2017 09:18:05 -0700 Subject: [PATCH 03/73] Fix MarathonQueueItem to know about the possible last_unused_offers arg --- marathon/models/queue.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/marathon/models/queue.py b/marathon/models/queue.py index 33c0228..d53fd70 100644 --- a/marathon/models/queue.py +++ b/marathon/models/queue.py @@ -26,7 +26,7 @@ class MarathonQueueItem(MarathonResource): """ 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 @@ def __init__(self, app=None, overdue=None, count=None, delay=None, since=None, 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): From ee31446d6b1d9610490a846ef434c60992833133 Mon Sep 17 00:00:00 2001 From: Kyle Anderson Date: Mon, 16 Oct 2017 11:35:50 -0700 Subject: [PATCH 04/73] Make travis automatically upload to pypi on new tags --- .travis.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 168f551..b3d19f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,4 +28,14 @@ addons: packages: - libstdc++6-4.7-dev -sudo: required # make it explicit: it was by default only because this repo was set up before 2015 (new forks need it) +sudo: required + +deploy: + - provider: pypi + user: yelplabs + password: + secure: "Wl8GWxsfPy4KoORYH26N3FllvMeWrifzeCbEx2Af4corcBQl43heeiFRRTlUOcSX0TIasER21PUvQ0R0cAgCjfknDb3SOROcRtcSBe16+cMmvwysfxcAx2OcF1UYBPY8e/qOsGge2Zyzx2PAPNEmJoWKbIT3vUJ4WvlLVeGYdJ0=" + on: + tags: true + condition: MARATHONVERSION == "1.4.7" + repo: thefactory/marathon-python From a55293279a77f2c70cdd978c282ee0cdc28852d4 Mon Sep 17 00:00:00 2001 From: Kyle Anderson Date: Mon, 16 Oct 2017 13:25:37 -0700 Subject: [PATCH 05/73] Release 0.9.3 --- CHANGELOG.md | 16 ++++++++++++++++ setup.py | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b979c5..d5c0a40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,26 @@ # Change Log +## [0.9.3](https://github.com/thefactory/marathon-python/tree/0.9.3) (2017-10-16) +[Full Changelog](https://github.com/thefactory/marathon-python/compare/0.9.2...0.9.3) + +**Closed issues:** + +- `list\_queue` doesn't like the `embed\_last\_unused\_offers` option [\#220](https://github.com/thefactory/marathon-python/issues/220) + +**Merged pull requests:** + +- Make travis automatically upload to pypi on new tags [\#223](https://github.com/thefactory/marathon-python/pull/223) ([solarkennedy](https://github.com/solarkennedy)) +- Fix MarathonQueueItem to know about the possible last\_unused\_offers arg [\#221](https://github.com/thefactory/marathon-python/pull/221) ([matthewbentley](https://github.com/matthewbentley)) +- support more datetime formats in MarathonAppVersionInfo [\#219](https://github.com/thefactory/marathon-python/pull/219) ([somic](https://github.com/somic)) +- Remove default container.docker.network [\#218](https://github.com/thefactory/marathon-python/pull/218) ([protetore](https://github.com/protetore)) +- Make MarathonZooKeeperConfig compatible with maraton 1.5 [\#216](https://github.com/thefactory/marathon-python/pull/216) ([fengyehong](https://github.com/fengyehong)) + ## [0.9.2](https://github.com/thefactory/marathon-python/tree/0.9.2) (2017-09-13) [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.9.1...0.9.2) **Closed issues:** +- Failed to import marathon in python3 [\#217](https://github.com/thefactory/marathon-python/issues/217) - No support for "USER" network mode. [\#173](https://github.com/thefactory/marathon-python/issues/173) - YAML support for marathon-cli [\#74](https://github.com/thefactory/marathon-python/issues/74) diff --git a/setup.py b/setup.py index d111518..5f0f2d5 100755 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setup( name='marathon', - version='0.9.2', + version='0.9.3', description='Marathon Client Library', long_description="""Python interface to the Mesos Marathon REST API.""", author='Mike Babineau', From d0c147df0432c19c22bca8c938d86ac337f9609c Mon Sep 17 00:00:00 2001 From: Kyle Anderson Date: Mon, 16 Oct 2017 15:15:08 -0700 Subject: [PATCH 06/73] Release 0.9.3 for real --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b3d19f7..fa43944 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,5 +37,5 @@ deploy: secure: "Wl8GWxsfPy4KoORYH26N3FllvMeWrifzeCbEx2Af4corcBQl43heeiFRRTlUOcSX0TIasER21PUvQ0R0cAgCjfknDb3SOROcRtcSBe16+cMmvwysfxcAx2OcF1UYBPY8e/qOsGge2Zyzx2PAPNEmJoWKbIT3vUJ4WvlLVeGYdJ0=" on: tags: true - condition: MARATHONVERSION == "1.4.7" + condition: $MARATHONVERSION == "1.4.7" repo: thefactory/marathon-python From d178cd41a83aef2e12f3e6a4429c50ebffeeb593 Mon Sep 17 00:00:00 2001 From: Dalton Barreto Date: Thu, 26 Oct 2017 15:08:12 -0200 Subject: [PATCH 07/73] Removes id validation from MarathonGroup() This validation was preventig the use of the root group (`/`), both from `MarathonGroup().from_json()` and `MarathonClient().get_group("/")` Fixes issue #227 --- marathon/models/group.py | 2 +- tests/test_model_group.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/test_model_group.py diff --git a/marathon/models/group.py b/marathon/models/group.py index c7c0339..5c946c6 100644 --- a/marathon/models/group.py +++ b/marathon/models/group.py @@ -36,5 +36,5 @@ def __init__(self, apps=None, dependencies=None, # 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 --git a/tests/test_model_group.py b/tests/test_model_group.py new file mode 100644 index 0000000..98925b1 --- /dev/null +++ b/tests/test_model_group.py @@ -0,0 +1,20 @@ +# encoding: utf-8 + +from marathon.models.group import MarathonGroup +import unittest + + +class MarathonGroupTest(unittest.TestCase): + + def test_from_json_parses_root_group(self): + data = { + "id": "/", + "groups": [ + {"id": "/foo", "apps": []}, + {"id": "/bla", "apps": []}, + ], + "apps": [] + } + group = MarathonGroup().from_json(data) + self.assertEqual("/", group.id) + From 800f90f334f80fb40c1bd7ce77476615609bdcb6 Mon Sep 17 00:00:00 2001 From: Dalton Barreto Date: Thu, 26 Oct 2017 16:23:07 -0200 Subject: [PATCH 08/73] Fixing flake8 errors --- marathon/models/group.py | 2 +- tests/test_model_group.py | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/marathon/models/group.py b/marathon/models/group.py index 5c946c6..bb4f6bc 100644 --- a/marathon/models/group.py +++ b/marathon/models/group.py @@ -1,4 +1,4 @@ -from .base import MarathonResource, assert_valid_id +from .base import MarathonResource from .app import MarathonApp diff --git a/tests/test_model_group.py b/tests/test_model_group.py index 98925b1..fb84c04 100644 --- a/tests/test_model_group.py +++ b/tests/test_model_group.py @@ -7,14 +7,13 @@ class MarathonGroupTest(unittest.TestCase): def test_from_json_parses_root_group(self): - data = { - "id": "/", - "groups": [ - {"id": "/foo", "apps": []}, - {"id": "/bla", "apps": []}, - ], - "apps": [] - } - group = MarathonGroup().from_json(data) - self.assertEqual("/", group.id) - + data = { + "id": "/", + "groups": [ + {"id": "/foo", "apps": []}, + {"id": "/bla", "apps": []}, + ], + "apps": [] + } + group = MarathonGroup().from_json(data) + self.assertEqual("/", group.id) From 015e5014833713bcfd8bea16bd865b41cb0b3954 Mon Sep 17 00:00:00 2001 From: Dalton Barreto Date: Fri, 27 Oct 2017 10:55:36 -0200 Subject: [PATCH 09/73] Fixing E722 flake8 errors E722: do not use bare except --- marathon/models/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/marathon/models/base.py b/marathon/models/base.py index ecc8030..db77076 100644 --- a/marathon/models/base.py +++ b/marathon/models/base.py @@ -13,7 +13,7 @@ def __repr__(self): def __eq__(self, other): try: return self.__dict__ == other.__dict__ - except: + except Exception: return False def __hash__(self): @@ -68,7 +68,7 @@ def __repr__(self): def __eq__(self, other): try: return self.__dict__ == other.__dict__ - except: + except Exception: return False def __hash__(self): From 9043905aa67ce0080d6fdc13144a5990a236669b Mon Sep 17 00:00:00 2001 From: diogommartins Date: Fri, 27 Oct 2017 17:25:07 -0200 Subject: [PATCH 10/73] Adding MarathonConstraint tests --- tests/test_model_constraint.py | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/test_model_constraint.py diff --git a/tests/test_model_constraint.py b/tests/test_model_constraint.py new file mode 100644 index 0000000..4b289e4 --- /dev/null +++ b/tests/test_model_constraint.py @@ -0,0 +1,35 @@ +from marathon.models.app import MarathonConstraint +import unittest + + +class MarathonConstraintTests(unittest.TestCase): + def test_repr_with_value(self): + constraint = MarathonConstraint('a_field', 'OPERATOR', 'a_value') + representation = repr(constraint) + self.assertEqual(representation, + "MarathonConstraint::a_field:OPERATOR:a_value") + + def test_repr_without_value(self): + constraint = MarathonConstraint('a_field', 'OPERATOR') + representation = repr(constraint) + self.assertEqual(representation, + "MarathonConstraint::a_field:OPERATOR") + + def test_json_repr_with_value(self): + constraint = MarathonConstraint('a_field', 'OPERATOR', 'a_value') + json_repr = constraint.json_repr() + self.assertEqual(json_repr, ['a_field', 'OPERATOR', 'a_value']) + + def test_json_repr_without_value(self): + constraint = MarathonConstraint('a_field', 'OPERATOR') + json_repr = constraint.json_repr() + self.assertEqual(json_repr, ['a_field', 'OPERATOR']) + + def test_from_json_with_value(self): + constraint = MarathonConstraint.from_json(['a_field', 'OPERATOR', 'a_value']) + self.assertEqual(constraint, + MarathonConstraint('a_field', 'OPERATOR', 'a_value')) + + def test_from_json_without_value(self): + constraint = MarathonConstraint.from_json(['a_field', 'OPERATOR']) + self.assertEqual(constraint, MarathonConstraint('a_field', 'OPERATOR')) From 262c659967d3c2b6f82d3e470c877a905a5518db Mon Sep 17 00:00:00 2001 From: diogommartins Date: Fri, 27 Oct 2017 17:25:27 -0200 Subject: [PATCH 11/73] Adding MarathonConstraint.from_string classmethod tests --- tests/test_model_constraint.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test_model_constraint.py b/tests/test_model_constraint.py index 4b289e4..a33417a 100644 --- a/tests/test_model_constraint.py +++ b/tests/test_model_constraint.py @@ -33,3 +33,22 @@ def test_from_json_with_value(self): def test_from_json_without_value(self): constraint = MarathonConstraint.from_json(['a_field', 'OPERATOR']) self.assertEqual(constraint, MarathonConstraint('a_field', 'OPERATOR')) + + def test_from_string_with_value(self): + constraint = MarathonConstraint.from_string('a_field:OPERATOR:a_value') + self.assertEqual(constraint, + MarathonConstraint('a_field', 'OPERATOR', 'a_value')) + + def test_from_string_without_value(self): + constraint = MarathonConstraint.from_string('a_field:OPERATOR') + self.assertEqual(constraint, MarathonConstraint('a_field', 'OPERATOR')) + + def test_from_string_raises_an_error_for_invalid_format(self): + with self.assertRaises(ValueError): + MarathonConstraint.from_string('a_field:OPERATOR:a_value:') + + with self.assertRaises(ValueError): + MarathonConstraint.from_string('a_field') + + with self.assertRaises(ValueError): + MarathonConstraint.from_string('a_field:OPERATOR:a_value:something') From c64e2032c187f69a8717e8dc37d203ab6a7d803e Mon Sep 17 00:00:00 2001 From: diogommartins Date: Fri, 27 Oct 2017 17:25:42 -0200 Subject: [PATCH 12/73] Adding MarathonConstraint.from_string implementation --- marathon/models/constraint.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/marathon/models/constraint.py b/marathon/models/constraint.py index 7b14bf3..83d749a 100644 --- a/marathon/models/constraint.py +++ b/marathon/models/constraint.py @@ -55,3 +55,22 @@ def from_json(cls, obj): 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` + """ + parts = constraint.split(':') + + if len(parts) == 2: + (field, operator) = parts + return cls(field, operator) + elif len(parts) > 2: + (field, operator, value) = parts + return cls(field, operator, value) + else: + raise ValueError("Invalid string format. " + "Expected `field:operator:value`") From 2c97a2cbb6c11f8437108d674df5573f97f9e328 Mon Sep 17 00:00:00 2001 From: diogommartins Date: Fri, 27 Oct 2017 18:21:58 -0200 Subject: [PATCH 13/73] Refactoring MarathonConstraint.from_string to use MarathonConstraint.from_json --- marathon/models/constraint.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/marathon/models/constraint.py b/marathon/models/constraint.py index 83d749a..cc6b3f1 100644 --- a/marathon/models/constraint.py +++ b/marathon/models/constraint.py @@ -63,14 +63,11 @@ def from_string(cls, constraint): :rtype: :class:`MarathonConstraint` """ - parts = constraint.split(':') + obj = constraint.split(':') + marathon_constraint = cls.from_json(obj) - if len(parts) == 2: - (field, operator) = parts - return cls(field, operator) - elif len(parts) > 2: - (field, operator, value) = parts - return cls(field, operator, value) - else: - raise ValueError("Invalid string format. " - "Expected `field:operator:value`") + if marathon_constraint: + return marathon_constraint + + raise ValueError("Invalid string format. " + "Expected `field:operator:value`") From d1982b4e2b1efb35bf97ef6c1c94c5110f46a6de Mon Sep 17 00:00:00 2001 From: diogommartins Date: Sat, 28 Oct 2017 13:02:26 -0200 Subject: [PATCH 14/73] Removing whitespaces on blank lines --- marathon/models/constraint.py | 2 +- tests/test_model_constraint.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/marathon/models/constraint.py b/marathon/models/constraint.py index cc6b3f1..cbbde87 100644 --- a/marathon/models/constraint.py +++ b/marathon/models/constraint.py @@ -55,7 +55,7 @@ def from_json(cls, obj): if len(obj) > 2: (field, operator, value) = obj return cls(field, operator, value) - + @classmethod def from_string(cls, constraint): """ diff --git a/tests/test_model_constraint.py b/tests/test_model_constraint.py index a33417a..e8ad1d5 100644 --- a/tests/test_model_constraint.py +++ b/tests/test_model_constraint.py @@ -19,12 +19,12 @@ def test_json_repr_with_value(self): constraint = MarathonConstraint('a_field', 'OPERATOR', 'a_value') json_repr = constraint.json_repr() self.assertEqual(json_repr, ['a_field', 'OPERATOR', 'a_value']) - + def test_json_repr_without_value(self): constraint = MarathonConstraint('a_field', 'OPERATOR') json_repr = constraint.json_repr() self.assertEqual(json_repr, ['a_field', 'OPERATOR']) - + def test_from_json_with_value(self): constraint = MarathonConstraint.from_json(['a_field', 'OPERATOR', 'a_value']) self.assertEqual(constraint, From ab0397bbbf03b411bf06d8a989a4bc492e67a6b4 Mon Sep 17 00:00:00 2001 From: Diego Date: Fri, 10 Nov 2017 10:40:24 +0000 Subject: [PATCH 15/73] Move getLogger to util so it can be used from anywhere without unnecessary imports from marathon/__init__.py --- marathon/__init__.py | 5 ++--- marathon/util.py | 5 +++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/marathon/__init__.py b/marathon/__init__.py index 8d2975c..fdb6e47 100644 --- a/marathon/__init__.py +++ b/marathon/__init__.py @@ -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 --git a/marathon/util.py b/marathon/util.py index e999333..571a072 100644 --- a/marathon/util.py +++ b/marathon/util.py @@ -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) From c7304bf2b1e79ec0a7f25d938d1bbbe7780697b2 Mon Sep 17 00:00:00 2001 From: Diego Date: Fri, 10 Nov 2017 10:41:48 +0000 Subject: [PATCH 16/73] Create correct command format when receiving a string and issue a deprecation message when receiving a dict --- marathon/models/app.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/marathon/models/app.py b/marathon/models/app.py index 021d580..a142a1e 100644 --- a/marathon/models/app.py +++ b/marathon/models/app.py @@ -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,19 @@ class MarathonHealthCheck(MarathonObject): 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 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 @@ -318,8 +333,8 @@ class MarathonAppVersionInfo(MarathonObject): """ DATETIME_FORMATS = [ - '%Y-%m-%dT%H:%M:%S.%fZ', - '%Y-%m-%dT%H:%M:%SZ', + '%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): From 061d485c1fd4e2286f51ed53952baad3679eba45 Mon Sep 17 00:00:00 2001 From: Diego Date: Fri, 10 Nov 2017 14:09:48 +0000 Subject: [PATCH 17/73] Fix healthcheck creation when command is None --- marathon/models/app.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/marathon/models/app.py b/marathon/models/app.py index a142a1e..cadc3d7 100644 --- a/marathon/models/app.py +++ b/marathon/models/app.py @@ -214,7 +214,9 @@ class MarathonHealthCheck(MarathonObject): 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): - if is_stringy(command): + if command is None: + self.command = None + elif is_stringy(command): self.command = { "value": command } From ba6cf7471f31c32e1a20d7be0f7190710b1007d4 Mon Sep 17 00:00:00 2001 From: iandyh Date: Thu, 30 Nov 2017 11:00:07 +0900 Subject: [PATCH 18/73] make models.info compatible with 1.4.9 --- marathon/models/info.py | 66 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/marathon/models/info.py b/marathon/models/info.py index 40d4626..ab879f1 100644 --- a/marathon/models/info.py +++ b/marathon/models/info.py @@ -74,13 +74,47 @@ class MarathonConfig(MarathonObject): :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 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_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 +133,26 @@ def __init__(self, checkpoint=None, executor=None, failover_timeout=None, framew 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_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): @@ -116,10 +170,15 @@ class MarathonZooKeeperConfig(MarathonObject): :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_connection_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_hosts = zk_hosts self.zk_path = zk_path @@ -129,6 +188,9 @@ def __init__(self, zk=None, zk_future_timeout=None, zk_hosts=None, zk_max_versio 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): From a45eb210cfbe96702b8857283b216abfb5c6153b Mon Sep 17 00:00:00 2001 From: iandyh Date: Thu, 7 Dec 2017 11:07:02 +0900 Subject: [PATCH 19/73] fix pep8 --- marathon/models/info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/marathon/models/info.py b/marathon/models/info.py index ab879f1..803a26a 100644 --- a/marathon/models/info.py +++ b/marathon/models/info.py @@ -114,7 +114,7 @@ def __init__(self, checkpoint=None, executor=None, failover_timeout=None, framew 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 From 5fbf29659c587e4256b1d7a66360620f3b6a8df6 Mon Sep 17 00:00:00 2001 From: iandyh Date: Mon, 11 Dec 2017 11:12:12 +0900 Subject: [PATCH 20/73] replace 1.4.7 with 1.4.9 in travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fa43944..0cc300c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ env: - - MARATHONVERSION: 1.4.7 + - MARATHONVERSION: 1.4.9 - MARATHONVERSION: 1.3.0 - MARATHONVERSION: 1.1.2 - MARATHONVERSION: 0.15.3 From f3b8cd32a58795554b1a11233e7c19b2c3110ad0 Mon Sep 17 00:00:00 2001 From: Nathan Handler Date: Wed, 3 Jan 2018 13:50:48 -0800 Subject: [PATCH 21/73] Test against 1.4.10 instead of 1.4.9 Let's test against the latest point release in the series. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0cc300c..ad4d4d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ env: - - MARATHONVERSION: 1.4.9 + - MARATHONVERSION: 1.4.10 - MARATHONVERSION: 1.3.0 - MARATHONVERSION: 1.1.2 - MARATHONVERSION: 0.15.3 From 92ba425dfad5625c26f48c73441cd564547e61b9 Mon Sep 17 00:00:00 2001 From: jack Date: Thu, 11 Jan 2018 11:58:13 +0800 Subject: [PATCH 22/73] fix isuuse-238 --- marathon/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/marathon/client.py b/marathon/client.py index 2616711..fefc5f7 100644 --- a/marathon/client.py +++ b/marathon/client.py @@ -479,9 +479,9 @@ def scale_group(self, group_id, scale_by): :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): From fe38bc6046d23bf759cebcdcf2674f28e9e7c242 Mon Sep 17 00:00:00 2001 From: Nathan Handler Date: Wed, 17 Jan 2018 14:56:56 -0800 Subject: [PATCH 23/73] Test against 1.4.11 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ad4d4d1..b034ae5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ env: - - MARATHONVERSION: 1.4.10 + - MARATHONVERSION: 1.4.11 - MARATHONVERSION: 1.3.0 - MARATHONVERSION: 1.1.2 - MARATHONVERSION: 0.15.3 From a7127e80248592ae820947c6c425dd7df866c207 Mon Sep 17 00:00:00 2001 From: "cmg\\mkatica" Date: Wed, 7 Mar 2018 01:38:29 -0500 Subject: [PATCH 24/73] fixes for issue 244 --- marathon/models/container.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/marathon/models/container.py b/marathon/models/container.py index 9cadedd..0ec267a 100644 --- a/marathon/models/container.py +++ b/marathon/models/container.py @@ -97,7 +97,7 @@ class MarathonContainerPortMapping(MarathonObject): 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 From 4ef3905a15fcae6df59b8030bd3dcb21c51c7422 Mon Sep 17 00:00:00 2001 From: iandyh Date: Mon, 9 Apr 2018 11:02:31 +0900 Subject: [PATCH 25/73] add reset delay api --- marathon/client.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/marathon/client.py b/marathon/client.py index fefc5f7..590539a 100644 --- a/marathon/client.py +++ b/marathon/client.py @@ -707,6 +707,11 @@ def delete_deployment(self, deployment_id, force=False): '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. From 3864a4c3110f53aadac9cc4718ff6969c74e58c0 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Fri, 4 May 2018 11:59:49 +0200 Subject: [PATCH 26/73] install-marathon.sh: do not remove oracle-java7-installer oracle-java7-installer is not present on recent travis vms. --- itests/install-marathon.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/itests/install-marathon.sh b/itests/install-marathon.sh index 3ba2a2b..5edab54 100755 --- a/itests/install-marathon.sh +++ b/itests/install-marathon.sh @@ -16,7 +16,6 @@ sudo apt-get update # Install packages sudo DEBIAN_FRONTEND=noninteractive apt-get -y install oracle-java8-installer -sudo apt-get -y purge oracle-java7-installer sudo update-java-alternatives -s java-8-oracle sudo DEBIAN_FRONTEND=noninteractive apt-get install oracle-java8-set-default From cfde0fa24ab680ccbb04c314d1265be3a3a77d2b Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Fri, 4 May 2018 11:38:21 +0200 Subject: [PATCH 27/73] MarathonClient: set verify when using sse_session See #247 --- marathon/client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/marathon/client.py b/marathon/client.py index 590539a..38f5333 100644 --- a/marathon/client.py +++ b/marathon/client.py @@ -128,7 +128,8 @@ def _do_sse_request(self, path, params=None): 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( From 027413bc9f951c29655ed7c1b9ba7904f9ddfcc6 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 May 2018 19:41:06 +0200 Subject: [PATCH 28/73] events: add a few attributes Found in https://github.com/mesosphere/marathon/blob/master/src/main/scala/mesosphere/marathon/core/event/Events.scala --- marathon/models/events.py | 41 ++++++++++++++++++++++++--------- tests/test_model_event.py | 48 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 76 insertions(+), 13 deletions(-) diff --git a/marathon/models/events.py b/marathon/models/events.py index bd64b16..630e360 100644 --- a/marathon/models/events.py +++ b/marathon/models/events.py @@ -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 @@ class MarathonEvent(MarathonObject): 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 @@ def __init__(self, event_type, timestamp, **kwargs): 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 MarathonApiPostEvent(MarathonEvent): 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 MarathonRemoveHealthCheckEvent(MarathonEvent): 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 MarathonDeploymentFailed(MarathonEvent): class MarathonDeploymentInfo(MarathonEvent): - KNOWN_ATTRIBUTES = ['plan'] + KNOWN_ATTRIBUTES = ['plan', 'current_step'] class MarathonDeploymentStepSuccess(MarathonEvent): @@ -112,7 +131,7 @@ class MarathonEventStreamDetached(MarathonEvent): class MarathonUnhealthyTaskKillEvent(MarathonEvent): - KNOWN_ATTRIBUTES = ['app_id', 'task_id', 'version', 'reason'] + KNOWN_ATTRIBUTES = ['app_id', 'task_id', 'instance_id', 'version', 'reason'] class MarathonAppTerminatedEvent(MarathonEvent): diff --git a/tests/test_model_event.py b/tests/test_model_event.py index ad1efef..e62c0b3 100644 --- a/tests/test_model_event.py +++ b/tests/test_model_event.py @@ -1,10 +1,54 @@ # encoding: utf-8 -from marathon.models.events import EventFactory +from marathon.models.events import EventFactory, MarathonStatusUpdateEvent +from marathon.models.task import MarathonIpAddress import unittest class MarathonEventTest(unittest.TestCase): def test_event_factory(self): - self.assertEqual(set(EventFactory.event_to_class.keys()), set(EventFactory.class_to_event.values())) + self.assertEqual( + set(EventFactory.event_to_class.keys()), + set(EventFactory.class_to_event.values()), + ) + + def test_marathon_event(self): + """Test that we can process at least one kind of event.""" + payload = { + "eventType": "status_update_event", + "slaveId": "slave-01", + "taskId": "task-01", + "taskStatus": "TASK_RUNNING", + "message": "Some message", + "appId": "/foo/bar", + "host": "host-01", + "ipAddresses": [ + {"ip_address": "127.0.0.1", "protocol": "tcp"}, + {"ip_address": "127.0.0.1", "protocol": "udp"}, + ], + "ports": [0, 1], + "version": "1234", + "timestamp": 12345, + } + factory = EventFactory() + event = factory.process(payload) + + expected_event = MarathonStatusUpdateEvent( + event_type="status_update_event", + timestamp=12345, + slave_id="slave-01", + task_id="task-01", + task_status="TASK_RUNNING", + message="Some message", + app_id="/foo/bar", + host="host-01", + ports=[0, 1], + version="1234", + ) + expected_event.ip_addresses = [ + MarathonIpAddress(ip_address="127.0.0.1", protocol="tcp"), + MarathonIpAddress(ip_address="127.0.0.1", protocol="udp"), + ] + + self.assertEqual(event.to_json(), expected_event.to_json()) From 6a2803fe0d18c681ddc189b7bbab30724c51f900 Mon Sep 17 00:00:00 2001 From: Kornel Maleszka Date: Wed, 16 May 2018 12:22:33 +0200 Subject: [PATCH 29/73] Possibility for send the full json object on create --- marathon/client.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/marathon/client.py b/marathon/client.py index 38f5333..dcb6d06 100644 --- a/marathon/client.py +++ b/marathon/client.py @@ -148,17 +148,18 @@ def list_endpoints(self): """ 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) From 941269af413e9f384ba3e31ea83889be4dc97f6e Mon Sep 17 00:00:00 2001 From: Joris De Winne Date: Fri, 3 Aug 2018 14:03:59 -0700 Subject: [PATCH 30/73] Testing with 1.6 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index b034ae5..4c96d9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ env: + - MARATHONVERSION: 1.6.322 - MARATHONVERSION: 1.4.11 - MARATHONVERSION: 1.3.0 - MARATHONVERSION: 1.1.2 From 2e968e5674191eb590dc66fb444ca5cc2f715f23 Mon Sep 17 00:00:00 2001 From: Joris De Winne Date: Fri, 3 Aug 2018 14:11:03 -0700 Subject: [PATCH 31/73] Depending on mesos 1.11.* --- itests/install-marathon.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itests/install-marathon.sh b/itests/install-marathon.sh index 5edab54..b86ba98 100755 --- a/itests/install-marathon.sh +++ b/itests/install-marathon.sh @@ -19,7 +19,7 @@ sudo DEBIAN_FRONTEND=noninteractive apt-get -y install oracle-java8-installer sudo update-java-alternatives -s java-8-oracle sudo DEBIAN_FRONTEND=noninteractive apt-get install oracle-java8-set-default -sudo DEBIAN_FRONTEND=noninteractive apt-get -y --force-yes install mesos=1.1.* marathon=$MARATHONVERSION* +sudo DEBIAN_FRONTEND=noninteractive apt-get -y --force-yes install mesos=1.11.* marathon=$MARATHONVERSION* # WTF MARATHON? # Why does the precise version have java7 hardcoded if it requires java8? From e49f02f6d53c6e890a2bc32816f1e2cbadce21b1 Mon Sep 17 00:00:00 2001 From: Joris De Winne Date: Fri, 3 Aug 2018 14:25:04 -0700 Subject: [PATCH 32/73] Trying with mesos 1.6 --- itests/install-marathon.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itests/install-marathon.sh b/itests/install-marathon.sh index b86ba98..6bff172 100755 --- a/itests/install-marathon.sh +++ b/itests/install-marathon.sh @@ -19,7 +19,7 @@ sudo DEBIAN_FRONTEND=noninteractive apt-get -y install oracle-java8-installer sudo update-java-alternatives -s java-8-oracle sudo DEBIAN_FRONTEND=noninteractive apt-get install oracle-java8-set-default -sudo DEBIAN_FRONTEND=noninteractive apt-get -y --force-yes install mesos=1.11.* marathon=$MARATHONVERSION* +sudo DEBIAN_FRONTEND=noninteractive apt-get -y --force-yes install mesos=1.6.* marathon=$MARATHONVERSION* # WTF MARATHON? # Why does the precise version have java7 hardcoded if it requires java8? From 25b6a451ae63ec143bd7566f92c5b73b87e1233e Mon Sep 17 00:00:00 2001 From: Joris De Winne Date: Mon, 6 Aug 2018 09:20:22 -0700 Subject: [PATCH 33/73] Adding mesos_bridge_name --- marathon/models/info.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/marathon/models/info.py b/marathon/models/info.py index 803a26a..fdd68b0 100644 --- a/marathon/models/info.py +++ b/marathon/models/info.py @@ -81,6 +81,7 @@ class MarathonConfig(MarathonObject): :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: @@ -104,7 +105,7 @@ def __init__(self, checkpoint=None, executor=None, failover_timeout=None, framew 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, + 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, @@ -140,6 +141,7 @@ def __init__(self, checkpoint=None, executor=None, failover_timeout=None, framew 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 From eec72ae08a199c843f082970bc25046edb3fd652 Mon Sep 17 00:00:00 2001 From: Joris De Winne Date: Mon, 6 Aug 2018 13:35:37 -0700 Subject: [PATCH 34/73] Using correct marathon startup command for 1.6 --- README.md | 1 + itests/start-marathon.sh | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0822d97..f18704a 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ This is a Python library for interfacing with [Marathon](https://github.com/meso #### 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 and 0.15.x, use at least 0.8.1 * For Marathon 0.14.x, use at least 0.7.6 diff --git a/itests/start-marathon.sh b/itests/start-marathon.sh index 6b18743..8be80b9 100755 --- a/itests/start-marathon.sh +++ b/itests/start-marathon.sh @@ -1,12 +1,14 @@ #!/bin/bash if [[ $MARATHONVERSION != '0.8.1' ]]; then - LOGGER="--no-logger" + LOGGER="--logging_level info" else LOGGER="" fi java -version export MESOS_WORK_DIR='/tmp/mesos' +export ZK_HOST=`cat /etc/mesos/zk` + mkdir -p "$MESOS_WORK_DIR" -exec /usr/bin/marathon --master local $LOGGER --hostname localhost +exec /usr/bin/marathon --master $ZK_HOST $LOGGER --hostname localhost From 87263c21ea4f873a7657129251b949a673712491 Mon Sep 17 00:00:00 2001 From: Joris De Winne Date: Mon, 6 Aug 2018 19:41:45 -0700 Subject: [PATCH 35/73] Changing docker compose to expose port --- itests/docker-compose.yml | 2 +- itests/itest_utils.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/itests/docker-compose.yml b/itests/docker-compose.yml index 13a0062..809d456 100644 --- a/itests/docker-compose.yml +++ b/itests/docker-compose.yml @@ -2,4 +2,4 @@ marathon: build: . ports: - - 8080 + - 18080:8080 diff --git a/itests/itest_utils.py b/itests/itest_utils.py index 6b4d17c..f5028fe 100644 --- a/itests/itest_utils.py +++ b/itests/itest_utils.py @@ -63,8 +63,7 @@ def get_marathon_connection_string(): return 'localhost:8080' else: service_port = get_service_internal_port('marathon') - local_port = get_compose_service('marathon').get_container().get_local_port(service_port) - return local_port + return "localhost:%s" % service_port.published def get_service_internal_port(service_name): From 81dcdb93341f14c53ccd0f03ed94b9f5fa13c733 Mon Sep 17 00:00:00 2001 From: Joris De Winne Date: Tue, 7 Aug 2018 20:57:15 -0700 Subject: [PATCH 36/73] Fixing Dockerfile and compose to run Mesos and Marathon on 1 container for testing --- itests/Dockerfile | 2 +- itests/docker-compose.yml | 1 + itests/start-marathon.sh | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/itests/Dockerfile b/itests/Dockerfile index c43b783..b2223c7 100644 --- a/itests/Dockerfile +++ b/itests/Dockerfile @@ -14,6 +14,6 @@ ADD ./marathon-version /root/marathon-version ADD ./install-marathon.sh /root/install-marathon.sh RUN /root/install-marathon.sh -EXPOSE 8080 +EXPOSE 8080 5050 ADD ./start-marathon.sh /root/start-marathon.sh CMD /etc/init.d/zookeeper start && /root/start-marathon.sh diff --git a/itests/docker-compose.yml b/itests/docker-compose.yml index 809d456..064c756 100644 --- a/itests/docker-compose.yml +++ b/itests/docker-compose.yml @@ -3,3 +3,4 @@ marathon: build: . ports: - 18080:8080 + - 15050:5050 diff --git a/itests/start-marathon.sh b/itests/start-marathon.sh index 8be80b9..2c3cc8a 100755 --- a/itests/start-marathon.sh +++ b/itests/start-marathon.sh @@ -11,4 +11,6 @@ export MESOS_WORK_DIR='/tmp/mesos' export ZK_HOST=`cat /etc/mesos/zk` mkdir -p "$MESOS_WORK_DIR" -exec /usr/bin/marathon --master $ZK_HOST $LOGGER --hostname localhost +nohup mesos-master --work_dir=/tmp/mesosmaster --zk=$ZK_HOST --quorum=1 & +nohup mesos-agent --master=$ZK_HOST --work_dir=/tmp/mesosagent --launcher=posix & +exec /usr/bin/marathon --master $ZK_HOST $LOGGER From a1c614116330e7ccf71f4916a3adeba86b46633c Mon Sep 17 00:00:00 2001 From: Joris De Winne Date: Tue, 7 Aug 2018 21:08:25 -0700 Subject: [PATCH 37/73] Removing unneeded space --- marathon/models/info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/marathon/models/info.py b/marathon/models/info.py index fdd68b0..5ca62d4 100644 --- a/marathon/models/info.py +++ b/marathon/models/info.py @@ -105,7 +105,7 @@ def __init__(self, checkpoint=None, executor=None, failover_timeout=None, framew 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, + 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, From b321daa573b3d2bc2b4b9a8d19f102093aa33aa9 Mon Sep 17 00:00:00 2001 From: Kyle Anderson Date: Wed, 8 Aug 2018 10:25:22 -0700 Subject: [PATCH 38/73] Remove support for marathon pre 1.0 --- .travis.yml | 5 ----- README.md | 6 +----- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4c96d9b..b4a62a7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,11 +3,6 @@ env: - MARATHONVERSION: 1.4.11 - MARATHONVERSION: 1.3.0 - MARATHONVERSION: 1.1.2 - - MARATHONVERSION: 0.15.3 - - MARATHONVERSION: 0.14.1 - - MARATHONVERSION: 0.13.1 - - MARATHONVERSION: 0.11.1 - - MARATHONVERSION: 0.10.1 language: python python: diff --git a/README.md b/README.md index f18704a..cde7fde 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,7 @@ This is a Python library for interfacing with [Marathon](https://github.com/meso * 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 and 0.15.x, use at least 0.8.1 -* For Marathon 0.14.x, use at least 0.7.6 -* For Marathon 0.8.x-0.11.x, use at least marathon-python 0.7.5 -* For Marathon 0.8.x-0.9.x, use as least marathon-python 0.6.11 - 0.7.4 -* For Marathon 0.7.x, use at least marathon-python 0.6.10 +* 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 From e0d00510bef973c77a414d61460942e9b62c8635 Mon Sep 17 00:00:00 2001 From: Kyle Anderson Date: Wed, 8 Aug 2018 10:27:12 -0700 Subject: [PATCH 39/73] Remove support for more older versions of marathon --- .travis.yml | 2 +- README.md | 2 +- itests/start-marathon.sh | 6 +----- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index b4a62a7..1f0831d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,5 +33,5 @@ deploy: secure: "Wl8GWxsfPy4KoORYH26N3FllvMeWrifzeCbEx2Af4corcBQl43heeiFRRTlUOcSX0TIasER21PUvQ0R0cAgCjfknDb3SOROcRtcSBe16+cMmvwysfxcAx2OcF1UYBPY8e/qOsGge2Zyzx2PAPNEmJoWKbIT3vUJ4WvlLVeGYdJ0=" on: tags: true - condition: $MARATHONVERSION == "1.4.7" + condition: $MARATHONVERSION == "1.6.322" repo: thefactory/marathon-python diff --git a/README.md b/README.md index cde7fde..592bb92 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ make itests ### Running The Tests Against a Specific Version of Marathon ```bash -MARATHONVERSION=0.9.0 make itests +MARATHONVERSION=1.6.322 make itests ``` ## Documentation diff --git a/itests/start-marathon.sh b/itests/start-marathon.sh index 2c3cc8a..0da2305 100755 --- a/itests/start-marathon.sh +++ b/itests/start-marathon.sh @@ -1,10 +1,6 @@ #!/bin/bash -if [[ $MARATHONVERSION != '0.8.1' ]]; then - LOGGER="--logging_level info" -else - LOGGER="" -fi +LOGGER="--logging_level info" java -version export MESOS_WORK_DIR='/tmp/mesos' From 6d352c8b48b074fdf4bc4845f91d3eded98460c4 Mon Sep 17 00:00:00 2001 From: Kyle Anderson Date: Wed, 8 Aug 2018 10:28:12 -0700 Subject: [PATCH 40/73] Make a new release to 0.10.0 --- CHANGELOG.md | 35 ++++++++++++++++++++++++++++++++--- setup.py | 2 +- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5c0a40..b29b553 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,33 @@ # Change Log +## [0.10.0](https://github.com/thefactory/marathon-python/tree/0.10.0) (2018-08-08) +[Full Changelog](https://github.com/thefactory/marathon-python/compare/0.9.3...0.10.0) + +**Closed issues:** + +- Travis tests are broken [\#249](https://github.com/thefactory/marathon-python/issues/249) +- SSE SSL authentication not supported [\#247](https://github.com/thefactory/marathon-python/issues/247) +- Lack of support for container.networks [\#243](https://github.com/thefactory/marathon-python/issues/243) +- \_\_init\_\_\(\) got an unexpected keyword argument 'port\_mappings' [\#237](https://github.com/thefactory/marathon-python/issues/237) +- Wrong health check object generated for COMMAND protocol [\#222](https://github.com/thefactory/marathon-python/issues/222) + +**Merged pull requests:** + +- Add support for mesos 1.6 [\#255](https://github.com/thefactory/marathon-python/pull/255) ([jdewinne](https://github.com/jdewinne)) +- Possibility for send the full json object on create [\#252](https://github.com/thefactory/marathon-python/pull/252) ([kkorekk](https://github.com/kkorekk)) +- events: add a few attributes [\#251](https://github.com/thefactory/marathon-python/pull/251) ([iksaif](https://github.com/iksaif)) +- install-marathon.sh: do not remove oracle-java7-installer [\#250](https://github.com/thefactory/marathon-python/pull/250) ([iksaif](https://github.com/iksaif)) +- MarathonClient: set verify when using sse\_session [\#248](https://github.com/thefactory/marathon-python/pull/248) ([iksaif](https://github.com/iksaif)) +- add reset delay api [\#246](https://github.com/thefactory/marathon-python/pull/246) ([iandyh](https://github.com/iandyh)) +- fixes for issue 244 [\#245](https://github.com/thefactory/marathon-python/pull/245) ([mikekatica](https://github.com/mikekatica)) +- Test against 1.4.11 [\#240](https://github.com/thefactory/marathon-python/pull/240) ([nhandler](https://github.com/nhandler)) +- fix isuuse-238 [\#239](https://github.com/thefactory/marathon-python/pull/239) ([yudong2015](https://github.com/yudong2015)) +- Test against 1.4.10 instead of 1.4.9 [\#236](https://github.com/thefactory/marathon-python/pull/236) ([nhandler](https://github.com/nhandler)) +- make models.info compatible with 1.4.9 [\#233](https://github.com/thefactory/marathon-python/pull/233) ([iandyh](https://github.com/iandyh)) +- Fix health check 'command' [\#231](https://github.com/thefactory/marathon-python/pull/231) ([protetore](https://github.com/protetore)) +- Feature/marathon constraint model improvements [\#229](https://github.com/thefactory/marathon-python/pull/229) ([diogommartins](https://github.com/diogommartins)) +- Removes id validation from MarathonGroup\(\) [\#228](https://github.com/thefactory/marathon-python/pull/228) ([daltonmatos](https://github.com/daltonmatos)) + ## [0.9.3](https://github.com/thefactory/marathon-python/tree/0.9.3) (2017-10-16) [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.9.2...0.9.3) @@ -9,6 +37,7 @@ **Merged pull requests:** +- Release 0.9.3 [\#224](https://github.com/thefactory/marathon-python/pull/224) ([solarkennedy](https://github.com/solarkennedy)) - Make travis automatically upload to pypi on new tags [\#223](https://github.com/thefactory/marathon-python/pull/223) ([solarkennedy](https://github.com/solarkennedy)) - Fix MarathonQueueItem to know about the possible last\_unused\_offers arg [\#221](https://github.com/thefactory/marathon-python/pull/221) ([matthewbentley](https://github.com/matthewbentley)) - support more datetime formats in MarathonAppVersionInfo [\#219](https://github.com/thefactory/marathon-python/pull/219) ([somic](https://github.com/somic)) @@ -44,7 +73,7 @@ - Support filtering applications by labels [\#211](https://github.com/thefactory/marathon-python/pull/211) ([iandyh](https://github.com/iandyh)) - add embed option for /v2/queue [\#210](https://github.com/thefactory/marathon-python/pull/210) ([Rob-Johnson](https://github.com/Rob-Johnson)) - Enable TCP keepalive for sse requests [\#209](https://github.com/thefactory/marathon-python/pull/209) ([fengyehong](https://github.com/fengyehong)) -- Add "udp,tcp" to authorized protocols for containers [\#208](https://github.com/thefactory/marathon-python/pull/208) ([fuegowolf](https://github.com/fuegowolf)) +- Add "udp,tcp" to authorized protocols for containers [\#208](https://github.com/thefactory/marathon-python/pull/208) ([alxkt](https://github.com/alxkt)) - Allow event type filter on event stream [\#207](https://github.com/thefactory/marathon-python/pull/207) ([fengyehong](https://github.com/fengyehong)) - Fix MarathonResource hash as well [\#205](https://github.com/thefactory/marathon-python/pull/205) ([jolynch](https://github.com/jolynch)) @@ -362,7 +391,7 @@ **Merged pull requests:** -- Updated to support Marathon 0.9.1 with get\_info\(\) calls [\#59](https://github.com/thefactory/marathon-python/pull/59) ([pyronicide](https://github.com/pyronicide)) +- Updated to support Marathon 0.9.1 with get\_info\(\) calls [\#59](https://github.com/thefactory/marathon-python/pull/59) ([grampelberg](https://github.com/grampelberg)) - Add support for building with a wheel and cleanup setup.py [\#58](https://github.com/thefactory/marathon-python/pull/58) ([mattrobenolt](https://github.com/mattrobenolt)) - travis should run unit tests [\#55](https://github.com/thefactory/marathon-python/pull/55) ([Rob-Johnson](https://github.com/Rob-Johnson)) - implement \_\_eq\_\_ on base models + fix tests to be useful [\#54](https://github.com/thefactory/marathon-python/pull/54) ([Rob-Johnson](https://github.com/Rob-Johnson)) @@ -408,7 +437,7 @@ **Merged pull requests:** - Added forcePullImage parameter for the container model [\#31](https://github.com/thefactory/marathon-python/pull/31) ([solarkennedy](https://github.com/solarkennedy)) -- Quick fix \#29 - add kwargs to MarathonDockerContainer.\_\_init\_\_ [\#30](https://github.com/thefactory/marathon-python/pull/30) ([g----](https://github.com/g----)) +- Quick fix \#29 - add kwargs to MarathonDockerContainer.\_\_init\_\_ [\#30](https://github.com/thefactory/marathon-python/pull/30) ([ghost](https://github.com/ghost)) - Fixed \#26:Using try/except to get rid of use\_2to3 failing [\#27](https://github.com/thefactory/marathon-python/pull/27) ([vitan](https://github.com/vitan)) ## [0.6.13](https://github.com/thefactory/marathon-python/tree/0.6.13) (2015-03-24) diff --git a/setup.py b/setup.py index 5f0f2d5..dac7cb6 100755 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setup( name='marathon', - version='0.9.3', + version='0.10.0', description='Marathon Client Library', long_description="""Python interface to the Mesos Marathon REST API.""", author='Mike Babineau', From 72263bd22673f9eeb398ca74e3b2eae9d24da547 Mon Sep 17 00:00:00 2001 From: Guanglu Guo Date: Tue, 6 Nov 2018 11:45:45 +0800 Subject: [PATCH 41/73] Seperate no response error exception --- marathon/client.py | 4 ++-- marathon/exceptions.py | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/marathon/client.py b/marathon/client.py index dcb6d06..3750472 100644 --- a/marathon/client.py +++ b/marathon/client.py @@ -12,7 +12,7 @@ import marathon from .models import MarathonApp, MarathonDeployment, MarathonGroup, MarathonInfo, MarathonTask, MarathonEndpoint, MarathonQueueItem -from .exceptions import InternalServerError, NotFoundError, MarathonHttpError, MarathonError +from .exceptions import InternalServerError, NotFoundError, MarathonHttpError, MarathonError, NoResponseError from .models.events import EventFactory, MarathonEvent from .util import MarathonJsonEncoder, MarathonMinimalJsonEncoder @@ -96,7 +96,7 @@ def _do_request(self, method, path, params=None, data=None): '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( diff --git a/marathon/exceptions.py b/marathon/exceptions.py index 8d2d249..2697625 100644 --- a/marathon/exceptions.py +++ b/marathon/exceptions.py @@ -40,3 +40,7 @@ def __init__(self, param, value, options): param=param, value=value, options=options ) ) + + +class NoResponseError(MarathonError): + pass From e18f8e358efd1f26c2e8ae59effa9bc3b2e47d16 Mon Sep 17 00:00:00 2001 From: Guanglu Guo Date: Wed, 7 Nov 2018 21:47:32 +0800 Subject: [PATCH 42/73] Seperate conflict error exception --- marathon/client.py | 4 +++- marathon/exceptions.py | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/marathon/client.py b/marathon/client.py index 3750472..7d16356 100644 --- a/marathon/client.py +++ b/marathon/client.py @@ -12,7 +12,7 @@ import marathon from .models import MarathonApp, MarathonDeployment, MarathonGroup, MarathonInfo, MarathonTask, MarathonEndpoint, MarathonQueueItem -from .exceptions import InternalServerError, NotFoundError, MarathonHttpError, MarathonError, NoResponseError +from .exceptions import ConflictError, InternalServerError, NotFoundError, MarathonHttpError, MarathonError, NoResponseError from .models.events import EventFactory, MarathonEvent from .util import MarathonJsonEncoder, MarathonMinimalJsonEncoder @@ -107,6 +107,8 @@ def _do_request(self, method, path, params=None, data=None): 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: diff --git a/marathon/exceptions.py b/marathon/exceptions.py index 2697625..a5ffe73 100644 --- a/marathon/exceptions.py +++ b/marathon/exceptions.py @@ -32,6 +32,10 @@ class InternalServerError(MarathonHttpError): pass +class ConflictError(MarathonHttpError): + pass + + class InvalidChoiceError(MarathonError): def __init__(self, param, value, options): From ad4bd03fd194629030badc0c79882c656c652b1e Mon Sep 17 00:00:00 2001 From: Jonathan Meyer Date: Thu, 27 Dec 2018 15:34:50 -0500 Subject: [PATCH 43/73] Added region and zone members to task model. Missing region and zone members resulted in stack trace when making calls to get_app under DCOS EE 1.11+ --- marathon/models/task.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/marathon/models/task.py b/marathon/models/task.py index 6b81f74..c756e12 100644 --- a/marathon/models/task.py +++ b/marathon/models/task.py @@ -21,12 +21,17 @@ class MarathonTask(MarathonResource): :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 @@ def __init__(self, app_id=None, health_check_results=None, host=None, id=None, p 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): From 1407aed3011a0a531f50efcf15982ee31486e314 Mon Sep 17 00:00:00 2001 From: Kyle Anderson Date: Tue, 15 Jan 2019 11:10:13 -0800 Subject: [PATCH 44/73] Release 0.11.0 --- CHANGELOG.md | 9 +++++++++ setup.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b29b553..ded5397 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Change Log +## [0.11.0](https://github.com/thefactory/marathon-python/tree/0.11.0) (2019-01-15) +[Full Changelog](https://github.com/thefactory/marathon-python/compare/0.10.0...0.11.0) + +**Merged pull requests:** + +- Added region and zone members to task model. [\#260](https://github.com/thefactory/marathon-python/pull/260) ([gisjedi](https://github.com/gisjedi)) +- Exception [\#259](https://github.com/thefactory/marathon-python/pull/259) ([fengyehong](https://github.com/fengyehong)) +- New release 0.10.0 [\#256](https://github.com/thefactory/marathon-python/pull/256) ([solarkennedy](https://github.com/solarkennedy)) + ## [0.10.0](https://github.com/thefactory/marathon-python/tree/0.10.0) (2018-08-08) [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.9.3...0.10.0) diff --git a/setup.py b/setup.py index dac7cb6..2e8edfa 100755 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setup( name='marathon', - version='0.10.0', + version='0.11.0', description='Marathon Client Library', long_description="""Python interface to the Mesos Marathon REST API.""", author='Mike Babineau', From 9d2df6aae456dd80c0cd9b78972276fab3845fe4 Mon Sep 17 00:00:00 2001 From: Guanglu Guo Date: Tue, 21 Nov 2017 17:43:47 +0800 Subject: [PATCH 45/73] Compatible with event stream redirect behavior. --- marathon/client.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/marathon/client.py b/marathon/client.py index 7d16356..9e0f2b9 100644 --- a/marathon/client.py +++ b/marathon/client.py @@ -122,9 +122,12 @@ def _do_request(self, method, path, params=None, data=None): def _do_sse_request(self, path, params=None): """Query Marathon server for events.""" - for server in list(self.servers): - url = ''.join([server.rstrip('/'), path]) + urls = [''.join([server.rstrip('/'), path]) for server in self.servers] + while urls: + url = urls.pop() try: + # Requests does not set the original Authorization header on cross origin + # redirects. If set allow_redirects=True we may get a 401 response. response = self.sse_session.get( url, params=params, @@ -132,12 +135,16 @@ def _do_sse_request(self, path, params=None): headers={'Accept': 'text/event-stream'}, auth=self.auth, verify=self.verify, + allow_redirects=False ) except Exception as e: marathon.log.error( 'Error while calling %s: %s', url, e.message) else: - if response.ok: + if response.is_redirect and response.next: + urls.append(response.next.url) + marathon.log.debug("Got redirect to {}".format(response.next.url)) + elif response.ok: return response.iter_lines() raise MarathonError('No remaining Marathon servers to try') From 5fcc3d9f0e4dfe5be0e012da3bf1b6a90edba1f8 Mon Sep 17 00:00:00 2001 From: Evan Krall Date: Wed, 13 Nov 2019 13:12:21 -0800 Subject: [PATCH 46/73] Drop support for python2.7, bump python3 support to 3.6 and 3.7 --- .gitignore | 1 + .pre-commit-config.yaml | 5 +++++ .travis.yml | 3 ++- Makefile | 8 +++---- docs/conf.py | 25 ++++++++++----------- itests/steps/marathon_steps.py | 32 +++++++++++++-------------- marathon/_compat.py | 11 ---------- marathon/client.py | 40 +++++++++++++++++----------------- marathon/exceptions.py | 4 ++-- marathon/models/app.py | 10 ++++----- marathon/models/base.py | 6 ++--- marathon/models/events.py | 4 ++-- marathon/util.py | 10 ++------- setup.py | 5 ++--- tests/test_api.py | 12 +++++----- tests/test_model_app.py | 2 -- tests/test_model_event.py | 2 -- tests/test_model_group.py | 2 -- tests/test_model_object.py | 2 -- tox.ini | 16 ++++++++++---- 20 files changed, 94 insertions(+), 106 deletions(-) create mode 100644 .pre-commit-config.yaml delete mode 100644 marathon/_compat.py diff --git a/.gitignore b/.gitignore index 99e9b12..2947fe4 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,4 @@ packer_cache /gh-pages itests/marathon-version +.pytest_cache/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..80e93db --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,5 @@ +- repo: https://github.com/asottile/pyupgrade + rev: v1.25.1 + hooks: + - id: pyupgrade + args: [--py36-plus] \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 1f0831d..c227dfb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,8 @@ env: language: python python: - - 2.7 + - 3.6 + - 3.7 install: - pip install tox script: diff --git a/Makefile b/Makefile index 6c3bbe8..f64cc3e 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,11 @@ itests: - tox -e itest-py27 - tox -e itest-py33 + tox -e itest-py36 + tox -e itest-py37 test: tox -e pep8 - tox -e test-py27 - tox -e test-py33 + tox -e test-py36 + tox -e test-py37 clean: rm -rf dist/ build/ diff --git a/docs/conf.py b/docs/conf.py index e3a0e64..b5611ef 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # marathon-python documentation build configuration file, created by # sphinx-quickstart on Tue Apr 22 11:36:23 2014. @@ -46,8 +45,8 @@ master_doc = 'index' # General information about the project. -project = u'marathon-python' -copyright = u'2014, The Factory' +project = 'marathon-python' +copyright = '2014, The Factory' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -203,8 +202,8 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'marathon-python.tex', u'marathon-python Documentation', - u'Mike Babineau', 'manual'), + ('index', 'marathon-python.tex', 'marathon-python Documentation', + 'Mike Babineau', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -233,8 +232,8 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'marathon-python', u'marathon-python Documentation', - [u'Mike Babineau'], 1) + ('index', 'marathon-python', 'marathon-python Documentation', + ['Mike Babineau'], 1) ] # If true, show URL addresses after external links. @@ -247,8 +246,8 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'marathon-python', u'marathon-python Documentation', - u'Mike Babineau', 'marathon-python', 'One line description of project.', + ('index', 'marathon-python', 'marathon-python Documentation', + 'Mike Babineau', 'marathon-python', 'One line description of project.', 'Miscellaneous'), ] @@ -268,10 +267,10 @@ # -- Options for Epub output ---------------------------------------------- # Bibliographic Dublin Core info. -epub_title = u'marathon-python' -epub_author = u'Mike Babineau' -epub_publisher = u'Mike Babineau' -epub_copyright = u'2014, The Factory' +epub_title = 'marathon-python' +epub_author = 'Mike Babineau' +epub_publisher = 'Mike Babineau' +epub_copyright = '2014, The Factory' # The basename for the epub file. It defaults to the project name. #epub_basename = u'marathon-python' diff --git a/itests/steps/marathon_steps.py b/itests/steps/marathon_steps.py index 434bc04..131c199 100644 --- a/itests/steps/marathon_steps.py +++ b/itests/steps/marathon_steps.py @@ -20,18 +20,18 @@ def working_marathon(context): context.client = marathon.MarathonClient(marathon_connection_string) -@then(u'we get the marathon instance\'s info') +@then('we get the marathon instance\'s info') def get_marathon_info(context): assert context.client.get_info() -@when(u'we create a trivial new app') +@when('we create a trivial new app') def create_trivial_new_app(context): context.client.create_app('test-trivial-app', marathon.MarathonApp( cmd='sleep 3600', mem=16, cpus=0.1, instances=5)) -@then(u'we should be able to kill the tasks') +@then('we should be able to kill the tasks') def kill_a_task(context): time.sleep(5) app = context.client.get_app('test-trivial-app') @@ -40,7 +40,7 @@ def kill_a_task(context): app_id='test-trivial-app', task_id=tasks[0].id, scale=True) -@when(u'we create a complex new app') +@when('we create a complex new app') def create_complex_new_app_with_unicode(context): app_config = { 'container': { @@ -51,13 +51,13 @@ def create_complex_new_app_with_unicode(context): 'name': 'myport', 'containerPort': 8888, 'hostPort': 0}], - 'image': u'localhost/fake_docker_url', + 'image': 'localhost/fake_docker_url', 'network': 'BRIDGE', 'parameters': [{'key': 'add-host', 'value': 'google-public-dns-a.google.com:8.8.8.8'}], }, 'volumes': - [{'hostPath': u'/etc/stuff', - 'containerPath': u'/etc/stuff', + [{'hostPath': '/etc/stuff', + 'containerPath': '/etc/stuff', 'mode': 'RO'}], }, 'instances': 1, @@ -68,7 +68,7 @@ def create_complex_new_app_with_unicode(context): 'uris': ['file:///root/.dockercfg'], 'backoff_seconds': 1, 'constraints': None, - 'cmd': u'/bin/true', + 'cmd': '/bin/true', 'health_checks': [ { 'protocol': 'HTTP', @@ -85,13 +85,13 @@ def create_complex_new_app_with_unicode(context): 'test-complex-app', marathon.MarathonApp(**app_config)) -@then(u'we should see the {which} app running via the marathon api') +@then('we should see the {which} app running via the marathon api') def see_complext_app_running(context, which): print(context.client.list_apps()) assert context.client.get_app('test-%s-app' % which) -@when(u'we wait the {which} app deployment finish') +@when('we wait the {which} app deployment finish') def wait_deployment_finish(context, which): while True: time.sleep(1) @@ -100,7 +100,7 @@ def wait_deployment_finish(context, which): break -@then(u'we should be able to kill the #{to_kill} tasks of the {which} app') +@then('we should be able to kill the #{to_kill} tasks of the {which} app') def kill_tasks(context, to_kill, which): app_tasks = context.client.get_app( 'test-%s-app' % which, embed_tasks=True).tasks @@ -111,11 +111,11 @@ def kill_tasks(context, to_kill, which): context.client.kill_given_tasks(task_to_kill) -@then(u'we should be able to list tasks of the {which} app') +@then('we should be able to list tasks of the {which} app') def list_tasks(context, which): app = context.client.get_app('test-%s-app' % which) tasks = context.client.list_tasks('test-%s-app' % which) - assert len(tasks) == app.instances, "we defined %s tasks, got %s tasks" % (app.instances, len(tasks)) + assert len(tasks) == app.instances, "we defined {} tasks, got {} tasks".format(app.instances, len(tasks)) def listen_for_events(client, events): @@ -123,14 +123,14 @@ def listen_for_events(client, events): events.append(msg) -@when(u'marathon version is greater than {version}') +@when('marathon version is greater than {version}') def marathon_version_chech(context, version): info = context.client.get_info() if LooseVersion(info.version) < LooseVersion(version): context.scenario.skip(reason='Marathon version is too low for this scenario') -@when(u'we start listening for events') +@when('we start listening for events') def start_listening_stream(context): manager = multiprocessing.Manager() mlist = manager.list() @@ -141,7 +141,7 @@ def start_listening_stream(context): context.p = p -@then(u'we should see list of events') +@then('we should see list of events') def stop_listening_stream(context): time.sleep(10) context.p.terminate() diff --git a/marathon/_compat.py b/marathon/_compat.py deleted file mode 100644 index b2c19ff..0000000 --- a/marathon/_compat.py +++ /dev/null @@ -1,11 +0,0 @@ -""" -Support for python 2 & 3, ripped pieces from six.py -""" -import sys - -PY3 = sys.version_info[0] == 3 - -if PY3: - string_types = str, -else: - string_types = basestring, diff --git a/marathon/client.py b/marathon/client.py index 9e0f2b9..25782db 100644 --- a/marathon/client.py +++ b/marathon/client.py @@ -17,7 +17,7 @@ from .util import MarathonJsonEncoder, MarathonMinimalJsonEncoder -class MarathonClient(object): +class MarathonClient: """Client interface for the Marathon REST API.""" @@ -79,7 +79,7 @@ def _do_request(self, method, path, params=None, data=None): 'Content-Type': 'application/json', 'Accept': 'application/json'} if self.auth_token: - headers['Authorization'] = "token={}".format(self.auth_token) + headers['Authorization'] = f"token={self.auth_token}" response = None servers = list(self.servers) @@ -143,7 +143,7 @@ def _do_sse_request(self, path, params=None): else: if response.is_redirect and response.next: urls.append(response.next.url) - marathon.log.debug("Got redirect to {}".format(response.next.url)) + marathon.log.debug(f"Got redirect to {response.next.url}") elif response.ok: return response.iter_lines() @@ -257,7 +257,7 @@ def get_app(self, app_id, embed_tasks=False, embed_counts=False, params['embed'] = filtered_embed_params response = self._do_request( - 'GET', '/v2/apps/{app_id}'.format(app_id=app_id), params=params) + 'GET', f'/v2/apps/{app_id}', params=params) return self._parse_response(response, MarathonApp, resource_name='app') def restart_app(self, app_id, force=False): @@ -270,7 +270,7 @@ def restart_app(self, app_id, force=False): """ params = {'force': force} response = self._do_request( - 'POST', '/v2/apps/{app_id}/restart'.format(app_id=app_id), params=params) + 'POST', f'/v2/apps/{app_id}/restart', params=params) return response.json() def update_app(self, app_id, app, force=False, minimal=True): @@ -295,7 +295,7 @@ def update_app(self, app_id, app, force=False, minimal=True): data = app.to_json(minimal=minimal) response = self._do_request( - 'PUT', '/v2/apps/{app_id}'.format(app_id=app_id), params=params, data=data) + 'PUT', f'/v2/apps/{app_id}', params=params, data=data) return response.json() def update_apps(self, apps, force=False, minimal=True): @@ -337,7 +337,7 @@ def rollback_app(self, app_id, version, force=False): params = {'force': force} data = json.dumps({'version': version}) response = self._do_request( - 'PUT', '/v2/apps/{app_id}'.format(app_id=app_id), params=params, data=data) + 'PUT', f'/v2/apps/{app_id}', params=params, data=data) return response.json() def delete_app(self, app_id, force=False): @@ -351,7 +351,7 @@ def delete_app(self, app_id, force=False): """ params = {'force': force} response = self._do_request( - 'DELETE', '/v2/apps/{app_id}'.format(app_id=app_id), params=params) + 'DELETE', f'/v2/apps/{app_id}', params=params) return response.json() def scale_app(self, app_id, instances=None, delta=None, force=False): @@ -378,7 +378,7 @@ def scale_app(self, app_id, instances=None, delta=None, force=False): try: app = self.get_app(app_id) except NotFoundError: - marathon.log.error('App "{app}" not found'.format(app=app_id)) + marathon.log.error(f'App "{app_id}" not found') return desired = instances if instances is not None else ( @@ -421,7 +421,7 @@ def get_group(self, group_id): :rtype: :class:`marathon.models.group.MarathonGroup` """ response = self._do_request( - 'GET', '/v2/groups/{group_id}'.format(group_id=group_id)) + 'GET', f'/v2/groups/{group_id}') return self._parse_response(response, MarathonGroup) def update_group(self, group_id, group, force=False, minimal=True): @@ -446,7 +446,7 @@ def update_group(self, group_id, group, force=False, minimal=True): data = group.to_json(minimal=minimal) response = self._do_request( - 'PUT', '/v2/groups/{group_id}'.format(group_id=group_id), data=data, params=params) + 'PUT', f'/v2/groups/{group_id}', data=data, params=params) return response.json() def rollback_group(self, group_id, version, force=False): @@ -478,7 +478,7 @@ def delete_group(self, group_id, force=False): """ params = {'force': force} response = self._do_request( - 'DELETE', '/v2/groups/{group_id}'.format(group_id=group_id), params=params) + 'DELETE', f'/v2/groups/{group_id}', params=params) return response.json() def scale_group(self, group_id, scale_by): @@ -492,7 +492,7 @@ def scale_group(self, group_id, scale_by): """ data = {'scaleBy': scale_by} response = self._do_request( - 'PUT', '/v2/groups/{group_id}'.format(group_id=group_id), data=json.dumps(data)) + 'PUT', f'/v2/groups/{group_id}', data=json.dumps(data)) return response.json() def list_tasks(self, app_id=None, **kwargs): @@ -558,7 +558,7 @@ def batch(iterable, size): if host: params['host'] = host response = self._do_request( - 'DELETE', '/v2/apps/{app_id}/tasks'.format(app_id=app_id), params) + 'DELETE', f'/v2/apps/{app_id}/tasks', params) # Marathon is inconsistent about what type of object it returns on the multi # task deletion endpoint, depending on the version of Marathon. See: # https://github.com/mesosphere/marathon/blob/06a6f763a75fb6d652b4f1660685ae234bd15387/src/main/scala/mesosphere/marathon/api/v2/AppTasksResource.scala#L88-L95 @@ -576,12 +576,12 @@ def batch(iterable, size): # Pause until the tasks have been killed to avoid race # conditions - killed_task_ids = set(t.id for t in killed_tasks) + killed_task_ids = {t.id for t in killed_tasks} running_task_ids = killed_task_ids while killed_task_ids.intersection(running_task_ids): time.sleep(1) - running_task_ids = set( - t.id for t in self.get_app(app_id).tasks) + running_task_ids = { + t.id for t in self.get_app(app_id).tasks} if batch_delay == 0: # Pause until the replacement tasks are healthy @@ -626,7 +626,7 @@ def list_versions(self, app_id): :rtype: list[str] """ response = self._do_request( - 'GET', '/v2/apps/{app_id}/versions'.format(app_id=app_id)) + 'GET', f'/v2/apps/{app_id}/versions') return [version for version in response.json()['versions']] def get_version(self, app_id, version): @@ -715,12 +715,12 @@ def delete_deployment(self, deployment_id, force=False): return {} else: response = self._do_request( - 'DELETE', '/v2/deployments/{deployment}'.format(deployment=deployment_id)) + 'DELETE', f'/v2/deployments/{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) + "DELETE", f'/v2/queue/{app_id}/delay' ) def get_info(self): diff --git a/marathon/exceptions.py b/marathon/exceptions.py index a5ffe73..0889cb9 100644 --- a/marathon/exceptions.py +++ b/marathon/exceptions.py @@ -14,7 +14,7 @@ def __init__(self, response): self.error_message = content.get('message', self.error_message) self.error_details = content.get('details') self.status_code = response.status_code - super(MarathonHttpError, self).__init__(self.__str__()) + super().__init__(self.__str__()) def __repr__(self): return 'MarathonHttpError: HTTP %s returned with message, "%s"' % \ @@ -39,7 +39,7 @@ class ConflictError(MarathonHttpError): class InvalidChoiceError(MarathonError): def __init__(self, param, value, options): - super(InvalidChoiceError, self).__init__( + super().__init__( 'Invalid choice "{value}" for param "{param}". Must be one of {options}'.format( param=param, value=value, options=options ) diff --git a/marathon/models/app.py b/marathon/models/app.py index cadc3d7..0a3dd5f 100644 --- a/marathon/models/app.py +++ b/marathon/models/app.py @@ -6,7 +6,7 @@ from .container import MarathonContainer from .deployment import MarathonDeployment from .task import MarathonTask -from ..util import is_stringy, get_log +from ..util import get_log log = get_log() @@ -216,7 +216,7 @@ def __init__(self, command=None, grace_period_seconds=None, interval_seconds=Non if command is None: self.command = None - elif is_stringy(command): + elif isinstance(command, str): self.command = { "value": command } @@ -226,7 +226,7 @@ def __init__(self, command=None, grace_period_seconds=None, interval_seconds=Non "value": command['value'] } else: - raise ValueError('Invalid command format: {}'.format(command)) + raise ValueError(f'Invalid command format: {command}') self.grace_period_seconds = grace_period_seconds self.interval_seconds = interval_seconds @@ -320,7 +320,7 @@ def __init__(self, unreachable_inactive_after_seconds=None, def from_json(cls, attributes): if attributes == cls.DISABLED: return cls.DISABLED - return super(MarathonUnreachableStrategy, cls).from_json(attributes) + return super().from_json(attributes) class MarathonAppVersionInfo(MarathonObject): @@ -352,7 +352,7 @@ def _to_datetime(self, timestamp): return datetime.strptime(timestamp, fmt) except ValueError: pass - raise ValueError('Unrecognized datetime format: {}'.format(timestamp)) + raise ValueError(f'Unrecognized datetime format: {timestamp}') class MarathonTaskStats(MarathonObject): diff --git a/marathon/models/base.py b/marathon/models/base.py index db77076..39aba17 100644 --- a/marathon/models/base.py +++ b/marathon/models/base.py @@ -4,7 +4,7 @@ from marathon.util import to_camel_case, to_snake_case, MarathonJsonEncoder, MarathonMinimalJsonEncoder -class MarathonObject(object): +class MarathonObject: """Base Marathon object.""" def __repr__(self): @@ -61,7 +61,7 @@ class MarathonResource(MarathonObject): def __repr__(self): if 'id' in list(vars(self).keys()): - return "{clazz}::{id}".format(clazz=self.__class__.__name__, id=self.id) + return f"{self.__class__.__name__}::{self.id}" else: return "{clazz}::{obj}".format(clazz=self.__class__.__name__, obj=self.to_json()) @@ -78,7 +78,7 @@ def __hash__(self): return hash(self.to_json()) def __str__(self): - return "{clazz}::".format(clazz=self.__class__.__name__) + str(self.__dict__) + return f"{self.__class__.__name__}::" + str(self.__dict__) # See: diff --git a/marathon/models/events.py b/marathon/models/events.py index 630e360..a0b3d4d 100644 --- a/marathon/models/events.py +++ b/marathon/models/events.py @@ -206,7 +206,7 @@ def __init__(self): 'pod_deleted_event': MarathonPodDeletedEvent, } - class_to_event = dict((v, k) for k, v in event_to_class.items()) + class_to_event = {v: k for k, v in event_to_class.items()} def process(self, event): event_type = event['eventType'] @@ -214,4 +214,4 @@ def process(self, event): clazz = self.event_to_class[event_type] return clazz.from_json(event) else: - raise MarathonError('Unknown event_type: {}, data: {}'.format(event_type, event)) + raise MarathonError(f'Unknown event_type: {event_type}, data: {event}') diff --git a/marathon/util.py b/marathon/util.py index 571a072..539bb5d 100644 --- a/marathon/util.py +++ b/marathon/util.py @@ -8,17 +8,11 @@ import simplejson as json import re -from ._compat import string_types - def get_log(): return logging.getLogger(__name__.split('.')[0]) -def is_stringy(obj): - return isinstance(obj, string_types) - - class MarathonJsonEncoder(json.JSONEncoder): """Custom JSON encoder for Marathon object serialization.""" @@ -30,7 +24,7 @@ def default(self, obj): if isinstance(obj, datetime.datetime): return obj.strftime('%Y-%m-%dT%H:%M:%S.%fZ') - if isinstance(obj, collections.Iterable) and not is_stringy(obj): + if isinstance(obj, collections.Iterable) and not isinstance(obj, str): try: return {k: self.default(v) for k, v in obj.items()} except AttributeError: @@ -50,7 +44,7 @@ def default(self, obj): if isinstance(obj, datetime.datetime): return obj.strftime('%Y-%m-%dT%H:%M:%S.%fZ') - if isinstance(obj, collections.Iterable) and not is_stringy(obj): + if isinstance(obj, collections.Iterable) and not isinstance(obj, str): try: return {k: self.default(v) for k, v in obj.items() if (v or v in (False, 0))} except AttributeError: diff --git a/setup.py b/setup.py index 2e8edfa..9637850 100755 --- a/setup.py +++ b/setup.py @@ -24,10 +24,9 @@ 'Intended Audience :: System Administrators', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', 'Programming Language :: Python', 'Topic :: Software Development :: Libraries :: Python Modules' ], diff --git a/tests/test_api.py b/tests/test_api.py index cee709e..3b0ec7d 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -34,7 +34,7 @@ def test_get_deployments_pre_1_0(): mock_client = MarathonClient(servers='http://fake_server') actual_deployments = mock_client.list_deployments() expected_deployments = [models.MarathonDeployment( - id=u"fakeid", + id="fakeid", steps=[ [models.MarathonDeploymentAction( action="ScaleApplication", app="/test")]], @@ -42,8 +42,8 @@ def test_get_deployments_pre_1_0(): action="ScaleApplication", app="/test")], current_step=1, total_steps=1, - affected_apps=[u"/test"], - version=u"fakeversion" + affected_apps=["/test"], + version="fakeversion" )] assert expected_deployments == actual_deployments @@ -90,7 +90,7 @@ def test_get_deployments_post_1_0(): mock_client = MarathonClient(servers='http://fake_server') actual_deployments = mock_client.list_deployments() expected_deployments = [models.MarathonDeployment( - id=u"4d2ff4d8-fbe5-4239-a886-f0831ed68d20", + id="4d2ff4d8-fbe5-4239-a886-f0831ed68d20", steps=[ models.MarathonDeploymentStep( actions=[models.MarathonDeploymentAction( @@ -106,8 +106,8 @@ def test_get_deployments_post_1_0(): ], current_step=2, total_steps=2, - affected_apps=[u"/test-trivial-app"], - version=u"2016-04-20T18:00:20.084Z" + affected_apps=["/test-trivial-app"], + version="2016-04-20T18:00:20.084Z" )] # Helpful for tox to see the diff assert expected_deployments[0].__dict__ == actual_deployments[0].__dict__ diff --git a/tests/test_model_app.py b/tests/test_model_app.py index 345aa8c..b86655c 100644 --- a/tests/test_model_app.py +++ b/tests/test_model_app.py @@ -1,5 +1,3 @@ -# encoding: utf-8 - from marathon.models.app import MarathonApp, MarathonAppVersionInfo from datetime import datetime import unittest diff --git a/tests/test_model_event.py b/tests/test_model_event.py index e62c0b3..22453e3 100644 --- a/tests/test_model_event.py +++ b/tests/test_model_event.py @@ -1,5 +1,3 @@ -# encoding: utf-8 - from marathon.models.events import EventFactory, MarathonStatusUpdateEvent from marathon.models.task import MarathonIpAddress import unittest diff --git a/tests/test_model_group.py b/tests/test_model_group.py index fb84c04..e9fb340 100644 --- a/tests/test_model_group.py +++ b/tests/test_model_group.py @@ -1,5 +1,3 @@ -# encoding: utf-8 - from marathon.models.group import MarathonGroup import unittest diff --git a/tests/test_model_object.py b/tests/test_model_object.py index 89795fb..05c6ee4 100644 --- a/tests/test_model_object.py +++ b/tests/test_model_object.py @@ -1,5 +1,3 @@ -# encoding: utf-8 - from marathon.models.base import MarathonObject from marathon.models.base import MarathonResource import unittest diff --git a/tox.ini b/tox.ini index 7841c6e..300705a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,13 +1,13 @@ [tox] passenv = TRAVIS usedevelop=True -envlist={test,itest}-{py27,py33},pep8 +envlist={test,itest}-{py36,py37},pep8 [testenv] passenv = TRAVIS MARATHONVERSION DOCKER_HOST DOCKER_TLS_VERIFY DOCKER_CERT_PATH DOCKER_MACHINE_NAME basepython = - py27: python2.7 - py33: python3 + py36: python3.6 + py37: python3.7 whitelist_externals=/bin/bash skipsdist=True changedir = @@ -25,10 +25,18 @@ commands = itest: ./itest.sh {posargs} [testenv:pep8] -basepython = python2.7 +basepython = python3.6 deps = flake8 commands = flake8 . [flake8] exclude = .tox,*.egg,docs,build,__init__.py max-line-length = 160 + +[testenv:pre-commit] +basepython = python3.7 +deps = + pre-commit>=1.20.0 +commands = + pre-commit install -f --install-hooks + pre-commit run --all-files \ No newline at end of file From 79322b645e2a056e39f01b6beaffa7d2a14fb695 Mon Sep 17 00:00:00 2001 From: Evan Krall Date: Wed, 13 Nov 2019 14:01:33 -0800 Subject: [PATCH 47/73] Refactor all timestamp parsing into one function. --- marathon/models/app.py | 28 ++++------------------------ marathon/models/task.py | 18 ++++++------------ marathon/util.py | 18 ++++++++++++++++++ tests/test_model_app.py | 10 +--------- tests/test_util.py | 10 +++++++++- 5 files changed, 38 insertions(+), 46 deletions(-) diff --git a/marathon/models/app.py b/marathon/models/app.py index 0a3dd5f..00a3d57 100644 --- a/marathon/models/app.py +++ b/marathon/models/app.py @@ -1,5 +1,3 @@ -from datetime import datetime - from ..exceptions import InvalidChoiceError from .base import MarathonResource, MarathonObject, assert_valid_path from .constraint import MarathonConstraint @@ -7,6 +5,7 @@ from .deployment import MarathonDeployment from .task import MarathonTask from ..util import get_log +from ..util import to_datetime log = get_log() @@ -256,8 +255,6 @@ class MarathonTaskFailure(MarathonObject): :param str version: app version with which this task was started """ - DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ' - def __init__(self, app_id=None, host=None, message=None, task_id=None, instance_id=None, slave_id=None, state=None, timestamp=None, version=None): self.app_id = app_id @@ -267,8 +264,7 @@ def __init__(self, app_id=None, host=None, message=None, task_id=None, instance_ self.instance_id = instance_id self.slave_id = slave_id self.state = state - self.timestamp = timestamp if (timestamp is None or isinstance(timestamp, datetime)) \ - else datetime.strptime(timestamp, self.DATETIME_FORMAT) + self.timestamp = to_datetime(timestamp) self.version = version @@ -334,25 +330,9 @@ class MarathonAppVersionInfo(MarathonObject): :param str host: mesos slave running the task """ - 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) - self.last_config_change_at = self._to_datetime(last_config_change_at) - - def _to_datetime(self, timestamp): - if (timestamp is None or isinstance(timestamp, datetime)): - return timestamp - else: - for fmt in self.DATETIME_FORMATS: - try: - return datetime.strptime(timestamp, fmt) - except ValueError: - pass - raise ValueError(f'Unrecognized datetime format: {timestamp}') + self.last_scaling_at = to_datetime(last_scaling_at) + self.last_config_change_at = to_datetime(last_config_change_at) class MarathonTaskStats(MarathonObject): diff --git a/marathon/models/task.py b/marathon/models/task.py index c756e12..e049748 100644 --- a/marathon/models/task.py +++ b/marathon/models/task.py @@ -1,6 +1,5 @@ -from datetime import datetime - from .base import MarathonResource, MarathonObject +from ..util import to_datetime class MarathonTask(MarathonResource): @@ -44,10 +43,8 @@ def __init__(self, app_id=None, health_check_results=None, host=None, id=None, p self.ports = ports or [] self.service_ports = service_ports or [] self.slave_id = slave_id - self.staged_at = staged_at if (staged_at is None or isinstance(staged_at, datetime)) \ - else datetime.strptime(staged_at, self.DATETIME_FORMAT) - self.started_at = started_at if (started_at is None or isinstance(started_at, datetime)) \ - else datetime.strptime(started_at, self.DATETIME_FORMAT) + self.staged_at = to_datetime(staged_at) + self.started_at = to_datetime(started_at) self.state = state self.version = version self.ip_addresses = [ @@ -90,12 +87,9 @@ def __init__(self, alive=None, consecutive_failures=None, first_success=None, last_failure_cause=None, instance_id=None): self.alive = alive self.consecutive_failures = consecutive_failures - self.first_success = first_success if (first_success is None or isinstance(first_success, datetime)) \ - else datetime.strptime(first_success, self.DATETIME_FORMAT) - self.last_failure = last_failure if (last_failure is None or isinstance(last_failure, datetime)) \ - else datetime.strptime(last_failure, self.DATETIME_FORMAT) - self.last_success = last_success if (last_success is None or isinstance(last_success, datetime)) \ - else datetime.strptime(last_success, self.DATETIME_FORMAT) + self.first_success = to_datetime(first_success) + self.last_failure = to_datetime(last_failure) + self.last_success = to_datetime(last_success) self.task_id = task_id self.last_failure_cause = last_failure_cause self.instance_id = instance_id diff --git a/marathon/util.py b/marathon/util.py index 539bb5d..722aa5b 100644 --- a/marathon/util.py +++ b/marathon/util.py @@ -61,3 +61,21 @@ def to_camel_case(snake_str): def to_snake_case(camel_str): s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', camel_str) return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() + + +DATETIME_FORMATS = [ + '%Y-%m-%dT%H:%M:%S.%fZ', + '%Y-%m-%dT%H:%M:%SZ', # Marathon omits milliseconds when they would be .000 +] + + +def to_datetime(timestamp): + if (timestamp is None or isinstance(timestamp, datetime.datetime)): + return timestamp + else: + for fmt in DATETIME_FORMATS: + try: + return datetime.datetime.strptime(timestamp, fmt) + except ValueError: + pass + raise ValueError(f'Unrecognized datetime format: {timestamp}') diff --git a/tests/test_model_app.py b/tests/test_model_app.py index b86655c..b211efe 100644 --- a/tests/test_model_app.py +++ b/tests/test_model_app.py @@ -1,5 +1,4 @@ -from marathon.models.app import MarathonApp, MarathonAppVersionInfo -from datetime import datetime +from marathon.models.app import MarathonApp import unittest @@ -23,10 +22,3 @@ def test_add_env_non_empty_dict(self): app.add_env("MY_ENV", "my-value") self.assertDictEqual({"MY_ENV": "my-value", "OTHER_ENV": "other-value"}, app.env) - - def test_version_info_datetime(self): - app_ver_info = MarathonAppVersionInfo() - self.assertEquals(app_ver_info._to_datetime("2017-09-28T00:31:55Z"), datetime(2017, 9, 28, 0, 31, 55)) - self.assertEquals(app_ver_info._to_datetime("2017-09-28T00:31:55.4Z"), datetime(2017, 9, 28, 0, 31, 55, 400000)) - self.assertEquals(app_ver_info._to_datetime("2017-09-28T00:31:55.004Z"), datetime(2017, 9, 28, 0, 31, 55, 4000)) - self.assertEquals(app_ver_info._to_datetime("2017-09-28T00:31:55.00042Z"), datetime(2017, 9, 28, 0, 31, 55, 420)) diff --git a/tests/test_util.py b/tests/test_util.py index 8956051..6ada31c 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -1,4 +1,5 @@ -from marathon.util import to_camel_case, to_snake_case +from datetime import datetime +from marathon.util import to_camel_case, to_snake_case, to_datetime def _apply_on_pairs(f): @@ -27,3 +28,10 @@ def test(camel, snake): assert to_snake_case(camel) == snake _apply_on_pairs(test) + + +def test_version_info_datetime(): + assert to_datetime("2017-09-28T00:31:55Z") == datetime(2017, 9, 28, 0, 31, 55) + assert to_datetime("2017-09-28T00:31:55.4Z") == datetime(2017, 9, 28, 0, 31, 55, 400000) + assert to_datetime("2017-09-28T00:31:55.004Z") == datetime(2017, 9, 28, 0, 31, 55, 4000) + assert to_datetime("2017-09-28T00:31:55.00042Z") == datetime(2017, 9, 28, 0, 31, 55, 420) From ea6e198ff8c13bcbab7d4db37f70af5330604a88 Mon Sep 17 00:00:00 2001 From: Evan Krall Date: Wed, 13 Nov 2019 14:07:19 -0800 Subject: [PATCH 48/73] Always create tz-aware datetime objects. --- marathon/util.py | 2 +- tests/test_util.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/marathon/util.py b/marathon/util.py index 722aa5b..d9f5664 100644 --- a/marathon/util.py +++ b/marathon/util.py @@ -75,7 +75,7 @@ def to_datetime(timestamp): else: for fmt in DATETIME_FORMATS: try: - return datetime.datetime.strptime(timestamp, fmt) + return datetime.datetime.strptime(timestamp, fmt).replace(tzinfo=datetime.timezone.utc) except ValueError: pass raise ValueError(f'Unrecognized datetime format: {timestamp}') diff --git a/tests/test_util.py b/tests/test_util.py index 6ada31c..2a967ba 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timezone from marathon.util import to_camel_case, to_snake_case, to_datetime @@ -31,7 +31,7 @@ def test(camel, snake): def test_version_info_datetime(): - assert to_datetime("2017-09-28T00:31:55Z") == datetime(2017, 9, 28, 0, 31, 55) - assert to_datetime("2017-09-28T00:31:55.4Z") == datetime(2017, 9, 28, 0, 31, 55, 400000) - assert to_datetime("2017-09-28T00:31:55.004Z") == datetime(2017, 9, 28, 0, 31, 55, 4000) - assert to_datetime("2017-09-28T00:31:55.00042Z") == datetime(2017, 9, 28, 0, 31, 55, 420) + assert to_datetime("2017-09-28T00:31:55Z") == datetime(2017, 9, 28, 0, 31, 55, tzinfo=timezone.utc) + assert to_datetime("2017-09-28T00:31:55.4Z") == datetime(2017, 9, 28, 0, 31, 55, 400000, tzinfo=timezone.utc) + assert to_datetime("2017-09-28T00:31:55.004Z") == datetime(2017, 9, 28, 0, 31, 55, 4000, tzinfo=timezone.utc) + assert to_datetime("2017-09-28T00:31:55.00042Z") == datetime(2017, 9, 28, 0, 31, 55, 420, tzinfo=timezone.utc) From fdf0f38287469919bb193524ff5a760bebcf83a3 Mon Sep 17 00:00:00 2001 From: Kyle Anderson Date: Wed, 13 Nov 2019 17:22:06 -0800 Subject: [PATCH 49/73] Release 0.12.0 --- CHANGELOG.md | 87 ++++++++++++++++++++++++++++++++++++++++++++++------ setup.py | 2 +- 2 files changed, 78 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ded5397..8f9e928 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,20 @@ -# Change Log +# Changelog + +## [Unreleased](https://github.com/thefactory/marathon-python/tree/HEAD) + +[Full Changelog](https://github.com/thefactory/marathon-python/compare/0.11.0...HEAD) + +**Closed issues:** + +- Downloading Log For App [\#265](https://github.com/thefactory/marathon-python/issues/265) + +**Merged pull requests:** + +- Always create TZ-aware datetime objects. \(also drop support for python 2\) [\#267](https://github.com/thefactory/marathon-python/pull/267) ([EvanKrall](https://github.com/EvanKrall)) +- Compatible with event stream redirect behavior. [\#262](https://github.com/thefactory/marathon-python/pull/262) ([fengyehong](https://github.com/fengyehong)) ## [0.11.0](https://github.com/thefactory/marathon-python/tree/0.11.0) (2019-01-15) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.10.0...0.11.0) **Merged pull requests:** @@ -10,6 +24,7 @@ - New release 0.10.0 [\#256](https://github.com/thefactory/marathon-python/pull/256) ([solarkennedy](https://github.com/solarkennedy)) ## [0.10.0](https://github.com/thefactory/marathon-python/tree/0.10.0) (2018-08-08) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.9.3...0.10.0) **Closed issues:** @@ -38,6 +53,7 @@ - Removes id validation from MarathonGroup\(\) [\#228](https://github.com/thefactory/marathon-python/pull/228) ([daltonmatos](https://github.com/daltonmatos)) ## [0.9.3](https://github.com/thefactory/marathon-python/tree/0.9.3) (2017-10-16) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.9.2...0.9.3) **Closed issues:** @@ -54,6 +70,7 @@ - Make MarathonZooKeeperConfig compatible with maraton 1.5 [\#216](https://github.com/thefactory/marathon-python/pull/216) ([fengyehong](https://github.com/fengyehong)) ## [0.9.2](https://github.com/thefactory/marathon-python/tree/0.9.2) (2017-09-13) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.9.1...0.9.2) **Closed issues:** @@ -68,6 +85,7 @@ - Fix events [\#214](https://github.com/thefactory/marathon-python/pull/214) ([fengyehong](https://github.com/fengyehong)) ## [0.9.1](https://github.com/thefactory/marathon-python/tree/0.9.1) (2017-09-06) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.9.0...0.9.1) **Closed issues:** @@ -87,6 +105,7 @@ - Fix MarathonResource hash as well [\#205](https://github.com/thefactory/marathon-python/pull/205) ([jolynch](https://github.com/jolynch)) ## [0.9.0](https://github.com/thefactory/marathon-python/tree/0.9.0) (2017-06-21) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.8.14...0.9.0) **Closed issues:** @@ -111,8 +130,13 @@ - Remove out of date constraint validation of operator. [\#190](https://github.com/thefactory/marathon-python/pull/190) ([akatrevorjay](https://github.com/akatrevorjay)) - Add raw\_data option for event\_stream method [\#189](https://github.com/thefactory/marathon-python/pull/189) ([fengyehong](https://github.com/fengyehong)) - handle case when non-ascii char are logged [\#188](https://github.com/thefactory/marathon-python/pull/188) ([tgermain](https://github.com/tgermain)) +- \[fix\] util.to\_camel\_case doesn't handle digits [\#184](https://github.com/thefactory/marathon-python/pull/184) ([hlerebours](https://github.com/hlerebours)) +- \[fix\] broken build: glibc++ not found [\#183](https://github.com/thefactory/marathon-python/pull/183) ([hlerebours](https://github.com/hlerebours)) +- Support for "disabled" unreachableStrategy. [\#182](https://github.com/thefactory/marathon-python/pull/182) ([nihn](https://github.com/nihn)) +- \[fix\] Handle non-JSON errors from Marathon [\#178](https://github.com/thefactory/marathon-python/pull/178) ([hlerebours](https://github.com/hlerebours)) ## [0.8.14](https://github.com/thefactory/marathon-python/tree/0.8.14) (2017-03-24) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.8.13...0.8.14) **Closed issues:** @@ -122,14 +146,8 @@ - ignoreHttp1xx or ignoreHttp1Xx [\#125](https://github.com/thefactory/marathon-python/issues/125) - ValueError when 401 Unauthorized is received [\#22](https://github.com/thefactory/marathon-python/issues/22) -**Merged pull requests:** - -- \[fix\] util.to\_camel\_case doesn't handle digits [\#184](https://github.com/thefactory/marathon-python/pull/184) ([hlerebours](https://github.com/hlerebours)) -- \[fix\] broken build: glibc++ not found [\#183](https://github.com/thefactory/marathon-python/pull/183) ([hlerebours](https://github.com/hlerebours)) -- Support for "disabled" unreachableStrategy. [\#182](https://github.com/thefactory/marathon-python/pull/182) ([nihn](https://github.com/nihn)) -- \[fix\] Handle non-JSON errors from Marathon [\#178](https://github.com/thefactory/marathon-python/pull/178) ([hlerebours](https://github.com/hlerebours)) - ## [0.8.13](https://github.com/thefactory/marathon-python/tree/0.8.13) (2017-03-17) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.8.12...0.8.13) **Merged pull requests:** @@ -137,6 +155,7 @@ - Support processed\_offers\_summary attribute [\#177](https://github.com/thefactory/marathon-python/pull/177) ([nhandler](https://github.com/nhandler)) ## [0.8.12](https://github.com/thefactory/marathon-python/tree/0.8.12) (2017-03-17) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.8.11...0.8.12) **Closed issues:** @@ -151,6 +170,7 @@ - Updated event.py to handle app\_terminated\_event. [\#171](https://github.com/thefactory/marathon-python/pull/171) ([Jbrownstone](https://github.com/Jbrownstone)) ## [0.8.11](https://github.com/thefactory/marathon-python/tree/0.8.11) (2017-02-22) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.8.10...0.8.11) **Merged pull requests:** @@ -160,6 +180,7 @@ - Adds MarathonApp.add\_env\(\) method [\#166](https://github.com/thefactory/marathon-python/pull/166) ([daltonmatos](https://github.com/daltonmatos)) ## [0.8.10](https://github.com/thefactory/marathon-python/tree/0.8.10) (2017-01-07) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.8.9...0.8.10) **Closed issues:** @@ -173,6 +194,7 @@ - Add new Marathon 1.4 API keywords [\#162](https://github.com/thefactory/marathon-python/pull/162) ([stj](https://github.com/stj)) ## [0.8.9](https://github.com/thefactory/marathon-python/tree/0.8.9) (2016-12-15) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.8.8...0.8.9) **Closed issues:** @@ -184,6 +206,7 @@ - Added more unimplemented Marathon 1.4 API keywords [\#161](https://github.com/thefactory/marathon-python/pull/161) ([solarkennedy](https://github.com/solarkennedy)) ## [0.8.8](https://github.com/thefactory/marathon-python/tree/0.8.8) (2016-12-09) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.8.7...0.8.8) **Closed issues:** @@ -196,6 +219,7 @@ - Expose error details from response object MarathonHttpError [\#157](https://github.com/thefactory/marathon-python/pull/157) ([moonkev](https://github.com/moonkev)) ## [0.8.7](https://github.com/thefactory/marathon-python/tree/0.8.7) (2016-10-24) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.8.6...0.8.7) **Closed issues:** @@ -213,6 +237,7 @@ - Add external volume support [\#146](https://github.com/thefactory/marathon-python/pull/146) ([drewrobb](https://github.com/drewrobb)) ## [0.8.6](https://github.com/thefactory/marathon-python/tree/0.8.6) (2016-08-29) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.8.5...0.8.6) **Closed issues:** @@ -230,6 +255,7 @@ - Add support for unhealthy\_task\_kill\_event [\#137](https://github.com/thefactory/marathon-python/pull/137) ([nuclon](https://github.com/nuclon)) ## [0.8.5](https://github.com/thefactory/marathon-python/tree/0.8.5) (2016-08-10) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.8.4...0.8.5) **Closed issues:** @@ -244,6 +270,7 @@ - Marathon 1.1.2 and Mesos 1.0.\* [\#134](https://github.com/thefactory/marathon-python/pull/134) ([nhandler](https://github.com/nhandler)) ## [0.8.4](https://github.com/thefactory/marathon-python/tree/0.8.4) (2016-07-20) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.8.3...0.8.4) **Closed issues:** @@ -256,6 +283,7 @@ - Expose id query param in list\_apps [\#129](https://github.com/thefactory/marathon-python/pull/129) ([moonkev](https://github.com/moonkev)) ## [0.8.3](https://github.com/thefactory/marathon-python/tree/0.8.3) (2016-07-19) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.8.2...0.8.3) **Closed issues:** @@ -285,6 +313,7 @@ - Add message field for MarathonStatusUpdateEvent. [\#109](https://github.com/thefactory/marathon-python/pull/109) ([oilbeater](https://github.com/oilbeater)) ## [0.8.2](https://github.com/thefactory/marathon-python/tree/0.8.2) (2016-06-14) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.8.1...0.8.2) **Closed issues:** @@ -304,6 +333,7 @@ - add name attribute to port mapping [\#101](https://github.com/thefactory/marathon-python/pull/101) ([Rob-Johnson](https://github.com/Rob-Johnson)) ## [0.8.1](https://github.com/thefactory/marathon-python/tree/0.8.1) (2016-04-21) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.8.0...0.8.1) **Closed issues:** @@ -318,6 +348,7 @@ - Support the deployments endpoint correctly in marathon 1.1.1 [\#95](https://github.com/thefactory/marathon-python/pull/95) ([solarkennedy](https://github.com/solarkennedy)) ## [0.8.0](https://github.com/thefactory/marathon-python/tree/0.8.0) (2016-04-18) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.7.7...0.8.0) **Closed issues:** @@ -332,6 +363,7 @@ - update for v2/queue and v2/apps?embed=apps.taskStats [\#89](https://github.com/thefactory/marathon-python/pull/89) ([bergerx](https://github.com/bergerx)) ## [0.7.7](https://github.com/thefactory/marathon-python/tree/0.7.7) (2016-02-29) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.7.6...0.7.7) **Merged pull requests:** @@ -340,6 +372,7 @@ - a small fix for fetching apps for marathon v0.15 [\#87](https://github.com/thefactory/marathon-python/pull/87) ([burakbostancioglu](https://github.com/burakbostancioglu)) ## [0.7.6](https://github.com/thefactory/marathon-python/tree/0.7.6) (2016-02-12) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.7.5...0.7.6) **Closed issues:** @@ -359,23 +392,26 @@ - Use the /v2/tasks/delete endpoint for taskkill [\#67](https://github.com/thefactory/marathon-python/pull/67) ([fengyehong](https://github.com/fengyehong)) ## [0.7.5](https://github.com/thefactory/marathon-python/tree/0.7.5) (2015-12-09) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.7.4...0.7.5) **Merged pull requests:** - Release 0.7.5 for official Marathon 11 support [\#73](https://github.com/thefactory/marathon-python/pull/73) ([solarkennedy](https://github.com/solarkennedy)) - Added tests for killing tasks on an app [\#72](https://github.com/thefactory/marathon-python/pull/72) ([solarkennedy](https://github.com/solarkennedy)) +- Use automatic changelog generation [\#69](https://github.com/thefactory/marathon-python/pull/69) ([solarkennedy](https://github.com/solarkennedy)) - Provide proper compatability support for str/unicode in py3 [\#57](https://github.com/thefactory/marathon-python/pull/57) ([mattrobenolt](https://github.com/mattrobenolt)) ## [0.7.4](https://github.com/thefactory/marathon-python/tree/0.7.4) (2015-11-20) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.7.3...0.7.4) **Merged pull requests:** -- Use automatic changelog generation [\#69](https://github.com/thefactory/marathon-python/pull/69) ([solarkennedy](https://github.com/solarkennedy)) - Marathon 11 Support [\#68](https://github.com/thefactory/marathon-python/pull/68) ([solarkennedy](https://github.com/solarkennedy)) ## [0.7.3](https://github.com/thefactory/marathon-python/tree/0.7.3) (2015-11-12) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.7.2...0.7.3) **Closed issues:** @@ -389,6 +425,7 @@ - Remove call to logging.basicConfig [\#64](https://github.com/thefactory/marathon-python/pull/64) ([itamaro](https://github.com/itamaro)) ## [0.7.2](https://github.com/thefactory/marathon-python/tree/0.7.2) (2015-09-18) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.7.0...0.7.2) **Closed issues:** @@ -412,6 +449,7 @@ - First pass at adding an itest framework [\#42](https://github.com/thefactory/marathon-python/pull/42) ([solarkennedy](https://github.com/solarkennedy)) ## [0.7.0](https://github.com/thefactory/marathon-python/tree/0.7.0) (2015-07-06) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.6.15...0.7.0) **Closed issues:** @@ -427,6 +465,7 @@ - Feature/event factory [\#32](https://github.com/thefactory/marathon-python/pull/32) ([kevinschoon](https://github.com/kevinschoon)) ## [0.6.15](https://github.com/thefactory/marathon-python/tree/0.6.15) (2015-06-05) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.6.14...0.6.15) **Merged pull requests:** @@ -434,6 +473,7 @@ - Make `force\_pull\_image` actually work [\#33](https://github.com/thefactory/marathon-python/pull/33) ([mattrobenolt](https://github.com/mattrobenolt)) ## [0.6.14](https://github.com/thefactory/marathon-python/tree/0.6.14) (2015-05-28) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.6.13...0.6.14) **Closed issues:** @@ -450,6 +490,7 @@ - Fixed \#26:Using try/except to get rid of use\_2to3 failing [\#27](https://github.com/thefactory/marathon-python/pull/27) ([vitan](https://github.com/vitan)) ## [0.6.13](https://github.com/thefactory/marathon-python/tree/0.6.13) (2015-03-24) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.6.12...0.6.13) **Merged pull requests:** @@ -460,9 +501,11 @@ - Possibility to send the full object to Marathon on update [\#20](https://github.com/thefactory/marathon-python/pull/20) ([wndhydrnt](https://github.com/wndhydrnt)) ## [0.6.12](https://github.com/thefactory/marathon-python/tree/0.6.12) (2015-03-07) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.6.11...0.6.12) ## [0.6.11](https://github.com/thefactory/marathon-python/tree/0.6.11) (2015-03-06) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.6.10...0.6.11) **Merged pull requests:** @@ -470,6 +513,7 @@ - Small changes to fix compatibility issues with Marathon 0.8.0 [\#19](https://github.com/thefactory/marathon-python/pull/19) ([cloudify](https://github.com/cloudify)) ## [0.6.10](https://github.com/thefactory/marathon-python/tree/0.6.10) (2014-12-17) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.6.8...0.6.10) **Merged pull requests:** @@ -479,9 +523,11 @@ - apparently undocumented API in Marathon [\#16](https://github.com/thefactory/marathon-python/pull/16) ([elyast](https://github.com/elyast)) ## [0.6.8](https://github.com/thefactory/marathon-python/tree/0.6.8) (2014-11-19) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.6.7...0.6.8) ## [0.6.7](https://github.com/thefactory/marathon-python/tree/0.6.7) (2014-11-18) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.6.6...0.6.7) **Closed issues:** @@ -493,6 +539,7 @@ - fixing issues with resources /v2/tasks, v2/info [\#15](https://github.com/thefactory/marathon-python/pull/15) ([elyast](https://github.com/elyast)) ## [0.6.6](https://github.com/thefactory/marathon-python/tree/0.6.6) (2014-11-17) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.6.5...0.6.6) **Closed issues:** @@ -500,9 +547,11 @@ - scale\_app\(...\) calls update\_app\(...\) with only 1 argument [\#13](https://github.com/thefactory/marathon-python/issues/13) ## [0.6.5](https://github.com/thefactory/marathon-python/tree/0.6.5) (2014-11-14) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.6.4...0.6.5) ## [0.6.4](https://github.com/thefactory/marathon-python/tree/0.6.4) (2014-11-14) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.6.3...0.6.4) **Merged pull requests:** @@ -510,6 +559,7 @@ - Add MarathonHealthCheckResult Class to tasks File and Include it in MarathonTask [\#12](https://github.com/thefactory/marathon-python/pull/12) ([JTCunning](https://github.com/JTCunning)) ## [0.6.3](https://github.com/thefactory/marathon-python/tree/0.6.3) (2014-10-10) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.6.2...0.6.3) **Merged pull requests:** @@ -517,6 +567,7 @@ - add service\_port argument [\#11](https://github.com/thefactory/marathon-python/pull/11) ([danielfrg](https://github.com/danielfrg)) ## [0.6.2](https://github.com/thefactory/marathon-python/tree/0.6.2) (2014-10-09) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.6.1...0.6.2) **Merged pull requests:** @@ -524,9 +575,11 @@ - Add `LIKE` and `UNLIKE` constraint [\#10](https://github.com/thefactory/marathon-python/pull/10) ([iven](https://github.com/iven)) ## [0.6.1](https://github.com/thefactory/marathon-python/tree/0.6.1) (2014-09-29) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.6.0...0.6.1) ## [0.6.0](https://github.com/thefactory/marathon-python/tree/0.6.0) (2014-09-29) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.5.1...0.6.0) **Closed issues:** @@ -534,9 +587,11 @@ - Support for HA nodes [\#8](https://github.com/thefactory/marathon-python/issues/8) ## [0.5.1](https://github.com/thefactory/marathon-python/tree/0.5.1) (2014-09-18) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.5.0...0.5.1) ## [0.5.0](https://github.com/thefactory/marathon-python/tree/0.5.0) (2014-09-18) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.4.0...0.5.0) **Merged pull requests:** @@ -544,6 +599,7 @@ - Bug Fix: Cannot define constraints with a tuple of strings [\#6](https://github.com/thefactory/marathon-python/pull/6) ([adgaudio](https://github.com/adgaudio)) ## [0.4.0](https://github.com/thefactory/marathon-python/tree/0.4.0) (2014-08-19) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.3.1...0.4.0) **Merged pull requests:** @@ -552,6 +608,7 @@ - Fix container options not being sent to marathon [\#4](https://github.com/thefactory/marathon-python/pull/4) ([boffbowsh](https://github.com/boffbowsh)) ## [0.3.1](https://github.com/thefactory/marathon-python/tree/0.3.1) (2014-08-05) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.2.9...0.3.1) **Merged pull requests:** @@ -559,12 +616,15 @@ - Raise exceptions instead of swallowing them silently [\#3](https://github.com/thefactory/marathon-python/pull/3) ([StephanErb](https://github.com/StephanErb)) ## [0.2.9](https://github.com/thefactory/marathon-python/tree/0.2.9) (2014-08-04) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.2.7...0.2.9) ## [0.2.7](https://github.com/thefactory/marathon-python/tree/0.2.7) (2014-07-24) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.2.6...0.2.7) ## [0.2.6](https://github.com/thefactory/marathon-python/tree/0.2.6) (2014-07-24) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.2.5...0.2.6) **Merged pull requests:** @@ -572,6 +632,7 @@ - Updated README.md with correction to create\_app args [\#2](https://github.com/thefactory/marathon-python/pull/2) ([rasathus](https://github.com/rasathus)) ## [0.2.5](https://github.com/thefactory/marathon-python/tree/0.2.5) (2014-07-02) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.2.3...0.2.5) **Merged pull requests:** @@ -579,15 +640,21 @@ - allowing stagedAt and startedAt keys to be null [\#1](https://github.com/thefactory/marathon-python/pull/1) ([Codeacious](https://github.com/Codeacious)) ## [0.2.3](https://github.com/thefactory/marathon-python/tree/0.2.3) (2014-06-02) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.2.0...0.2.3) ## [0.2.0](https://github.com/thefactory/marathon-python/tree/0.2.0) (2014-04-28) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.1.1...0.2.0) ## [0.1.1](https://github.com/thefactory/marathon-python/tree/0.1.1) (2014-04-23) + [Full Changelog](https://github.com/thefactory/marathon-python/compare/0.1.0...0.1.1) ## [0.1.0](https://github.com/thefactory/marathon-python/tree/0.1.0) (2014-04-23) +[Full Changelog](https://github.com/thefactory/marathon-python/compare/8060b138250686d1fe2f79d4d5118fef39aa553e...0.1.0) + + -\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* \ No newline at end of file +\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* diff --git a/setup.py b/setup.py index 9637850..8adc7f0 100755 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setup( name='marathon', - version='0.11.0', + version='0.12.0', description='Marathon Client Library', long_description="""Python interface to the Mesos Marathon REST API.""", author='Mike Babineau', From 7a16250db8a61e350dcf63cf381e50d7d9a684a0 Mon Sep 17 00:00:00 2001 From: "Tilian R. Honig" Date: Wed, 15 Jan 2020 20:06:34 +0100 Subject: [PATCH 50/73] Fix return value for kill_given_tasks. --- marathon/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/marathon/client.py b/marathon/client.py index 25782db..8e61973 100644 --- a/marathon/client.py +++ b/marathon/client.py @@ -531,7 +531,7 @@ def kill_given_tasks(self, task_ids, scale=False, force=None): data = json.dumps({"ids": task_ids}) response = self._do_request( 'POST', '/v2/tasks/delete', params=params, data=data) - return response == 200 + return response.status_code == 200 def kill_tasks(self, app_id, scale=False, wipe=False, host=None, batch_size=0, batch_delay=0): From d8992e4753453623035ee892f37d83b47e6713b7 Mon Sep 17 00:00:00 2001 From: Ricardo Rosales Date: Wed, 4 Mar 2020 18:27:20 -0600 Subject: [PATCH 51/73] Added role to task model --- marathon/models/task.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/marathon/models/task.py b/marathon/models/task.py index e049748..0348c6a 100644 --- a/marathon/models/task.py +++ b/marathon/models/task.py @@ -24,13 +24,15 @@ class MarathonTask(MarathonResource): :param region: fault domain region support in DCOS EE :type zone: str :param zone: fault domain zone support in DCOS EE + :type role: str + :param role: mesos role """ 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, - region=None, zone=None): + region=None, zone=None, role=None): self.app_id = app_id self.health_check_results = health_check_results or [] self.health_check_results = [ @@ -54,6 +56,7 @@ def __init__(self, app_id=None, health_check_results=None, host=None, id=None, p self.local_volumes = local_volumes or [] self.region = region self.zone = zone + self.role = role class MarathonIpAddress(MarathonObject): From f4b32d372c10b492cf1108cc93ea8d03f66a914d Mon Sep 17 00:00:00 2001 From: Ricardo Rosales Date: Wed, 4 Mar 2020 19:23:31 -0600 Subject: [PATCH 52/73] Adding role to app model --- marathon/models/app.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/marathon/models/app.py b/marathon/models/app.py index 00a3d57..e290fd9 100644 --- a/marathon/models/app.py +++ b/marathon/models/app.py @@ -38,6 +38,7 @@ class MarathonApp(MarathonResource): :param health_checks: health checks :type health_checks: list[:class:`marathon.models.MarathonHealthCheck`] or list[dict] :param str id: app id + :param str role: mesos role :param int instances: instances :param last_task_failure: last task failure :type last_task_failure: :class:`marathon.models.app.MarathonTaskFailure` or dict @@ -75,7 +76,7 @@ class MarathonApp(MarathonResource): 'args', 'backoff_factor', 'backoff_seconds', 'cmd', 'constraints', 'container', 'cpus', 'dependencies', 'disk', 'env', 'executor', 'gpus', 'health_checks', 'instances', 'kill_selection', 'labels', 'max_launch_delay_seconds', 'mem', 'ports', 'require_ports', 'store_urls', 'task_rate_limit', 'upgrade_strategy', 'unreachable_strategy', - 'uris', 'user', 'version' + 'uris', 'user', 'version', 'role' ] """List of attributes which may be updated/changed after app creation""" @@ -90,7 +91,7 @@ class MarathonApp(MarathonResource): def __init__(self, accepted_resource_roles=None, args=None, backoff_factor=None, backoff_seconds=None, cmd=None, constraints=None, container=None, cpus=None, dependencies=None, deployments=None, disk=None, env=None, - executor=None, health_checks=None, id=None, instances=None, kill_selection=None, labels=None, + executor=None, health_checks=None, id=None, role=None, instances=None, kill_selection=None, labels=None, last_task_failure=None, max_launch_delay_seconds=None, mem=None, ports=None, require_ports=None, store_urls=None, task_rate_limit=None, tasks=None, tasks_running=None, tasks_staged=None, tasks_healthy=None, task_kill_grace_period_seconds=None, tasks_unhealthy=None, upgrade_strategy=None, @@ -131,6 +132,7 @@ def __init__(self, accepted_resource_roles=None, args=None, backoff_factor=None, for hc in (health_checks or []) ] self.id = assert_valid_path(id) + self.role = role self.instances = instances if kill_selection and kill_selection not in self.KILL_SELECTIONS: raise InvalidChoiceError( From 08e8350d90e1a0d6cacd38f9d3a2e650079d2ca1 Mon Sep 17 00:00:00 2001 From: Ricardo Rosales Date: Thu, 5 Mar 2020 16:01:15 -0600 Subject: [PATCH 53/73] Added debug log when creating app --- marathon/client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/marathon/client.py b/marathon/client.py index 8e61973..9db890c 100644 --- a/marathon/client.py +++ b/marathon/client.py @@ -169,6 +169,7 @@ def create_app(self, app_id, app, minimal=True): """ app.id = app_id data = app.to_json(minimal=minimal) + marathon.log.debug('create app JSON sent: {}'.format(data)) response = self._do_request('POST', '/v2/apps', data=data) if response.status_code == 201: return self._parse_response(response, MarathonApp) From 2b73d7cc373814ab07ed6c341c36a536ac6b1ff8 Mon Sep 17 00:00:00 2001 From: Ricardo Rosales Date: Thu, 5 Mar 2020 16:50:59 -0600 Subject: [PATCH 54/73] Now we validate app id is valid on creation --- marathon/client.py | 3 ++- marathon/models/deployment.py | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/marathon/client.py b/marathon/client.py index 9db890c..26c7914 100644 --- a/marathon/client.py +++ b/marathon/client.py @@ -13,6 +13,7 @@ import marathon from .models import MarathonApp, MarathonDeployment, MarathonGroup, MarathonInfo, MarathonTask, MarathonEndpoint, MarathonQueueItem from .exceptions import ConflictError, InternalServerError, NotFoundError, MarathonHttpError, MarathonError, NoResponseError +from .models.base import assert_valid_path from .models.events import EventFactory, MarathonEvent from .util import MarathonJsonEncoder, MarathonMinimalJsonEncoder @@ -167,7 +168,7 @@ def create_app(self, app_id, app, minimal=True): :returns: the created app (on success) :rtype: :class:`marathon.models.app.MarathonApp` or False """ - app.id = app_id + app.id = assert_valid_path(app_id) data = app.to_json(minimal=minimal) marathon.log.debug('create app JSON sent: {}'.format(data)) response = self._do_request('POST', '/v2/apps', data=data) diff --git a/marathon/models/deployment.py b/marathon/models/deployment.py index 97e73ec..4885689 100644 --- a/marathon/models/deployment.py +++ b/marathon/models/deployment.py @@ -1,4 +1,4 @@ -from .base import MarathonObject, MarathonResource +from .base import MarathonObject, MarathonResource assert_valid_path class MarathonDeployment(MarathonResource): @@ -60,8 +60,8 @@ class MarathonDeploymentAction(MarathonObject): def __init__(self, action=None, app=None, apps=None, type=None, readiness_check_results=None, pod=None): self.action = action - self.app = app - self.apps = apps + self.app = assert_valid_path(app) + self.apps = assert_valid_path(apps) self.pod = pod self.type = type # TODO: Remove builtin shadow self.readiness_check_results = readiness_check_results # TODO: The docs say this is called just "readinessChecks?" From c1f802d92b924e1e96cf965f900e2e9073eb738f Mon Sep 17 00:00:00 2001 From: Ricardo Rosales Date: Thu, 5 Mar 2020 16:52:26 -0600 Subject: [PATCH 55/73] Separating import of assert_valid_path on deployment model --- marathon/models/deployment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/marathon/models/deployment.py b/marathon/models/deployment.py index 4885689..d7ac7cd 100644 --- a/marathon/models/deployment.py +++ b/marathon/models/deployment.py @@ -1,4 +1,4 @@ -from .base import MarathonObject, MarathonResource assert_valid_path +from .base import MarathonObject, MarathonResource, assert_valid_path class MarathonDeployment(MarathonResource): From ec1c153ddb604c0516eedbbbd46d59e13664f5a3 Mon Sep 17 00:00:00 2001 From: Ricardo Rosales Date: Thu, 5 Mar 2020 17:33:02 -0600 Subject: [PATCH 56/73] lower case app id --- marathon/client.py | 2 +- marathon/models/app.py | 2 +- marathon/models/deployment.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/marathon/client.py b/marathon/client.py index 26c7914..150f534 100644 --- a/marathon/client.py +++ b/marathon/client.py @@ -168,7 +168,7 @@ def create_app(self, app_id, app, minimal=True): :returns: the created app (on success) :rtype: :class:`marathon.models.app.MarathonApp` or False """ - app.id = assert_valid_path(app_id) + app.id = assert_valid_path(app_id.lower()) data = app.to_json(minimal=minimal) marathon.log.debug('create app JSON sent: {}'.format(data)) response = self._do_request('POST', '/v2/apps', data=data) diff --git a/marathon/models/app.py b/marathon/models/app.py index e290fd9..ede2341 100644 --- a/marathon/models/app.py +++ b/marathon/models/app.py @@ -131,7 +131,7 @@ def __init__(self, accepted_resource_roles=None, args=None, backoff_factor=None, hc, MarathonHealthCheck) else MarathonHealthCheck().from_json(hc) for hc in (health_checks or []) ] - self.id = assert_valid_path(id) + self.id = assert_valid_path(id.lower()) self.role = role self.instances = instances if kill_selection and kill_selection not in self.KILL_SELECTIONS: diff --git a/marathon/models/deployment.py b/marathon/models/deployment.py index d7ac7cd..c3398d2 100644 --- a/marathon/models/deployment.py +++ b/marathon/models/deployment.py @@ -60,8 +60,8 @@ class MarathonDeploymentAction(MarathonObject): def __init__(self, action=None, app=None, apps=None, type=None, readiness_check_results=None, pod=None): self.action = action - self.app = assert_valid_path(app) - self.apps = assert_valid_path(apps) + self.app = assert_valid_path(app.lower()) + self.apps = assert_valid_path(apps.lower()) self.pod = pod self.type = type # TODO: Remove builtin shadow self.readiness_check_results = readiness_check_results # TODO: The docs say this is called just "readinessChecks?" From d68374ab8a1873492b24a3897090544666519cd4 Mon Sep 17 00:00:00 2001 From: Ricardo Rosales Date: Fri, 15 May 2020 11:27:36 -0500 Subject: [PATCH 57/73] Updating compatibility and tests --- .travis.yml | 1 + README.md | 1 + 2 files changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index c227dfb..8d89ecf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ env: + - MARATHONVERSION: 1.9.109 - MARATHONVERSION: 1.6.322 - MARATHONVERSION: 1.4.11 - MARATHONVERSION: 1.3.0 diff --git a/README.md b/README.md index 592bb92..c109c83 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ This is a Python library for interfacing with [Marathon](https://github.com/meso #### Compatibility +* For Marathon 1.9.x, use at least 0.13.0 * 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 From f215d7c1dc09fdacec5842d910252dcb53bf9b1d Mon Sep 17 00:00:00 2001 From: Ricardo Rosales Date: Wed, 20 May 2020 11:20:26 -0500 Subject: [PATCH 58/73] Trying without app.lower and apps.lower --- marathon/client.py | 2 +- marathon/models/app.py | 2 +- marathon/models/deployment.py | 4 ++-- marathon/models/info.py | 16 ++++++++++------ 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/marathon/client.py b/marathon/client.py index 150f534..1e50c98 100644 --- a/marathon/client.py +++ b/marathon/client.py @@ -168,7 +168,7 @@ def create_app(self, app_id, app, minimal=True): :returns: the created app (on success) :rtype: :class:`marathon.models.app.MarathonApp` or False """ - app.id = assert_valid_path(app_id.lower()) + app.id = assert_valid_path(app_id.lower() if type(app_id) is str else app_id) data = app.to_json(minimal=minimal) marathon.log.debug('create app JSON sent: {}'.format(data)) response = self._do_request('POST', '/v2/apps', data=data) diff --git a/marathon/models/app.py b/marathon/models/app.py index ede2341..b4d6aed 100644 --- a/marathon/models/app.py +++ b/marathon/models/app.py @@ -131,7 +131,7 @@ def __init__(self, accepted_resource_roles=None, args=None, backoff_factor=None, hc, MarathonHealthCheck) else MarathonHealthCheck().from_json(hc) for hc in (health_checks or []) ] - self.id = assert_valid_path(id.lower()) + self.id = assert_valid_path(id.lower() if type(id) is str else id) self.role = role self.instances = instances if kill_selection and kill_selection not in self.KILL_SELECTIONS: diff --git a/marathon/models/deployment.py b/marathon/models/deployment.py index c3398d2..d7ac7cd 100644 --- a/marathon/models/deployment.py +++ b/marathon/models/deployment.py @@ -60,8 +60,8 @@ class MarathonDeploymentAction(MarathonObject): def __init__(self, action=None, app=None, apps=None, type=None, readiness_check_results=None, pod=None): self.action = action - self.app = assert_valid_path(app.lower()) - self.apps = assert_valid_path(apps.lower()) + self.app = assert_valid_path(app) + self.apps = assert_valid_path(apps) self.pod = pod self.type = type # TODO: Remove builtin shadow self.readiness_check_results = readiness_check_results # TODO: The docs say this is called just "readinessChecks?" diff --git a/marathon/models/info.py b/marathon/models/info.py index 5ca62d4..68622a3 100644 --- a/marathon/models/info.py +++ b/marathon/models/info.py @@ -64,10 +64,12 @@ class MarathonConfig(MarathonObject): :param int leader_proxy_read_timeout_ms: :param int local_port_min: :param int local_port_max: + :param bool maintenance_mode: :param str master: :param str mesos_leader_ui_url: :param str mesos_role: :param str mesos_user: + :param str new_group_enforce_role: :param str webui_url: :param int reconciliation_initial_delay: :param int reconciliation_interval: @@ -99,12 +101,12 @@ class MarathonConfig(MarathonObject): 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, - 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, + local_port_min=None, local_port_max=None, maintenance_mode=None, master=None, mesos_leader_ui_url=None, + mesos_role=None, mesos_user=None, new_group_enforce_role=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, + 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, @@ -124,10 +126,12 @@ def __init__(self, checkpoint=None, executor=None, failover_timeout=None, framew self.hostname = hostname self.local_port_min = local_port_min self.local_port_max = local_port_max + self.maintenance_mode = maintenance_mode self.master = master self.mesos_leader_ui_url = mesos_leader_ui_url self.mesos_role = mesos_role self.mesos_user = mesos_user + self.new_group_enforce_role = new_group_enforce_role self.webui_url = webui_url self.reconciliation_initial_delay = reconciliation_initial_delay self.reconciliation_interval = reconciliation_interval From 5eb3848cc2ff8c2c66af581c2b2a35b1c7872639 Mon Sep 17 00:00:00 2001 From: Ricardo Rosales Date: Wed, 20 May 2020 15:13:07 -0500 Subject: [PATCH 59/73] Removing .lower from app_id and id --- marathon/client.py | 2 +- marathon/models/app.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/marathon/client.py b/marathon/client.py index 1e50c98..26c7914 100644 --- a/marathon/client.py +++ b/marathon/client.py @@ -168,7 +168,7 @@ def create_app(self, app_id, app, minimal=True): :returns: the created app (on success) :rtype: :class:`marathon.models.app.MarathonApp` or False """ - app.id = assert_valid_path(app_id.lower() if type(app_id) is str else app_id) + app.id = assert_valid_path(app_id) data = app.to_json(minimal=minimal) marathon.log.debug('create app JSON sent: {}'.format(data)) response = self._do_request('POST', '/v2/apps', data=data) diff --git a/marathon/models/app.py b/marathon/models/app.py index b4d6aed..e290fd9 100644 --- a/marathon/models/app.py +++ b/marathon/models/app.py @@ -131,7 +131,7 @@ def __init__(self, accepted_resource_roles=None, args=None, backoff_factor=None, hc, MarathonHealthCheck) else MarathonHealthCheck().from_json(hc) for hc in (health_checks or []) ] - self.id = assert_valid_path(id.lower() if type(id) is str else id) + self.id = assert_valid_path(id) self.role = role self.instances = instances if kill_selection and kill_selection not in self.KILL_SELECTIONS: From 6cb62ed8017de542eaafd2605180152cadc1545d Mon Sep 17 00:00:00 2001 From: Ricardo Rosales Date: Thu, 21 May 2020 18:57:26 -0500 Subject: [PATCH 60/73] Moving marathon away from ubuntu and starting from mesosphere dockerhub --- .travis.yml | 24 +++++++------- README.md | 2 +- itests/.dockerignore | 66 ++++++++++++++++++++++++++++++++++++++ itests/Dockerfile | 16 +++------ itests/docker-compose.yml | 13 +++++--- itests/install-marathon.sh | 51 +++++++++++++++++------------ itests/itest.sh | 4 +-- itests/start-marathon.sh | 29 +++++++++++++---- 8 files changed, 147 insertions(+), 58 deletions(-) create mode 100644 itests/.dockerignore diff --git a/.travis.yml b/.travis.yml index 8d89ecf..37aa8b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,30 +1,28 @@ env: - - MARATHONVERSION: 1.9.109 - - MARATHONVERSION: 1.6.322 - - MARATHONVERSION: 1.4.11 - - MARATHONVERSION: 1.3.0 - - MARATHONVERSION: 1.1.2 + - MARATHONVERSION: v1.9.109 + - MARATHONVERSION: v1.6.322 + - MARATHONVERSION: v1.4.11 + - MARATHONVERSION: v1.3.0 + - MARATHONVERSION: v1.1.2 language: python +services: + - docker python: - 3.6 - 3.7 +before_install: + - docker pull "missingcharacter/marathon-python:${MARATHONVERSION}" + - docker run -d -p 18080:8080 -p 15050:5050 "missingcharacter/marathon-python:${MARATHONVERSION}" install: - pip install tox script: - make test - - ./itests/install-marathon.sh - - ./itests/start-marathon.sh & - make itests # Work around travis-ci/travis-ci#5227 addons: hostname: localhost - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - libstdc++6-4.7-dev sudo: required @@ -35,5 +33,5 @@ deploy: secure: "Wl8GWxsfPy4KoORYH26N3FllvMeWrifzeCbEx2Af4corcBQl43heeiFRRTlUOcSX0TIasER21PUvQ0R0cAgCjfknDb3SOROcRtcSBe16+cMmvwysfxcAx2OcF1UYBPY8e/qOsGge2Zyzx2PAPNEmJoWKbIT3vUJ4WvlLVeGYdJ0=" on: tags: true - condition: $MARATHONVERSION == "1.6.322" + condition: $MARATHONVERSION == "v1.6.322" repo: thefactory/marathon-python diff --git a/README.md b/README.md index c109c83..43b8f4f 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ make itests ### Running The Tests Against a Specific Version of Marathon ```bash -MARATHONVERSION=1.6.322 make itests +MARATHONVERSION=v1.6.322 make itests ``` ## Documentation diff --git a/itests/.dockerignore b/itests/.dockerignore new file mode 100644 index 0000000..2947fe4 --- /dev/null +++ b/itests/.dockerignore @@ -0,0 +1,66 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +bin/ +build/ +develop-eggs/ +dist/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# Rope +.ropeproject + +# Django stuff: +*.log +*.pot + +# Sphinx documentation +docs/_build/ + +.DS_Store + +# IntelliJ +.idea +*.iml + +# Packer http://packer.io +packer_cache + +/gh-pages +itests/marathon-version +.pytest_cache/ diff --git a/itests/Dockerfile b/itests/Dockerfile index b2223c7..0a3bf17 100644 --- a/itests/Dockerfile +++ b/itests/Dockerfile @@ -1,13 +1,6 @@ -FROM ubuntu:14.04 - -RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -y install \ - software-properties-common -RUN add-apt-repository ppa:webupd8team/java -RUN echo "debconf shared/accepted-oracle-license-v1-1 select true" | debconf-set-selections -RUN echo "debconf shared/accepted-oracle-license-v1-1 seen true" | debconf-set-selections -RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -y -q install \ - lsb-release \ - oracle-java8-installer +ARG MARATHONVERSION=v1.6.322 +FROM mesosphere/marathon:$MARATHONVERSION +USER root # Setup ADD ./marathon-version /root/marathon-version @@ -16,4 +9,5 @@ RUN /root/install-marathon.sh EXPOSE 8080 5050 ADD ./start-marathon.sh /root/start-marathon.sh -CMD /etc/init.d/zookeeper start && /root/start-marathon.sh +ENTRYPOINT [] +CMD ["/root/start-marathon.sh"] diff --git a/itests/docker-compose.yml b/itests/docker-compose.yml index 064c756..8c2a4c1 100644 --- a/itests/docker-compose.yml +++ b/itests/docker-compose.yml @@ -1,6 +1,9 @@ --- -marathon: - build: . - ports: - - 18080:8080 - - 15050:5050 +version: "3.8" +services: + marathon: + build: + context: . + ports: + - 18080:8080 + - 15050:5050 diff --git a/itests/install-marathon.sh b/itests/install-marathon.sh index 6bff172..e8e403a 100755 --- a/itests/install-marathon.sh +++ b/itests/install-marathon.sh @@ -1,27 +1,38 @@ -#!/bin/bash -set -vxeu +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' # Default version of marathon to test against if not set by the user [[ -f /root/marathon-version ]] && source /root/marathon-version -MARATHONVERSION="${MARATHONVERSION:-1.4.0}" +MARATHONVERSION="${MARATHONVERSION:-v1.6.322}" -# Setup -sudo apt-key adv --keyserver keyserver.ubuntu.com --recv 81026D0004C44CF7EF55ADF8DF7D54CBE56151BF -DISTRO=$(lsb_release -is | tr '[:upper:]' '[:lower:]') -CODENAME=$(lsb_release -cs) +export DEBIAN_FRONTEND=noninteractive -# Add the repository -echo "deb http://repos.mesosphere.com/${DISTRO} ${CODENAME} main" | sudo tee /etc/apt/sources.list.d/mesosphere.list -sudo apt-get update +shopt -s extglob -# Install packages -sudo DEBIAN_FRONTEND=noninteractive apt-get -y install oracle-java8-installer -sudo update-java-alternatives -s java-8-oracle -sudo DEBIAN_FRONTEND=noninteractive apt-get install oracle-java8-set-default +case "${MARATHONVERSION}" in + v1.9.109) + echo "Marathon version ${MARATHONVERSION} needs no specific changes" + apt update + ;; + v1.6.322) + sed -i 's!deb http://ftp.debian.org/debian jessie-backports main!!g' /etc/apt/sources.list + apt update + apt install -y mesos=1.6.* + ;; + v1.4.11) + sed -i 's!deb http://ftp.debian.org/debian jessie-backports main!!g' /etc/apt/sources.list + apt update + ;; + @(v1.3.0|v1.1.2)) + rm /etc/apt/sources.list.d/jessie-backports.list + apt update + ;; + *) + echo "Marathon version ${MARATHONVERSION} is not supported" + exit 1 + ;; +esac -sudo DEBIAN_FRONTEND=noninteractive apt-get -y --force-yes install mesos=1.6.* marathon=$MARATHONVERSION* - -# WTF MARATHON? -# Why does the precise version have java7 hardcoded if it requires java8? -sudo mkdir -p /usr/lib/jvm/java-7-oracle/bin/ -sudo ln -s /usr/lib/jvm/java-8-oracle/bin/java /usr/lib/jvm/java-7-oracle/bin/java +apt install -y --force-yes zookeeperd curl lsof +rm -rf /var/log/apt/* /var/log/alternatives.log /var/log/bootstrap.log /var/log/dpkg.log diff --git a/itests/itest.sh b/itests/itest.sh index bbc8b81..157ddf1 100755 --- a/itests/itest.sh +++ b/itests/itest.sh @@ -2,8 +2,8 @@ set -e -[[ -n $TRAVIS ]] || echo MARATHONVERSION=$MARATHONVERSION > marathon-version -[[ -n $TRAVIS ]] || docker-compose build +[[ -n $TRAVIS ]] || echo "MARATHONVERSION=${MARATHONVERSION}" > marathon-version +[[ -n $TRAVIS ]] || docker-compose build --build-arg "MARATHONVERSION=${MARATHONVERSION}" [[ -n $TRAVIS ]] || docker-compose pull [[ -n $TRAVIS ]] || docker-compose up -d behave "$@" diff --git a/itests/start-marathon.sh b/itests/start-marathon.sh index 0da2305..3d99c54 100755 --- a/itests/start-marathon.sh +++ b/itests/start-marathon.sh @@ -1,12 +1,29 @@ -#!/bin/bash +#!/usr/bin/env bash +set -xeuo pipefail +IFS=$'\n\t' LOGGER="--logging_level info" +# Default version of marathon to test against if not set by the user +[[ -f /root/marathon-version ]] && source /root/marathon-version +MARATHONVERSION="${MARATHONVERSION:-v1.6.322}" + +shopt -s extglob + +case "${MARATHONVERSION}" in + @(v1.4.11|v1.3.0|v1.1.2)) + ln -sf /marathon/bin/start /marathon/bin/marathon + ;; + *) + echo "Marathon version ${MARATHONVERSION} needs no specific changes" + ;; +esac java -version export MESOS_WORK_DIR='/tmp/mesos' -export ZK_HOST=`cat /etc/mesos/zk` +export ZK_HOST=$(cat /etc/mesos/zk) -mkdir -p "$MESOS_WORK_DIR" -nohup mesos-master --work_dir=/tmp/mesosmaster --zk=$ZK_HOST --quorum=1 & -nohup mesos-agent --master=$ZK_HOST --work_dir=/tmp/mesosagent --launcher=posix & -exec /usr/bin/marathon --master $ZK_HOST $LOGGER +mkdir -p "${MESOS_WORK_DIR}" +/etc/init.d/zookeeper start +nohup mesos-master --work_dir=/tmp/mesosmaster --zk=${ZK_HOST} --quorum=1 &> mesos-master.log & +nohup /usr/bin/env MESOS_SYSTEMD_ENABLE_SUPPORT=false mesos-slave --master=${ZK_HOST} --work_dir=/tmp/mesosagent --launcher=posix &> mesos-agent.log & +eval "bin/marathon --master ${ZK_HOST} ${LOGGER}" From 4c12ec19a85d284510acd1f7118e3c1e1cc4536b Mon Sep 17 00:00:00 2001 From: Ricardo Rosales Date: Thu, 21 May 2020 19:40:16 -0500 Subject: [PATCH 61/73] Trying to separate tests per python version --- .travis.yml | 11 ++++++++--- Makefile | 12 ++++++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 37aa8b6..13f03dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,14 +11,19 @@ services: python: - 3.6 - 3.7 +include: + - python: 3.7 + env: TOX_ENV=py37 + - python: 3.6 + env: TOX_ENV=py36 before_install: - docker pull "missingcharacter/marathon-python:${MARATHONVERSION}" - - docker run -d -p 18080:8080 -p 15050:5050 "missingcharacter/marathon-python:${MARATHONVERSION}" + - docker run --name marathon-python -d -p 8080:8080 -p 5050:5050 "missingcharacter/marathon-python:${MARATHONVERSION}" install: - pip install tox script: - - make test - - make itests + - make test-$TOX_ENV + - make itests-$TOX_ENV # Work around travis-ci/travis-ci#5227 addons: diff --git a/Makefile b/Makefile index f64cc3e..5326277 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,18 @@ -itests: +itests: itests-py36 itests-py37 + +itests-py36: tox -e itest-py36 + +itests-py37: tox -e itest-py37 -test: +test: test-py36 test-py37 + +test-py36: tox -e pep8 tox -e test-py36 + +test-py37: tox -e test-py37 clean: From ba4cbbe4bd2820eeda8d63fe571e42ef5dd9977e Mon Sep 17 00:00:00 2001 From: Ricardo Rosales Date: Thu, 21 May 2020 19:48:59 -0500 Subject: [PATCH 62/73] travis lint says .travis.yml is valid --- .travis.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 13f03dc..ceb9b3d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,11 +11,12 @@ services: python: - 3.6 - 3.7 -include: - - python: 3.7 - env: TOX_ENV=py37 - - python: 3.6 - env: TOX_ENV=py36 +jobs: + include: + - python: 3.7 + env: TOX_ENV=py37 + - python: 3.6 + env: TOX_ENV=py36 before_install: - docker pull "missingcharacter/marathon-python:${MARATHONVERSION}" - docker run --name marathon-python -d -p 8080:8080 -p 5050:5050 "missingcharacter/marathon-python:${MARATHONVERSION}" @@ -29,7 +30,8 @@ script: addons: hostname: localhost -sudo: required +os: linux +dist: xenial deploy: - provider: pypi From 5c0f84101d1cf11e32c873c037ecf25caae7763c Mon Sep 17 00:00:00 2001 From: Ricardo Rosales Date: Thu, 21 May 2020 19:57:37 -0500 Subject: [PATCH 63/73] Trying to get tox environment from TRAVIS_PYTHON_VERSION --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ceb9b3d..de4e6fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,8 +23,8 @@ before_install: install: - pip install tox script: - - make test-$TOX_ENV - - make itests-$TOX_ENV + - make test-py${TRAVIS_PYTHON_VERSION/./} + - make itests-py${TRAVIS_PYTHON_VERSION/./} # Work around travis-ci/travis-ci#5227 addons: From fa3b4aba8d4b0a0346c50dfc6e05a40120054f58 Mon Sep 17 00:00:00 2001 From: Ricardo Rosales Date: Thu, 21 May 2020 19:58:59 -0500 Subject: [PATCH 64/73] Removing jobs from .travis.yml --- .travis.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index de4e6fb..7429125 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,12 +11,6 @@ services: python: - 3.6 - 3.7 -jobs: - include: - - python: 3.7 - env: TOX_ENV=py37 - - python: 3.6 - env: TOX_ENV=py36 before_install: - docker pull "missingcharacter/marathon-python:${MARATHONVERSION}" - docker run --name marathon-python -d -p 8080:8080 -p 5050:5050 "missingcharacter/marathon-python:${MARATHONVERSION}" From dfb944cb76b71a69fa589119e4c0179e5ab736c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 May 2020 01:37:41 +0000 Subject: [PATCH 65/73] Bump requests from 2.11.1 to 2.20.0 Bumps [requests](https://github.com/requests/requests) from 2.11.1 to 2.20.0. - [Release notes](https://github.com/requests/requests/releases) - [Changelog](https://github.com/psf/requests/blob/master/HISTORY.md) - [Commits](https://github.com/requests/requests/compare/v2.11.1...v2.20.0) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4661030..c20f36f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -requests==2.11.1 +requests==2.20.0 From acffecd307c38c3512b77487e2e83806963c7a8d Mon Sep 17 00:00:00 2001 From: Ricardo Rosales Date: Thu, 21 May 2020 20:57:41 -0500 Subject: [PATCH 66/73] Trying to add support for v1.10.19 --- .travis.yml | 1 + itests/install-marathon.sh | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7429125..542dd88 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ env: + - MARATHONVERSION: v1.10.19 - MARATHONVERSION: v1.9.109 - MARATHONVERSION: v1.6.322 - MARATHONVERSION: v1.4.11 diff --git a/itests/install-marathon.sh b/itests/install-marathon.sh index e8e403a..5a4ff52 100755 --- a/itests/install-marathon.sh +++ b/itests/install-marathon.sh @@ -11,7 +11,7 @@ export DEBIAN_FRONTEND=noninteractive shopt -s extglob case "${MARATHONVERSION}" in - v1.9.109) + @(v1.10.19|v1.9.109)) echo "Marathon version ${MARATHONVERSION} needs no specific changes" apt update ;; From 4a109c3fa287085d3d1d7348f595593bc6e79866 Mon Sep 17 00:00:00 2001 From: Ricardo Rosales Date: Thu, 21 May 2020 21:13:24 -0500 Subject: [PATCH 67/73] Updating README with support for 1.10.x --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 43b8f4f..d4e38cf 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This is a Python library for interfacing with [Marathon](https://github.com/meso #### Compatibility -* For Marathon 1.9.x, use at least 0.13.0 +* For Marathon 1.9.x and 1.10.x, use at least 0.13.0 * 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 From ab57d0da799f2f7590a829aa26d4538c6031c008 Mon Sep 17 00:00:00 2001 From: Ricardo Rosales Date: Fri, 22 May 2020 12:27:17 -0500 Subject: [PATCH 68/73] Local tests no longer need docker-compose and rely on "mini-marathon" --- itests/docker-compose.yml | 9 --------- itests/{ => docker}/.dockerignore | 0 itests/{ => docker}/Dockerfile | 5 +++-- itests/docker/README.md | 9 +++++++++ itests/{ => docker}/install-marathon.sh | 0 itests/{ => docker}/start-marathon.sh | 2 +- itests/itest.sh | 9 +++------ itests/itest_utils.py | 16 +--------------- 8 files changed, 17 insertions(+), 33 deletions(-) delete mode 100644 itests/docker-compose.yml rename itests/{ => docker}/.dockerignore (100%) rename itests/{ => docker}/Dockerfile (67%) create mode 100644 itests/docker/README.md rename itests/{ => docker}/install-marathon.sh (100%) rename itests/{ => docker}/start-marathon.sh (97%) diff --git a/itests/docker-compose.yml b/itests/docker-compose.yml deleted file mode 100644 index 8c2a4c1..0000000 --- a/itests/docker-compose.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -version: "3.8" -services: - marathon: - build: - context: . - ports: - - 18080:8080 - - 15050:5050 diff --git a/itests/.dockerignore b/itests/docker/.dockerignore similarity index 100% rename from itests/.dockerignore rename to itests/docker/.dockerignore diff --git a/itests/Dockerfile b/itests/docker/Dockerfile similarity index 67% rename from itests/Dockerfile rename to itests/docker/Dockerfile index 0a3bf17..511fbbf 100644 --- a/itests/Dockerfile +++ b/itests/docker/Dockerfile @@ -1,11 +1,12 @@ ARG MARATHONVERSION=v1.6.322 FROM mesosphere/marathon:$MARATHONVERSION +ARG MARATHONVERSION USER root # Setup -ADD ./marathon-version /root/marathon-version ADD ./install-marathon.sh /root/install-marathon.sh -RUN /root/install-marathon.sh +RUN echo "MARATHONVERSION=${MARATHONVERSION}" > /root/marathon-version \ + && /root/install-marathon.sh EXPOSE 8080 5050 ADD ./start-marathon.sh /root/start-marathon.sh diff --git a/itests/docker/README.md b/itests/docker/README.md new file mode 100644 index 0000000..807726e --- /dev/null +++ b/itests/docker/README.md @@ -0,0 +1,9 @@ +# mini-marathon + +**Note:** We currently only support the marathon versions listed in [.travis.yml](https://github.com/thefactory/marathon-python/blob/acffecd307c38c3512b77487e2e83806963c7a8d/.travis.yml#L2-L7) + +## How to build + +``` +docker build --build-arg "MARATHONVERSION=v1.6.322" . +``` diff --git a/itests/install-marathon.sh b/itests/docker/install-marathon.sh similarity index 100% rename from itests/install-marathon.sh rename to itests/docker/install-marathon.sh diff --git a/itests/start-marathon.sh b/itests/docker/start-marathon.sh similarity index 97% rename from itests/start-marathon.sh rename to itests/docker/start-marathon.sh index 3d99c54..116e15c 100755 --- a/itests/start-marathon.sh +++ b/itests/docker/start-marathon.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -set -xeuo pipefail +set -euo pipefail IFS=$'\n\t' LOGGER="--logging_level info" diff --git a/itests/itest.sh b/itests/itest.sh index 157ddf1..6867ef6 100755 --- a/itests/itest.sh +++ b/itests/itest.sh @@ -2,10 +2,7 @@ set -e -[[ -n $TRAVIS ]] || echo "MARATHONVERSION=${MARATHONVERSION}" > marathon-version -[[ -n $TRAVIS ]] || docker-compose build --build-arg "MARATHONVERSION=${MARATHONVERSION}" -[[ -n $TRAVIS ]] || docker-compose pull -[[ -n $TRAVIS ]] || docker-compose up -d +[[ -n $TRAVIS ]] || docker pull "missingcharacter/marathon-python:${MARATHONVERSION}" +[[ -n $TRAVIS ]] || docker run --rm --name marathon-python -d -p 18080:8080 -p 15050:5050 "missingcharacter/marathon-python:${MARATHONVERSION}" behave "$@" -[[ -n $TRAVIS ]] || docker-compose stop -[[ -n $TRAVIS ]] || docker-compose rm --force +[[ -n $TRAVIS ]] || docker kill marathon-python diff --git a/itests/itest_utils.py b/itests/itest_utils.py index f5028fe..0ec865e 100644 --- a/itests/itest_utils.py +++ b/itests/itest_utils.py @@ -5,7 +5,6 @@ import time import requests -import compose.cli.command class TimeoutError(Exception): @@ -51,22 +50,9 @@ def wait_for_marathon(): break -def get_compose_service(service_name): - """Returns a compose object for the service""" - project = compose.cli.command.get_project(os.path.dirname(os.path.realpath(__file__))) - return project.get_service(service_name) - - def get_marathon_connection_string(): # only reliable way I can detect travis.. if '/travis/' in os.environ.get('PATH'): return 'localhost:8080' else: - service_port = get_service_internal_port('marathon') - return "localhost:%s" % service_port.published - - -def get_service_internal_port(service_name): - """Gets the exposed port for service_name from docker-compose.yml. If there are - multiple ports. It returns the first one.""" - return get_compose_service(service_name).options['ports'][0] + return "localhost:18080" From 5e5a2bd884132a757ac12e8b93fd875d1ae0296b Mon Sep 17 00:00:00 2001 From: Shubham Sharma Date: Sat, 30 May 2020 21:07:10 +0530 Subject: [PATCH 69/73] Fix deserialization for Deploment model --- marathon/models/deployment.py | 6 +++--- tests/test_model_deployment.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 tests/test_model_deployment.py diff --git a/marathon/models/deployment.py b/marathon/models/deployment.py index c3398d2..711ea18 100644 --- a/marathon/models/deployment.py +++ b/marathon/models/deployment.py @@ -25,7 +25,7 @@ def __init__(self, affected_apps=None, current_actions=None, current_step=None, self.affected_apps = affected_apps self.current_actions = [ a if isinstance( - a, MarathonDeploymentAction) else MarathonDeploymentAction().from_json(a) + a, MarathonDeploymentAction) else MarathonDeploymentAction.from_json(a) for a in (current_actions or []) ] self.current_step = current_step @@ -41,7 +41,7 @@ def parse_deployment_step(self, step): return MarathonDeploymentStep().from_json(step) elif step.__class__ == list: # This is Marathon < 1.0.0 style, a list of actions - return [s if isinstance(s, MarathonDeploymentAction) else MarathonDeploymentAction().from_json(s) for s in step] + return [s if isinstance(s, MarathonDeploymentAction) else MarathonDeploymentAction.from_json(s) for s in step] else: return step @@ -61,7 +61,7 @@ class MarathonDeploymentAction(MarathonObject): def __init__(self, action=None, app=None, apps=None, type=None, readiness_check_results=None, pod=None): self.action = action self.app = assert_valid_path(app.lower()) - self.apps = assert_valid_path(apps.lower()) + self.apps = assert_valid_path(apps.lower()) if apps != None else None self.pod = pod self.type = type # TODO: Remove builtin shadow self.readiness_check_results = readiness_check_results # TODO: The docs say this is called just "readinessChecks?" diff --git a/tests/test_model_deployment.py b/tests/test_model_deployment.py new file mode 100644 index 0000000..9da4c9e --- /dev/null +++ b/tests/test_model_deployment.py @@ -0,0 +1,16 @@ +from marathon.models.deployment import MarathonDeployment +import unittest + + +class MarathonDeploymentTest(unittest.TestCase): + + def test_env_defaults_to_empty_dict(self): + """ + é testé + """ + deployment_json ={"id": "ID", "version": "2020-05-30T07:35:04.695Z", "affectedApps": ["/app"], "affectedPods": [], "steps": [{"actions": [{"action": "RestartApplication", "app": "/app"}]}], "currentActions": [{"action": "RestartApplication", "app": "/app", "readinessCheckResults": []}], "currentStep": 1, "totalSteps": 1} + + deployment = MarathonDeployment.from_json(deployment_json) + self.assertEquals(deployment.id, "ID") + self.assertEquals(deployment.current_actions[0].app, "/app") + From 27c5bd836e8743c15dd8299512f8f1a9fe0d3525 Mon Sep 17 00:00:00 2001 From: Ricardo Rosales Date: Fri, 19 Jun 2020 17:24:15 -0500 Subject: [PATCH 70/73] test_model_deployment flake8 formatting --- tests/test_model_deployment.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/tests/test_model_deployment.py b/tests/test_model_deployment.py index 9da4c9e..9e9723a 100644 --- a/tests/test_model_deployment.py +++ b/tests/test_model_deployment.py @@ -8,9 +8,26 @@ def test_env_defaults_to_empty_dict(self): """ é testé """ - deployment_json ={"id": "ID", "version": "2020-05-30T07:35:04.695Z", "affectedApps": ["/app"], "affectedPods": [], "steps": [{"actions": [{"action": "RestartApplication", "app": "/app"}]}], "currentActions": [{"action": "RestartApplication", "app": "/app", "readinessCheckResults": []}], "currentStep": 1, "totalSteps": 1} - + deployment_json = { + "id": "ID", + "version": "2020-05-30T07:35:04.695Z", + "affectedApps": ["/app"], + "affectedPods": [], + "steps": [{ + "actions": [{ + "action": "RestartApplication", + "app": "/app" + }] + }], + "currentActions": [{ + "action": "RestartApplication", + "app": "/app", + "readinessCheckResults": [] + }], + "currentStep": 1, + "totalSteps": 1 + } + deployment = MarathonDeployment.from_json(deployment_json) self.assertEquals(deployment.id, "ID") self.assertEquals(deployment.current_actions[0].app, "/app") - From e2cf2c12cbc830da522a25305f68bca6b921678f Mon Sep 17 00:00:00 2001 From: Kyle Anderson Date: Fri, 21 Aug 2020 09:28:33 -0700 Subject: [PATCH 71/73] Release 0.13.0 --- CHANGELOG.md | 24 +++++++++++++++++++++--- Makefile | 2 +- setup.py | 2 +- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f9e928..dc2946f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,26 @@ # Changelog -## [Unreleased](https://github.com/thefactory/marathon-python/tree/HEAD) +## [0.13.0](https://github.com/thefactory/marathon-python/tree/0.13.0) (2020-08-21) -[Full Changelog](https://github.com/thefactory/marathon-python/compare/0.11.0...HEAD) +[Full Changelog](https://github.com/thefactory/marathon-python/compare/0.12.0...0.13.0) + +**Closed issues:** + +- about view one instance app logs real time [\#277](https://github.com/thefactory/marathon-python/issues/277) + +**Merged pull requests:** + +- Fix deserialization for Deploment model [\#276](https://github.com/thefactory/marathon-python/pull/276) ([missingcharacter](https://github.com/missingcharacter)) +- Local tests no longer need docker-compose and rely on "mini-marathon" [\#274](https://github.com/thefactory/marathon-python/pull/274) ([missingcharacter](https://github.com/missingcharacter)) +- Adding support for v1.10.19 [\#273](https://github.com/thefactory/marathon-python/pull/273) ([missingcharacter](https://github.com/missingcharacter)) +- Moving marathon away from ubuntu and starting from mesosphere dockerhub [\#272](https://github.com/thefactory/marathon-python/pull/272) ([missingcharacter](https://github.com/missingcharacter)) +- Updates for Marathon 1.9.109 [\#270](https://github.com/thefactory/marathon-python/pull/270) ([missingcharacter](https://github.com/missingcharacter)) +- Fix return value for kill\_given\_tasks. [\#268](https://github.com/thefactory/marathon-python/pull/268) ([Tilian](https://github.com/Tilian)) +- Bump requests from 2.11.1 to 2.20.0 [\#266](https://github.com/thefactory/marathon-python/pull/266) ([dependabot[bot]](https://github.com/apps/dependabot)) + +## [0.12.0](https://github.com/thefactory/marathon-python/tree/0.12.0) (2019-11-14) + +[Full Changelog](https://github.com/thefactory/marathon-python/compare/0.11.0...0.12.0) **Closed issues:** @@ -100,7 +118,7 @@ - Support filtering applications by labels [\#211](https://github.com/thefactory/marathon-python/pull/211) ([iandyh](https://github.com/iandyh)) - add embed option for /v2/queue [\#210](https://github.com/thefactory/marathon-python/pull/210) ([Rob-Johnson](https://github.com/Rob-Johnson)) - Enable TCP keepalive for sse requests [\#209](https://github.com/thefactory/marathon-python/pull/209) ([fengyehong](https://github.com/fengyehong)) -- Add "udp,tcp" to authorized protocols for containers [\#208](https://github.com/thefactory/marathon-python/pull/208) ([alxkt](https://github.com/alxkt)) +- Add "udp,tcp" to authorized protocols for containers [\#208](https://github.com/thefactory/marathon-python/pull/208) ([fuegoio](https://github.com/fuegoio)) - Allow event type filter on event stream [\#207](https://github.com/thefactory/marathon-python/pull/207) ([fengyehong](https://github.com/fengyehong)) - Fix MarathonResource hash as well [\#205](https://github.com/thefactory/marathon-python/pull/205) ([jolynch](https://github.com/jolynch)) diff --git a/Makefile b/Makefile index 5326277..1d94d86 100644 --- a/Makefile +++ b/Makefile @@ -19,12 +19,12 @@ clean: rm -rf dist/ build/ package: clean + github_changelog_generator --user=thefactory --project=marathon-python --future-release=0.13.0 pip install wheel python setup.py sdist bdist_wheel publish: package pip install twine twine upload dist/* - github_changelog_generator .PHONY: itests test clean package publish diff --git a/setup.py b/setup.py index 8adc7f0..fa25a98 100755 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setup( name='marathon', - version='0.12.0', + version='0.13.0', description='Marathon Client Library', long_description="""Python interface to the Mesos Marathon REST API.""", author='Mike Babineau', From 21c9b1c55c64d4af31da78d5d64315c00ea1b16d Mon Sep 17 00:00:00 2001 From: Harold Dost Date: Wed, 26 Aug 2020 10:17:36 +0200 Subject: [PATCH 72/73] Add attribute enforce_role to groups. This was introduced in Marathon v1.9.32 Fixes #280 --- marathon/models/group.py | 3 ++- tests/test_model_group.py | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/marathon/models/group.py b/marathon/models/group.py index bb4f6bc..f68e230 100644 --- a/marathon/models/group.py +++ b/marathon/models/group.py @@ -20,7 +20,7 @@ class MarathonGroup(MarathonResource): """ def __init__(self, apps=None, dependencies=None, - groups=None, id=None, pods=None, version=None): + groups=None, id=None, pods=None, version=None, enforce_role=None): self.apps = [ a if isinstance(a, MarathonApp) else MarathonApp().from_json(a) for a in (apps or []) @@ -38,3 +38,4 @@ def __init__(self, apps=None, dependencies=None, # ] self.id = id self.version = version + self.enforce_role = enforce_role diff --git a/tests/test_model_group.py b/tests/test_model_group.py index e9fb340..45b20a3 100644 --- a/tests/test_model_group.py +++ b/tests/test_model_group.py @@ -15,3 +15,16 @@ def test_from_json_parses_root_group(self): } group = MarathonGroup().from_json(data) self.assertEqual("/", group.id) + + def test_from_json_parses_group_with_enforce_role(self): + data = { + "id": "/mygroup/works", + "groups": [ + {"id": "/foo", "apps": []}, + ], + "apps": [], + "enforceRole": False, + + } + group = MarathonGroup().from_json(data) + self.assertEqual("/mygroup/works", group.id) From ebb2ee3d5249b8e1454687ab4c71347b4ccd9c7e Mon Sep 17 00:00:00 2001 From: Harold Dost Date: Mon, 26 Oct 2020 14:32:06 +0100 Subject: [PATCH 73/73] Use collections.abc where available. As of Python 3.3 collections.abc should be used in place of collections. In Python 3.9 use of collections will be removed. --- marathon/util.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/marathon/util.py b/marathon/util.py index d9f5664..af2932e 100644 --- a/marathon/util.py +++ b/marathon/util.py @@ -1,4 +1,10 @@ -import collections +# collections.abc new as of 3.3, and collections is deprecated. collections +# will be unavailable in 3.9 +try: + import collections.abc as collections +except ImportError: + import collections + import datetime import logging