Skip to content

Commit ef520d5

Browse files
committed
WIP tests
1 parent 66e0da1 commit ef520d5

32 files changed

+206
-1531
lines changed

src/daisy/submit.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,9 @@
2828
from cassandra import WriteTimeout
2929
from cassandra.cqlengine.query import DoesNotExist
3030

31-
from daisy import utils
3231
from daisy.metrics import get_metrics
33-
from errortracker import cassandra_schema
32+
from errortracker import cassandra_schema, oopses, utils
3433
from errortracker.cassandra import cassandra_session
35-
from oopsrepository import oopses
3634

3735
metrics = get_metrics("daisy.%s" % socket.gethostname())
3836
logger = logging.getLogger("gunicorn.error")

src/daisy/submit_core.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import logging
2020
import os
2121
import random
22-
import shutil
2322
import socket
2423
from datetime import datetime
2524
from pathlib import Path
@@ -29,9 +28,7 @@
2928

3029
# from daisy import config
3130
from daisy.metrics import get_metrics
32-
from errortracker import cassandra_schema, config
33-
34-
from . import utils
31+
from errortracker import amqp_utils, cassandra_schema, config
3532

3633
metrics = get_metrics("daisy.%s" % socket.gethostname())
3734
logger = logging.getLogger("gunicorn.error")
@@ -247,8 +244,8 @@ def write_to_amqp(message, arch):
247244
connection = amqp.Connection(host=config.amqp_host)
248245
connection.connect()
249246
channel = connection.channel()
250-
except utils.amqplib_error_types as e:
251-
if utils.is_amqplib_connection_error(e):
247+
except amqp_utils.amqplib_error_types as e:
248+
if amqp_utils.is_amqplib_connection_error(e):
252249
# Could not connect
253250
return False
254251
# Unknown error mode : don't hide it.
@@ -267,8 +264,8 @@ def write_to_amqp(message, arch):
267264
msg = "%s added to %s queue" % (message.split(":")[0], queue)
268265
logger.info(msg)
269266
queued = True
270-
except utils.amqplib_error_types as e:
271-
if utils.is_amqplib_connection_error(e):
267+
except amqp_utils.amqplib_error_types as e:
268+
if amqp_utils.is_amqplib_connection_error(e):
272269
# Could not connect / interrupted connection
273270
queued = False
274271
# Unknown error mode : don't hide it.

src/errortracker/amqp_utils.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import socket
2+
3+
from amqp import ConnectionError as AMQPConnectionException
4+
5+
# From oops-amqp
6+
# These exception types always indicate an AMQP connection error/closure.
7+
# However you should catch amqplib_error_types and post-filter with
8+
# is_amqplib_connection_error.
9+
amqplib_connection_errors = (socket.error, AMQPConnectionException)
10+
# A tuple to reduce duplication in different code paths. Lists the types of
11+
# exceptions legitimately raised by amqplib when the AMQP server goes down.
12+
# Not all exceptions *will* be such errors - use is_amqplib_connection_error to
13+
# do a second-stage filter after catching the exception.
14+
amqplib_error_types = amqplib_connection_errors + (IOError,)
15+
16+
17+
# From oops-amqp
18+
def is_amqplib_ioerror(e):
19+
"""Returns True if e is an amqplib internal exception."""
20+
# Raised by amqplib rather than socket.error on ssl issues and short reads.
21+
if type(e) is not IOError:
22+
return False
23+
if e.args == ("Socket error",) or e.args == ("Socket closed",):
24+
return True
25+
return False
26+
27+
28+
# From oops-amqp
29+
def is_amqplib_connection_error(e):
30+
"""Return True if e was (probably) raised due to a connection issue."""
31+
return isinstance(e, amqplib_connection_errors) or is_amqplib_ioerror(e)

src/errortracker/cassandra.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,22 @@
1111
_session = None
1212

1313

14-
def setup_cassandra():
14+
def setup_cassandra(keyspace="crashdb", replication_factor=3):
1515
# cassandra wants this environment variable to be set, otherwise issues a warning. Let's please it.
1616
os.environ["CQLENG_ALLOW_SCHEMA_MANAGEMENT"] = "1"
1717
auth_provider = PlainTextAuthProvider(
1818
username=config.cassandra_username, password=config.cassandra_password
1919
)
2020
connection.setup(
2121
config.cassandra_hosts,
22-
"crashdb",
22+
keyspace,
2323
auth_provider=auth_provider,
2424
load_balancing_policy=RoundRobinPolicy(),
2525
protocol_version=4,
2626
)
27-
management.create_keyspace_simple("crashdb", 3)
27+
management.create_keyspace_simple(name=keyspace, replication_factor=replication_factor)
2828
sync_schema()
29-
# connection.execute("USE crashdb;")
29+
# connection.execute(f"USE {keyspace};")
3030

3131

3232
def sync_schema():

src/errortracker/cassandra_schema.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55

66
class ErrorTrackerTable(models.Model):
7-
__keyspace__ = "crashdb"
7+
# __table_name_case_sensitive__ is deprecated already, but let's keep it in case we run on older machines.
88
__table_name_case_sensitive__ = True
99
__abstract__ = True
1010

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@
1313
import uuid
1414
from hashlib import md5, sha1
1515

16-
from cassandra import ConsistencyLevel
17-
from cassandra.auth import PlainTextAuthProvider
18-
from cassandra.cluster import Cluster
1916
from cassandra.query import SimpleStatement
17+
from cassandra.cqlengine.query import BatchQuery
2018

2119
from errortracker import cassandra_schema
2220
from errortracker.cassandra import cassandra_session
@@ -27,7 +25,33 @@
2725
_cassandra_session = None
2826

2927

30-
def insert(config, oopsid, oops_json, user_token=None, fields=None, proposed_pkg=False):
28+
def prune():
29+
"""Remove OOPSES that are over 30 days old."""
30+
# Find days to prune
31+
days = set()
32+
prune_to = time.strftime("%Y%m%d", time.gmtime(time.time() - MONTH))
33+
for dayoops in cassandra_schema.DayOOPS.objects.distinct(["key"]):
34+
key = dayoops.key.decode()
35+
if key < prune_to:
36+
days.add(key)
37+
if not days:
38+
return
39+
# collect all the oopses (buffers all in memory; may want to make
40+
# incremental in future)
41+
for day in days:
42+
oops_ids = list(
43+
set(
44+
dayoops.value
45+
for dayoops in cassandra_schema.DayOOPS.filter(key=day.encode()).only(["value"])
46+
)
47+
)
48+
with BatchQuery() as b:
49+
cassandra_schema.DayOOPS.objects.batch(b).filter(key=day.encode()).delete()
50+
for id in oops_ids:
51+
cassandra_schema.OOPS.objects.batch(b).filter(key=id).delete()
52+
53+
54+
def insert(oopsid, oops_json, user_token=None, fields=None, proposed_pkg=False) -> str:
3155
"""Insert an OOPS into the system.
3256
3357
:return: The day which the oops was filed under.
@@ -39,8 +63,8 @@ def insert(config, oopsid, oops_json, user_token=None, fields=None, proposed_pkg
3963
assert isinstance(oops_dict, dict)
4064
insert_dict = {}
4165
for key, value in list(oops_dict.items()):
42-
insert_dict[key] = json.dumps(value)
43-
return _insert(config, oopsid, insert_dict, user_token, fields, proposed_pkg)
66+
insert_dict[key] = str(value)
67+
return _insert(oopsid, insert_dict, user_token, fields, proposed_pkg)
4468

4569

4670
def insert_dict(
@@ -50,7 +74,7 @@ def insert_dict(
5074
fields=None,
5175
proposed_pkg=False,
5276
ttl=None,
53-
):
77+
) -> str:
5478
"""Insert an OOPS into the system.
5579
5680
:return: The day which the oops was filed under.
@@ -66,7 +90,7 @@ def _insert(
6690
fields=None,
6791
proposed_pkg=False,
6892
ttl=None,
69-
):
93+
) -> str:
7094
"""Internal function. Do not call this directly.
7195
7296
:param oopsid: The identifier for this OOPS.
@@ -91,7 +115,7 @@ def _insert(
91115
cassandra_schema.OOPS.create(key=oopsid.encode(), column1=key, value=value).ttl(ttl)
92116

93117
automated_testing = False
94-
if user_token.startswith("deadbeef"):
118+
if user_token and user_token.startswith("deadbeef"):
95119
automated_testing = True
96120

97121
cassandra_schema.DayOOPS.create(key=day_key.encode(), column1=now_uuid, value=oopsid.encode())
Lines changed: 2 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,10 @@
11
import logging
22
import re
3-
import socket
43
import uuid
54

65
import apt
7-
from amqp import ConnectionError as AMQPConnectionException
86

9-
from oopsrepository import oopses
10-
11-
# From oops-amqp
12-
# These exception types always indicate an AMQP connection error/closure.
13-
# However you should catch amqplib_error_types and post-filter with
14-
# is_amqplib_connection_error.
15-
amqplib_connection_errors = (socket.error, AMQPConnectionException)
16-
# A tuple to reduce duplication in different code paths. Lists the types of
17-
# exceptions legitimately raised by amqplib when the AMQP server goes down.
18-
# Not all exceptions *will* be such errors - use is_amqplib_connection_error to
19-
# do a second-stage filter after catching the exception.
20-
amqplib_error_types = amqplib_connection_errors + (IOError,)
7+
from errortracker import oopses
218

229
EOL_RELEASES = {
2310
"Ubuntu 10.04": "lucid",
@@ -193,18 +180,6 @@ def bucket(oops_config, oops_id, crash_signature, report_dict):
193180
oopses.update_errors_by_release(oops_config, oops_uuid, system_uuid, release)
194181

195182

196-
def attach_error_report(report, context):
197-
# We only attach error report that was submitted by the client if we've hit
198-
# a MaximumRetryException from Cassandra.
199-
if "type" in report and report["type"] == "MaximumRetryException":
200-
env = context["wsgi_environ"]
201-
if "wsgi.input.decoded" in env:
202-
data = env["wsgi.input.decoded"]
203-
if "req_vars" not in report:
204-
report["req_vars"] = {}
205-
report["req_vars"]["wsgi.input.decoded"] = data
206-
207-
208183
def retraceable_release(release):
209184
if release in EOL_RELEASES:
210185
logging.info("%s is EoL, not retraceable", release)
@@ -237,31 +212,7 @@ def blocklisted_device(system_token):
237212
Used for devices that have repeatedly failed to submit a crash.
238213
"""
239214

240-
blocklist = [
241-
# 20150814 - OOPS count was at 43
242-
"2f175cea621bda810f267f1da46409a111f58011435f410aa198362e9372da78b6fafe6827ff26e025a5ab7d2859346de6b188f0622118c15a119c58ca538acb",
243-
# 20150826 - OOPS count was at 18
244-
"81b75a0bdd531a5c02a4455b05674ea45fbb65324bcae5fe51659bce850aa40bcd1388e3eed4d46ce9abb4e56d1dd7dde45ded473995feb0ac2c01518a841efe",
245-
# 20150903 - OOPS count was at 27
246-
"b5329547bdab8adea4245399ff9656ca608e825425fbb0ad2c68e182b75ce80c13f9186e4e9b8e7a17dd15dd196b12a65e1b7f513184296320dad50c587754f5",
247-
]
215+
blocklist = []
248216
if system_token in blocklist:
249217
return True
250218
return False
251-
252-
253-
# From oops-amqp
254-
def is_amqplib_ioerror(e):
255-
"""Returns True if e is an amqplib internal exception."""
256-
# Raised by amqplib rather than socket.error on ssl issues and short reads.
257-
if type(e) is not IOError:
258-
return False
259-
if e.args == ("Socket error",) or e.args == ("Socket closed",):
260-
return True
261-
return False
262-
263-
264-
# From oops-amqp
265-
def is_amqplib_connection_error(e):
266-
"""Return True if e was (probably) raised due to a connection issue."""
267-
return isinstance(e, amqplib_connection_errors) or is_amqplib_ioerror(e)

src/oopsrepository/DESIGN.txt

Lines changed: 0 additions & 79 deletions
This file was deleted.

0 commit comments

Comments
 (0)