Example #1
0
def crontabber_status(request):
    api = CrontabberState()

    # start by assuming the status is OK which means no jobs are broken
    context = {'status': 'ALLGOOD'}

    all_apps = api.get()['state']
    broken = [
        name for name, state in all_apps.items()
        if state['error_count']
    ]
    blocked = [
        name for name, state in all_apps.items()
        if set(broken) & set(state['depends_on'])
    ]

    # This infinite loop recurses deeper and deeper into the structure
    # to find all jobs that are blocked. If we find that job X is blocked
    # by job Y in iteration 1, we need to do another iteration to see if
    # there are jobs that are blocked by job X (which was blocked by job Y)
    while True:
        also_blocked = [
            name for name, state in all_apps.items()
            if name not in blocked and set(blocked) & set(state['depends_on'])
        ]
        if not also_blocked:
            break
        blocked += also_blocked

    if broken:
        # let's change our mind
        context['status'] = 'Broken'
        context['broken'] = broken
        context['blocked'] = blocked
    return context
Example #2
0
def crontabber_status(request):
    api = CrontabberState()

    # start by assuming the status is OK which means no jobs are broken
    context = {'status': 'ALLGOOD'}

    all_apps = api.get()['state']
    last_runs = [
        x['last_run'] for x in all_apps.values()
        if x['last_run']
    ]
    if last_runs:
        ancient_times = timezone.now() - datetime.timedelta(
            minutes=settings.CRONTABBER_STALE_MINUTES
        )
        most_recent_run = max(last_runs)
        if most_recent_run < ancient_times:
            context['status'] = 'Stale'
            context['last_run'] = max(last_runs)
    else:
        # if it's never run, then it's definitely stale
        context['status'] = 'Stale'

    broken = [
        name for name, state in all_apps.items()
        if state['error_count']
    ]
    blocked = [
        name for name, state in all_apps.items()
        if set(broken) & set(state['depends_on'])
    ]

    # This infinite loop recurses deeper and deeper into the structure
    # to find all jobs that are blocked. If we find that job X is blocked
    # by job Y in iteration 1, we need to do another iteration to see if
    # there are jobs that are blocked by job X (which was blocked by job Y)
    while True:
        also_blocked = [
            name for name, state in all_apps.items()
            if name not in blocked and set(blocked) & set(state['depends_on'])
        ]
        if not also_blocked:
            break
        blocked += also_blocked

    if broken:
        # let's change our mind
        context['status'] = 'Broken'
        context['broken'] = broken
        context['blocked'] = blocked
    return context
Example #3
0
    def test_crontabber_status_not_run_for_a_while(self):

        some_time_ago = (
            timezone.now() -
            datetime.timedelta(minutes=settings.CRONTABBER_STALE_MINUTES))

        def mocked_get(**options):
            return {
                'state': {
                    'job1': {
                        'error_count': 0,
                        'depends_on': [],
                        'last_run': some_time_ago,
                    },
                    'job2': {
                        'error_count': 0,
                        'depends_on': ['job1'],
                        'last_run': some_time_ago,
                    },
                }
            }

        CrontabberState.implementation().get.side_effect = mocked_get

        url = reverse('monitoring:crontabber_status')
        response = self.client.get(url)
        eq_(response.status_code, 200)
        data = json.loads(response.content)
        eq_(data['status'], 'Stale')
        eq_(data['last_run'], some_time_ago.isoformat())
Example #4
0
    def test_CORS(self):
        """any use of model_wrapper should return a CORS header"""
        def mocked_get(**options):
            dt = timezone.now()
            return {
                "state": {
                    "automatic-emails": {
                        "next_run": dt,
                        "first_run": dt,
                        "depends_on": [],
                        "last_run": dt,
                        "last_success": dt,
                        "error_count": 0,
                        "last_error": {}
                    },
                    "ftpscraper": {
                        "next_run": dt,
                        "first_run": dt,
                        "depends_on": [],
                        "last_run": dt,
                        "last_success": dt,
                        "error_count": 0,
                        "last_error": {}
                    }
                }
            }

        CrontabberState.implementation().get.side_effect = mocked_get

        url = reverse('api:model_wrapper', args=('CrontabberState', ))
        response = self.client.get(url)
        assert response.status_code == 200
        assert response['Access-Control-Allow-Origin'] == '*'
Example #5
0
    def test_CORS(self):
        """any use of model_wrapper should return a CORS header"""

        def mocked_get(**options):
            dt = timezone.now()
            return {
                "state": {
                    "automatic-emails": {
                        "next_run": dt,
                        "first_run": dt,
                        "depends_on": [],
                        "last_run": dt,
                        "last_success": dt,
                        "error_count": 0,
                        "last_error": {}
                    },
                    "ftpscraper": {
                        "next_run": dt,
                        "first_run": dt,
                        "depends_on": [],
                        "last_run": dt,
                        "last_success": dt,
                        "error_count": 0,
                        "last_error": {}
                    }
                }
            }

        CrontabberState.implementation().get.side_effect = mocked_get

        url = reverse('api:model_wrapper', args=('CrontabberState',))
        response = self.client.get(url)
        assert response.status_code == 200
        assert response['Access-Control-Allow-Origin'] == '*'
Example #6
0
    def test_crontabber_status_not_run_for_a_while(self):

        some_time_ago = (
            timezone.now() - datetime.timedelta(
                minutes=settings.CRONTABBER_STALE_MINUTES
            )
        )

        def mocked_get(**options):
            return {
                'state': {
                    'job1': {
                        'error_count': 0,
                        'depends_on': [],
                        'last_run': some_time_ago,
                    },
                    'job2': {
                        'error_count': 0,
                        'depends_on': ['job1'],
                        'last_run': some_time_ago,
                    },
                }
            }

        CrontabberState.implementation().get.side_effect = mocked_get

        url = reverse('monitoring:crontabber_status')
        response = self.client.get(url)
        eq_(response.status_code, 200)
        data = json.loads(response.content)
        eq_(data['status'], 'Stale')
        eq_(data['last_run'], some_time_ago.isoformat())
Example #7
0
def crontabber_status(request):
    api = CrontabberState()

    # start by assuming the status is OK which means no jobs are broken
    context = {'status': 'ALLGOOD'}

    all_apps = api.get()['state']
    last_runs = [
        isodate.parse_datetime(x['last_run']) for x in all_apps.values()
    ]
    if last_runs:
        ancient_times = timezone.now() - datetime.timedelta(
            minutes=settings.CRONTABBER_STALE_MINUTES)
        most_recent_run = max(last_runs)
        if most_recent_run < ancient_times:
            context['status'] = 'Stale'
            context['last_run'] = max(last_runs)
    else:
        # if it's never run, then it's definitely stale
        context['status'] = 'Stale'

    broken = [name for name, state in all_apps.items() if state['error_count']]
    blocked = [
        name for name, state in all_apps.items()
        if set(broken) & set(state['depends_on'])
    ]

    # This infinite loop recurses deeper and deeper into the structure
    # to find all jobs that are blocked. If we find that job X is blocked
    # by job Y in iteration 1, we need to do another iteration to see if
    # there are jobs that are blocked by job X (which was blocked by job Y)
    while True:
        also_blocked = [
            name for name, state in all_apps.items()
            if name not in blocked and set(blocked) & set(state['depends_on'])
        ]
        if not also_blocked:
            break
        blocked += also_blocked

    if broken:
        # let's change our mind
        context['status'] = 'Broken'
        context['broken'] = broken
        context['blocked'] = blocked
    return context
Example #8
0
    def test_crontabber_status_never_run(self):
        def mocked_get(**options):
            return {'state': {}}

        CrontabberState.implementation().get.side_effect = mocked_get

        url = reverse('monitoring:crontabber_status')
        response = self.client.get(url)
        eq_(response.status_code, 200)
        data = json.loads(response.content)
        eq_(data['status'], 'Stale')
Example #9
0
    def test_crontabber_status_never_run(self):

        def mocked_get(**options):
            return {
                'state': {}
            }

        CrontabberState.implementation().get.side_effect = mocked_get

        url = reverse('monitoring:crontabber_status')
        response = self.client.get(url)
        eq_(response.status_code, 200)
        data = json.loads(response.content)
        eq_(data['status'], 'Stale')
Example #10
0
    def test_crontabber_status_ok(self):
        def mocked_get(**options):
            recently = timezone.now()
            return {
                'state': {
                    'job1': {
                        'error_count': 0,
                        'depends_on': [],
                        'last_run': recently,
                    }
                }
            }

        CrontabberState.implementation().get.side_effect = mocked_get

        url = reverse('monitoring:crontabber_status')
        response = self.client.get(url)
        eq_(response.status_code, 200)
        eq_(json.loads(response.content), {'status': 'ALLGOOD'})
Example #11
0
    def test_crontabber_status_ok(self):

        def mocked_get(**options):
            recently = timezone.now()
            return {
                'state': {
                    'job1': {
                        'error_count': 0,
                        'depends_on': [],
                        'last_run': recently,
                    }
                }
            }

        CrontabberState.implementation().get.side_effect = mocked_get

        url = reverse('monitoring:crontabber_status')
        response = self.client.get(url)
        eq_(response.status_code, 200)
        eq_(json.loads(response.content), {'status': 'ALLGOOD'})
Example #12
0
    def test_crontabber_status_trouble(self):

        def mocked_get(**options):
            recently = timezone.now()
            return {
                'state': {
                    'job1': {
                        'error_count': 1,
                        'depends_on': [],
                        'last_run': recently,
                    },
                    'job2': {
                        'error_count': 0,
                        'depends_on': ['job1'],
                        'last_run': recently,
                    },
                    'job3': {
                        'error_count': 0,
                        'depends_on': ['job2'],
                        'last_run': recently,
                    },
                    'job1b': {
                        'error_count': 0,
                        'depends_on': [],
                        'last_run': recently,
                    },
                }
            }

        CrontabberState.implementation().get.side_effect = mocked_get

        url = reverse('monitoring:crontabber_status')
        response = self.client.get(url)
        eq_(response.status_code, 200)
        data = json.loads(response.content)
        eq_(data['status'], 'Broken')
        eq_(data['broken'], ['job1'])
        eq_(data['blocked'], ['job2', 'job3'])
Example #13
0
    def test_crontabber_status_trouble(self):
        def mocked_get(**options):
            recently = timezone.now()
            return {
                'state': {
                    'job1': {
                        'error_count': 1,
                        'depends_on': [],
                        'last_run': recently,
                    },
                    'job2': {
                        'error_count': 0,
                        'depends_on': ['job1'],
                        'last_run': recently,
                    },
                    'job3': {
                        'error_count': 0,
                        'depends_on': ['job2'],
                        'last_run': recently,
                    },
                    'job1b': {
                        'error_count': 0,
                        'depends_on': [],
                        'last_run': recently,
                    },
                }
            }

        CrontabberState.implementation().get.side_effect = mocked_get

        url = reverse('monitoring:crontabber_status')
        response = self.client.get(url)
        eq_(response.status_code, 200)
        data = json.loads(response.content)
        eq_(data['status'], 'Broken')
        eq_(data['broken'], ['job1'])
        eq_(data['blocked'], ['job2', 'job3'])
Example #14
0
    def test_CrontabberState(self):
        # The actual dates dont matter, but it matters that it's a
        # datetime.datetime object.

        def mocked_get(**options):
            dt = timezone.now()
            return {
                "state": {
                    "automatic-emails": {
                        "next_run": dt,
                        "first_run": dt,
                        "depends_on": [],
                        "last_run": dt,
                        "last_success": dt,
                        "error_count": 0,
                        "last_error": {}
                    },
                    "ftpscraper": {
                        "next_run": dt,
                        "first_run": dt,
                        "depends_on": [],
                        "last_run": dt,
                        "last_success": dt,
                        "error_count": 0,
                        "last_error": {}
                    }
                }
            }

        CrontabberState.implementation().get.side_effect = mocked_get

        url = reverse('api:model_wrapper', args=('CrontabberState',))
        response = self.client.get(url)
        eq_(response.status_code, 200)
        dump = json.loads(response.content)
        ok_(dump['state'])
Example #15
0
    def test_CrontabberState(self):
        # The actual dates dont matter, but it matters that it's a
        # datetime.datetime object.

        def mocked_get(**options):
            dt = timezone.now()
            return {
                "state": {
                    "automatic-emails": {
                        "next_run": dt,
                        "first_run": dt,
                        "depends_on": [],
                        "last_run": dt,
                        "last_success": dt,
                        "error_count": 0,
                        "last_error": {}
                    },
                    "ftpscraper": {
                        "next_run": dt,
                        "first_run": dt,
                        "depends_on": [],
                        "last_run": dt,
                        "last_success": dt,
                        "error_count": 0,
                        "last_error": {}
                    }
                }
            }

        CrontabberState.implementation().get.side_effect = mocked_get

        url = reverse('api:model_wrapper', args=('CrontabberState', ))
        response = self.client.get(url)
        assert response.status_code == 200
        dump = json.loads(response.content)
        assert dump['state']
Example #16
0
    def test_hit_or_not_hit_ratelimit(self):
        def mocked_get(**options):
            dt = timezone.now()
            return {
                "state": {
                    "automatic-emails": {
                        "next_run": dt,
                        "first_run": dt,
                        "depends_on": [],
                        "last_run": dt,
                        "last_success": dt,
                        "error_count": 0,
                        "last_error": {}
                    },
                    "ftpscraper": {
                        "next_run": dt,
                        "first_run": dt,
                        "depends_on": [],
                        "last_run": dt,
                        "last_success": dt,
                        "error_count": 0,
                        "last_error": {}
                    },
                }
            }

        CrontabberState.implementation().get.side_effect = mocked_get

        # doesn't matter much which model we use
        url = reverse('api:model_wrapper', args=('CrontabberState', ))

        response = self.client.get(url)
        assert response.status_code == 200
        with self.settings(API_RATE_LIMIT='3/m',
                           API_RATE_LIMIT_AUTHENTICATED='6/m'):
            current_limit = 3  # see above mentioned settings override
            # Double to avoid
            # https://bugzilla.mozilla.org/show_bug.cgi?id=1148470
            for __ in range(current_limit * 2):
                response = self.client.get(url,
                                           HTTP_X_FORWARDED_FOR='12.12.12.12')
            assert response.status_code == 429

            # But it'll work if you use a different X-Forwarded-For IP
            # because the rate limit is based on your IP address
            response = self.client.get(url, HTTP_X_FORWARDED_FOR='11.11.11.11')
            assert response.status_code == 200

            user = User.objects.create(username='******')
            token = Token.objects.create(user=user,
                                         notes="Just for avoiding rate limit")

            response = self.client.get(url, HTTP_AUTH_TOKEN=token.key)
            assert response.status_code == 200

            for __ in range(current_limit):
                response = self.client.get(url)
            assert response.status_code == 200

            # But even being signed in has a limit.
            authenticated_limit = 6  # see above mentioned settings override
            assert authenticated_limit > current_limit
            for __ in range(authenticated_limit * 2):
                response = self.client.get(url)
            # Even if you're authenticated - sure the limit is higher -
            # eventually you'll run into the limit there too.
            assert response.status_code == 429
Example #17
0
    def test_hit_or_not_hit_ratelimit(self):

        def mocked_get(**options):
            dt = timezone.now()
            return {
                "state": {
                    "automatic-emails": {
                        "next_run": dt,
                        "first_run": dt,
                        "depends_on": [],
                        "last_run": dt,
                        "last_success": dt,
                        "error_count": 0,
                        "last_error": {}
                    },
                    "ftpscraper": {
                        "next_run": dt,
                        "first_run": dt,
                        "depends_on": [],
                        "last_run": dt,
                        "last_success": dt,
                        "error_count": 0,
                        "last_error": {}
                    },
                }
            }

        CrontabberState.implementation().get.side_effect = mocked_get

        # doesn't matter much which model we use
        url = reverse('api:model_wrapper', args=('CrontabberState',))

        response = self.client.get(url)
        eq_(response.status_code, 200)
        with self.settings(
            API_RATE_LIMIT='3/m',
            API_RATE_LIMIT_AUTHENTICATED='6/m'
        ):
            current_limit = 3  # see above mentioned settings override
            # Double to avoid
            # https://bugzilla.mozilla.org/show_bug.cgi?id=1148470
            for __ in range(current_limit * 2):
                response = self.client.get(url)
            eq_(response.status_code, 429)

            # But it'll work if you use a different X-Forwarded-For IP
            # because the rate limit is based on your IP address
            response = self.client.get(url, HTTP_X_FORWARDED_FOR='11.11.11.11')
            eq_(response.status_code, 200)

            user = User.objects.create(username='******')
            token = Token.objects.create(
                user=user,
                notes="Just for avoiding rate limit"
            )

            response = self.client.get(url, HTTP_AUTH_TOKEN=token.key)
            eq_(response.status_code, 200)

            for __ in range(current_limit):
                response = self.client.get(url)
            eq_(response.status_code, 200)

            # But even being signed in has a limit.
            authenticated_limit = 6  # see above mentioned settings override
            assert authenticated_limit > current_limit
            for __ in range(authenticated_limit * 2):
                response = self.client.get(url)
            # Even if you're authenticated - sure the limit is higher -
            # eventually you'll run into the limit there too.
            eq_(response.status_code, 429)