Skip to content

Commit

Permalink
fix bug 1501780: fix b99 and reduce buildhub hits
Browse files Browse the repository at this point in the history
ftpscraper would generate a product_versions record for X.0b99 which is a
"final beta". This fixes the BetaVersionRule to do a lookup for
(product, build id, beta) channel and if that returns nothing and the
version is possibly a final beta version, it will do a second lookup for
(product, build id, release) and if that returns something, it adds a b99
to the end of the version string.

Buildhub only stores build information for Firefox and Fennec, so we shouldn't
bother hitting it for other products. This fixes that.

This also splits up the monolithic "test_everything" into seprate tests.
  • Loading branch information
willkg committed Oct 29, 2018
1 parent 5fa797a commit 467e83b
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 43 deletions.
42 changes: 34 additions & 8 deletions socorro/processor/mozilla_transform_rules.py
Expand Up @@ -625,6 +625,9 @@ class BetaVersionRule(Rule):
#: If we know it's good, cache it for 24 hours because it won't change
LONG_CACHE_TTL = 60 * 60 * 24

#: Products that are on Buildhub--we don't want to ask Buildhub about others
BUILDHUB_PRODUCTS = ['firefox', 'fennec', 'fennecandroid']

def __init__(self, config):
super(BetaVersionRule, self).__init__(config)
# NOTE(willkg): These config values come from Processor2015 instance.
Expand Down Expand Up @@ -660,6 +663,11 @@ def _get_version_data(self, product, build_id, channel):
if (product, channel) == ('firefox', 'aurora') and build_id > '20170601':
product = 'devedition'

# Buildhub product is "fennec", not "fennecandroid", so we have to switch
# it
if product == 'fennecandroid':
product = 'fennec'

key = '%s:%s:%s' % (product, build_id, channel)
if key in self.cache:
return self.cache[key]
Expand Down Expand Up @@ -697,34 +705,52 @@ def _get_version_data(self, product, build_id, channel):

return None

def is_final_beta(self, version):
"""Denotes whether this version could be a final beta"""
# NOTE(willkg): this started with 26.0 and 38.0.5 was an out-of-cycle
# exception
return version > 26.0 and (version.endswith('.0') or version == '38.0.5')

def _predicate(self, raw_crash, raw_dumps, processed_crash, proc_meta):
# Beta and aurora versions send the wrong version in the crash report,
# so we need to fix them
return processed_crash.get('release_channel', '').lower() in ('beta', 'aurora')

def _action(self, raw_crash, raw_dumps, processed_crash, processor_meta):
product = processed_crash.get('product').strip()
product = processed_crash.get('product').strip().lower()
try:
# Convert the build id to an int as a hand-wavey validity check
build_id = int(processed_crash.get('build').strip())
except ValueError:
build_id = None
release_channel = processed_crash.get('release_channel').strip()

# If we're missing one of the magic ingredients, then there's nothing
# to do
if product and build_id and release_channel:
if product and build_id and release_channel and product in self.BUILDHUB_PRODUCTS:
# Convert the build_id to a str for lookups
build_id = str(build_id)

try:
real_version = self._get_version_data(
product.lower(),
str(build_id),
release_channel
)
real_version = self._get_version_data(product, build_id, release_channel)

# If we got a real version, toss that in and we're done
if real_version:
processed_crash['version'] = real_version
return True

# We didn't get a version from Buildhub, but this might be a
# "final beta" which Buildhub has an entry for in the release
# channel. So we check Buildhub asking about the release
# channel and if it's there, we tack on a b99.
version = processed_crash.get('version').strip()
if version and release_channel == 'beta' and self.is_final_beta(version):
real_version = self._get_version_data(product, build_id, 'release')

if real_version:
processed_crash['version'] = real_version + 'b99'
return True

except RequestException as exc:
processor_meta.processor_notes.append('could not connect to Buildhub')
self.config.logger.exception(
Expand All @@ -736,7 +762,7 @@ def _action(self, raw_crash, raw_dumps, processed_crash, processor_meta):
processed_crash['version'] += 'b0'
processor_meta.processor_notes.append(
'release channel is %s but no version data was found - added "b0" '
'suffix to version number' % processed_crash['release_channel']
'suffix to version number' % release_channel
)

return True
Expand Down
177 changes: 142 additions & 35 deletions socorro/unittest/processor/test_mozilla_transform_rules.py
Expand Up @@ -1295,34 +1295,18 @@ def test_missing_key_2(self):
class TestBetaVersionRule:
API_URL = 'http://buildhub.example.com/v1/buckets/build-hub/collections/releases/records'

def test_everything_we_hoped_for(self):
def test_beta_channel_known_version(self):
# Beta channel with known version gets converted correctly
config = get_basic_config()
config.buildhub_api = self.API_URL

raw_crash = {}
raw_dumps = {}

# Release channel--doesn't trigger rule
with requests_mock.Mocker() as req_mock:
processed_crash = {
'product': 'WaterWolf',
'version': '2.0',
'release_channel': 'release',
'build': '20000801101010',
}

processor_meta = get_basic_processor_meta()

rule = BetaVersionRule(config)
rule.act(raw_crash, raw_dumps, processed_crash, processor_meta)
assert processed_crash['version'] == '2.0'
assert len(processor_meta.processor_notes) == 0

# A normal beta crash, with a known version
with requests_mock.Mocker() as req_mock:
req_mock.get(
self.API_URL + '?' + urlencode({
'source.product': 'WaterWolf',
'source.product': 'firefox',
'build.id': '"20001001101010"',
'target.channel': 'beta',
'_limit': 1
Expand All @@ -1339,7 +1323,7 @@ def test_everything_we_hoped_for(self):
)

processed_crash = {
'product': 'WaterWolf',
'product': 'Firefox',
'version': '3.0',
'release_channel': 'beta',
'build': '20001001101010',
Expand All @@ -1352,10 +1336,41 @@ def test_everything_we_hoped_for(self):
assert processed_crash['version'] == '3.0b1'
assert processor_meta.processor_notes == []

def test_release_channel(self):
# Release channel ignored
config = get_basic_config()
config.buildhub_api = self.API_URL

raw_crash = {}
raw_dumps = {}

# Release channel--doesn't trigger rule
with requests_mock.Mocker():
processed_crash = {
'product': 'Firefox',
'version': '2.0',
'release_channel': 'release',
'build': '20000801101010',
}

processor_meta = get_basic_processor_meta()

rule = BetaVersionRule(config)
rule.act(raw_crash, raw_dumps, processed_crash, processor_meta)
assert processed_crash['version'] == '2.0'
assert len(processor_meta.processor_notes) == 0

def test_nightly_channel(self):
# A nightly--doesn't trigger rule
with requests_mock.Mocker() as req_mock:
config = get_basic_config()
config.buildhub_api = self.API_URL

raw_crash = {}
raw_dumps = {}

with requests_mock.Mocker():
processed_crash = {
'product': 'WaterWolf',
'product': 'Firefox',
'version': '5.0a1',
'release_channel': 'nightly',
'build': '20000105101010',
Expand All @@ -1368,10 +1383,17 @@ def test_everything_we_hoped_for(self):
assert processed_crash['version'] == '5.0a1'
assert processor_meta.processor_notes == []

def test_bad_buildid(self):
# Bad buildid doesn't error out
with requests_mock.Mocker() as req_mock:
config = get_basic_config()
config.buildhub_api = self.API_URL

raw_crash = {}
raw_dumps = {}

with requests_mock.Mocker():
processed_crash = {
'product': 'WaterWolf',
'product': 'Firefox',
'version': '5.0',
'release_channel': 'beta',
'build': '2",381,,"',
Expand All @@ -1387,11 +1409,18 @@ def test_everything_we_hoped_for(self):
'added "b0" suffix to version number'
]

# beta crash with an unknown version gets a special mark.
def test_beta_channel_unknown_version(self):
# beta crash with an unknown version gets b0
config = get_basic_config()
config.buildhub_api = self.API_URL

raw_crash = {}
raw_dumps = {}

with requests_mock.Mocker() as req_mock:
req_mock.get(
self.API_URL + '?' + urlencode({
'source.product': 'Waterwolf',
'source.product': 'firefox',
'build.id': '"220000101101011"',
'target.channel': 'beta',
'_limit': 1
Expand All @@ -1402,7 +1431,34 @@ def test_everything_we_hoped_for(self):
)

processed_crash = {
'product': 'WaterWolf',
'product': 'Firefox',
'version': '3.0.1',
'release_channel': 'beta',
'build': '220000101101011',
}

processor_meta = get_basic_processor_meta()

rule = BetaVersionRule(config)
rule.act(raw_crash, raw_dumps, processed_crash, processor_meta)
assert processed_crash['version'] == '3.0.1b0'
assert processor_meta.processor_notes == [
'release channel is beta but no version data was found - '
'added "b0" suffix to version number'
]

def test_beta_channel_non_buildhub_product(self):
# beta crash with a a product not on Buildhub gets b0 without hitting
# Buildhub
config = get_basic_config()
config.buildhub_api = self.API_URL

raw_crash = {}
raw_dumps = {}

with requests_mock.Mocker():
processed_crash = {
'product': 'Waterwolf',
'version': '3.0',
'release_channel': 'beta',
'build': '220000101101011',
Expand All @@ -1418,22 +1474,18 @@ def test_everything_we_hoped_for(self):
'added "b0" suffix to version number'
]

def test_with_aurora_channel(self):
def test_aurora_channel(self):
"""Verify this works with aurora channel"""
config = get_basic_config()
config.buildhub_api = self.API_URL

raw_crash = copy.copy(canonical_standard_raw_crash)
raw_crash = {}
raw_dumps = {}
processed_crash = DotDict()
processed_crash.date_processed = '2014-12-31'
processed_crash.product = 'WaterWolf'

# A normal aurora crash, with a known version.
with requests_mock.Mocker() as req_mock:
req_mock.get(
self.API_URL + '?' + urlencode({
'source.product': 'WaterWolf',
'source.product': 'firefox',
'build.id': '"20001001101010"',
'target.channel': 'aurora',
'_limit': 1
Expand All @@ -1450,7 +1502,7 @@ def test_with_aurora_channel(self):
)

processed_crash = {
'product': 'WaterWolf',
'product': 'Firefox',
'version': '3.0',
'release_channel': 'aurora',
'build': '20001001101010',
Expand All @@ -1463,6 +1515,61 @@ def test_with_aurora_channel(self):
assert processed_crash['version'] == '3.0b1'
assert processor_meta.processor_notes == []

def test_b99(self):
"""Verify the switcheroo for b99 final beta"""
config = get_basic_config()
config.buildhub_api = self.API_URL

raw_crash = {}
raw_dumps = {}

with requests_mock.Mocker() as req_mock:
# Request for beta yields no data
req_mock.get(
self.API_URL + '?' + urlencode({
'source.product': 'firefox',
'build.id': '"20181018182531"',
'target.channel': 'beta',
'_limit': 1
}),
json={
'data': []
}
)

# Request for release yields release version number
req_mock.get(
self.API_URL + '?' + urlencode({
'source.product': 'firefox',
'build.id': '"20181018182531"',
'target.channel': 'release',
'_limit': 1
}),
json={
'data': [
{
'target': {
'version': '63.0'
}
}
]
}
)

processed_crash = {
'product': 'Firefox',
'version': '63.0',
'release_channel': 'beta',
'build': '20181018182531',
}

processor_meta = get_basic_processor_meta()

rule = BetaVersionRule(config)
rule.act(raw_crash, raw_dumps, processed_crash, processor_meta)
assert processed_crash['version'] == '63.0b99'
assert processor_meta.processor_notes == []


class TestOsPrettyName(TestCase):
def test_everything_we_hoped_for(self):
Expand Down

0 comments on commit 467e83b

Please sign in to comment.