addons-server/apps/market/tasks.py

182 строки
6.6 KiB
Python

import json
import logging
import time
from django.conf import settings
from django.core.cache import cache
from tower import ugettext as _
from addons.models import Addon
import amo
from amo.helpers import absolutify
from amo.utils import send_mail
from market.models import PaypalCheckStatus
from paypal.check import Check
from users.utils import get_task_user
from celeryutils import task
from jingo import env
from waffle import Sample, switch_is_active
log = logging.getLogger('z.market.task')
key = 'amo:market:check_paypal'
failures = '%s:failure' % key
passes = '%s:passes' % key
def check_paypal_multiple(ids, limit=None):
"""
Setup redis so that we can store the count of addons tested and failed
and spot when the test is completed.
ids: Is the add-on ids we'll be checking.
limit: The percentage of add-ons to change before we assume something
has gone wrong. Setting this to zero (not recommended) disables it.
If not set, we'll use the paypal-disabled-limit waffle sample
value.
"""
if limit is None:
limit = Sample.objects.get(name='paypal-disabled-limit').percent
log.info('Starting run of paypal addon checks: %s' % len(ids))
cache.set(key, [time.time(), len(ids), float(limit)])
PaypalCheckStatus.objects.all().delete()
return ids
def _check_paypal_completed():
started, count, limit = cache.get(key)
limit, count = float(limit), int(count)
if PaypalCheckStatus.objects.count() < count:
return False
failed = (PaypalCheckStatus.objects
.filter(failure_data__isnull=False))
passed = (PaypalCheckStatus.objects
.filter(failure_data__isnull=True)
.count())
context = {'checked': count,
'failed_addons': failed,
'failed': len(failed),
'passed': passed,
'limit': limit, 'rate': (len(failed) / float(count)) * 100,
'do_disable': switch_is_active('paypal-disable')}
_notify(context)
return True
def _notify(context):
"""
Notify the admins or the developers what happened. Performing a sanity
check in case something went wrong.
"""
log.info('Completed run of paypal addon checks: %s' % context['checked'])
failure_list = []
if context['limit'] and context['rate'] > context['limit']:
# This is too cope with something horrible going wrong like, paypal
# goes into maintenance mode, or netops changes something and all of
# a sudden all the apps start failing their paypal checks.
#
# It would really suck if one errant current job disabled every app
# on the Marketplace. So this is an attempt to sanity check this.
for f in context['failed_addons']:
failure_list.append(absolutify(f.addon.get_url_path()))
context['failure_list'] = failure_list
log.info('Too many failed: %s%%, aborting.' % context['limit'])
template = 'market/emails/check_error.txt'
send_mail('Cron job error on checking addons',
env.get_template(template).render(context),
recipient_list=[settings.FLIGTAR],
from_email=settings.NOBODY_EMAIL)
else:
if not context['failed_addons']:
return
for f in context['failed_addons']:
addon = f.addon
url = absolutify(addon.get_url_path())
# Add this to a list so we can tell the admins who got disabled.
failure_list.append(url)
if not context['do_disable']:
continue
# Write to the developers log that it failed to pass and update
# the status of the addon.
amo.log(amo.LOG.PAYPAL_FAILED, addon, user=get_task_user())
addon.update(status=amo.STATUS_DISABLED)
authors = [u.email for u in addon.authors.all()]
if addon.is_webapp():
template = 'market/emails/check_developer_app.txt'
subject = _('App disabled on the Firefox Marketplace.')
else:
template = 'market/emails/check_developer_addon.txt'
subject = _('Add-on disabled on the Firefox Marketplace.')
# Now email the developer and tell them the bad news.
send_mail(subject,
env.get_template(template).render({
'addon': addon,
'errors': json.loads(f.failure_data)
}),
recipient_list=authors,
from_email=settings.NOBODY_EMAIL)
context['failure_list'] = failure_list
# Now email the admins and tell them what happened.
template = 'market/emails/check_summary.txt'
send_mail('Cron job disabled %s add-ons' % context['failed'],
env.get_template(template).render(context),
recipient_list=[settings.FLIGTAR],
from_email=settings.NOBODY_EMAIL)
@task(rate_limit='4/m')
def check_paypal(ids, check=None, **kw):
"""
Checks an addon against PayPal for the following things:
- that the refund token is still there
- that they have enough currency
If the addon fails any of these tests then we'll log, email the developer
and alter the apps status so that it can't be bought.
This will be doing lots of pings to PayPal so I expect it to be pretty
slow.
"""
log.info('[%s@%s] checking paypal for addons starting with: %s'
% (len(ids), check_paypal.rate_limit, ids[0]))
if check is None:
check = Check
for id in ids:
try:
addon = Addon.objects.get(pk=id)
except Addon.DoesNotExist:
log.info('No addon: %s' % id)
continue
try:
log.info('Checking paypal for: %s' % addon.id)
result = check(addon=addon)
result.all()
if result.passed:
PaypalCheckStatus.objects.create(addon=addon)
log.info('Paypal checks all passed for: %s' % id)
else:
PaypalCheckStatus.objects.create(
addon=addon,
failure_data=json.dumps(result.errors))
log.info('Paypal checks failed for: %s' % id)
except:
# Note I'm marking this as a pass, because marking it as a failure
# would cause the app to be marked as disabled and I think that's
# the wrong thing to do.
log.error('Paypal check failed: %s' % id, exc_info=True)
PaypalCheckStatus.objects.create(addon=addon)
# Finally call to see if we've finished.
_check_paypal_completed()