1
0
mirror of https://github.com/github/octodns.git synced 2024-05-11 05:55:00 +00:00

Merge branch 'master' into master

This commit is contained in:
Heesu Hwang
2017-07-03 11:07:14 -07:00
committed by GitHub
4 changed files with 66 additions and 18 deletions

View File

@@ -6,7 +6,7 @@ from __future__ import absolute_import, division, print_function, \
unicode_literals unicode_literals
from StringIO import StringIO from StringIO import StringIO
from concurrent.futures import Future, ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from importlib import import_module from importlib import import_module
from os import environ from os import environ
import logging import logging
@@ -38,6 +38,17 @@ class _AggregateTarget(object):
return True return True
class MakeThreadFuture(object):
def __init__(self, func, args, kwargs):
self.func = func
self.args = args
self.kwargs = kwargs
def result(self):
return self.func(*self.args, **self.kwargs)
class MainThreadExecutor(object): class MainThreadExecutor(object):
''' '''
Dummy executor that runs things on the main thread during the involcation Dummy executor that runs things on the main thread during the involcation
@@ -48,13 +59,7 @@ class MainThreadExecutor(object):
''' '''
def submit(self, func, *args, **kwargs): def submit(self, func, *args, **kwargs):
future = Future() return MakeThreadFuture(func, args, kwargs)
try:
future.set_result(func(*args, **kwargs))
except Exception as e:
# TODO: get right stacktrace here
future.set_exception(e)
return future
class Manager(object): class Manager(object):

View File

@@ -7,7 +7,8 @@ from __future__ import absolute_import, division, print_function, \
from logging import getLogger from logging import getLogger
from nsone import NSONE from nsone import NSONE
from nsone.rest.errors import ResourceException from nsone.rest.errors import RateLimitException, ResourceException
from time import sleep
from ..record import Record from ..record import Record
from .base import BaseProvider from .base import BaseProvider
@@ -171,7 +172,14 @@ class Ns1Provider(BaseProvider):
name = self._get_name(new) name = self._get_name(new)
_type = new._type _type = new._type
params = getattr(self, '_params_for_{}'.format(_type))(new) params = getattr(self, '_params_for_{}'.format(_type))(new)
getattr(nsone_zone, 'add_{}'.format(_type))(name, **params) meth = getattr(nsone_zone, 'add_{}'.format(_type))
try:
meth(name, **params)
except RateLimitException as e:
self.log.warn('_apply_Create: rate limit encountered, pausing '
'for %ds and trying again', e.period)
sleep(e.period)
meth(name, **params)
def _apply_Update(self, nsone_zone, change): def _apply_Update(self, nsone_zone, change):
existing = change.existing existing = change.existing
@@ -180,14 +188,26 @@ class Ns1Provider(BaseProvider):
record = nsone_zone.loadRecord(name, _type) record = nsone_zone.loadRecord(name, _type)
new = change.new new = change.new
params = getattr(self, '_params_for_{}'.format(_type))(new) params = getattr(self, '_params_for_{}'.format(_type))(new)
record.update(**params) try:
record.update(**params)
except RateLimitException as e:
self.log.warn('_apply_Update: rate limit encountered, pausing '
'for %ds and trying again', e.period)
sleep(e.period)
record.update(**params)
def _apply_Delete(self, nsone_zone, change): def _apply_Delete(self, nsone_zone, change):
existing = change.existing existing = change.existing
name = self._get_name(existing) name = self._get_name(existing)
_type = existing._type _type = existing._type
record = nsone_zone.loadRecord(name, _type) record = nsone_zone.loadRecord(name, _type)
record.delete() try:
record.delete()
except RateLimitException as e:
self.log.warn('_apply_Delete: rate limit encountered, pausing '
'for %ds and trying again', e.period)
sleep(e.period)
record.delete()
def _apply(self, plan): def _apply(self, plan):
desired = plan.desired desired = plan.desired

View File

@@ -11,7 +11,7 @@ incf.countryutils==1.0
ipaddress==1.0.18 ipaddress==1.0.18
jmespath==0.9.0 jmespath==0.9.0
natsort==5.0.3 natsort==5.0.3
nsone==0.9.10 nsone==0.9.14
python-dateutil==2.6.0 python-dateutil==2.6.0
requests==2.13.0 requests==2.13.0
s3transfer==0.1.10 s3transfer==0.1.10

View File

@@ -6,7 +6,8 @@ from __future__ import absolute_import, division, print_function, \
unicode_literals unicode_literals
from mock import Mock, call, patch from mock import Mock, call, patch
from nsone.rest.errors import AuthException, ResourceException from nsone.rest.errors import AuthException, RateLimitException, \
ResourceException
from unittest import TestCase from unittest import TestCase
from octodns.record import Delete, Record, Update from octodns.record import Delete, Record, Update
@@ -225,7 +226,15 @@ class TestNs1Provider(TestCase):
create_mock.reset_mock() create_mock.reset_mock()
load_mock.side_effect = \ load_mock.side_effect = \
ResourceException('server error: zone not found') ResourceException('server error: zone not found')
create_mock.side_effect = None # ugh, need a mock zone with a mock prop since we're using getattr, we
# can actually control side effects on `meth` with that.
mock_zone = Mock()
mock_zone.add_SRV = Mock()
mock_zone.add_SRV.side_effect = [
RateLimitException('boo', period=0),
None,
]
create_mock.side_effect = [mock_zone]
got_n = provider.apply(plan) got_n = provider.apply(plan)
self.assertEquals(expected_n, got_n) self.assertEquals(expected_n, got_n)
@@ -245,12 +254,26 @@ class TestNs1Provider(TestCase):
self.assertEquals(2, len(plan.changes)) self.assertEquals(2, len(plan.changes))
self.assertIsInstance(plan.changes[0], Update) self.assertIsInstance(plan.changes[0], Update)
self.assertIsInstance(plan.changes[1], Delete) self.assertIsInstance(plan.changes[1], Delete)
# ugh, we need a mock record that can be returned from loadRecord for
# the update and delete targets, we can add our side effects to that to
# trigger rate limit handling
mock_record = Mock()
mock_record.update.side_effect = [
RateLimitException('one', period=0),
None,
]
mock_record.delete.side_effect = [
RateLimitException('two', period=0),
None,
]
nsone_zone.loadRecord.side_effect = [mock_record, mock_record]
got_n = provider.apply(plan) got_n = provider.apply(plan)
self.assertEquals(2, got_n) self.assertEquals(2, got_n)
nsone_zone.loadRecord.assert_has_calls([ nsone_zone.loadRecord.assert_has_calls([
call('unit.tests', u'A'), call('unit.tests', u'A'),
call().update(answers=[u'1.2.3.4'], ttl=32),
call('delete-me', u'A'), call('delete-me', u'A'),
call().delete() ])
mock_record.assert_has_calls([
call.update(answers=[u'1.2.3.4'], ttl=32),
call.delete()
]) ])