OLD | NEW |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import json | 5 import json |
6 import logging | 6 import logging |
7 import operator | 7 import operator |
8 | 8 |
9 from appengine_url_fetcher import AppEngineUrlFetcher | 9 from appengine_url_fetcher import AppEngineUrlFetcher |
10 import url_constants | 10 import url_constants |
11 | 11 |
| 12 class ChannelInfo(object): |
| 13 def __init__(self, channel, branch, version): |
| 14 self.channel = channel |
| 15 self.branch = branch |
| 16 self.version = version |
| 17 |
12 class BranchUtility(object): | 18 class BranchUtility(object): |
13 def __init__(self, fetch_url, fetcher, object_store_creator): | 19 def __init__(self, fetch_url, history_url, fetcher, object_store_creator): |
14 self._fetch_url = fetch_url | |
15 self._fetcher = fetcher | 20 self._fetcher = fetcher |
16 # BranchUtility is obviously cross-channel, so set the channel to None. | 21 # BranchUtility is obviously cross-channel, so set the channel to None. |
17 self._object_store = object_store_creator.Create(BranchUtility, | 22 self._branch_object_store = object_store_creator.Create(BranchUtility, |
18 channel=None) | 23 category='branch', |
| 24 channel=None) |
| 25 self._version_object_store = object_store_creator.Create(BranchUtility, |
| 26 category='version', |
| 27 channel=None) |
| 28 self._fetch_result = self._fetcher.FetchAsync(fetch_url) |
| 29 self._history_result = self._fetcher.FetchAsync(history_url) |
| 30 |
| 31 @staticmethod |
| 32 def GetAllChannelNames(): |
| 33 return ('stable', 'beta', 'dev', 'trunk') |
| 34 |
| 35 @staticmethod |
| 36 def NewestChannel(channels): |
| 37 for channel in reversed(BranchUtility.GetAllChannelNames()): |
| 38 if channel in channels: |
| 39 return channel |
19 | 40 |
20 @staticmethod | 41 @staticmethod |
21 def Create(object_store_creator): | 42 def Create(object_store_creator): |
22 return BranchUtility(url_constants.OMAHA_PROXY_URL, | 43 return BranchUtility(url_constants.OMAHA_PROXY_URL, |
| 44 url_constants.OMAHA_DEV_HISTORY, |
23 AppEngineUrlFetcher(), | 45 AppEngineUrlFetcher(), |
24 object_store_creator) | 46 object_store_creator) |
25 | 47 |
26 @staticmethod | 48 @staticmethod |
27 def GetAllChannelNames(): | |
28 return ['stable', 'beta', 'dev', 'trunk'] | |
29 | |
30 @staticmethod | |
31 def SplitChannelNameFromPath(path): | 49 def SplitChannelNameFromPath(path): |
32 """Splits the channel name out of |path|, returning the tuple | 50 '''Splits the channel name out of |path|, returning the tuple |
33 (channel_name, real_path). If the channel cannot be determined then returns | 51 (channel_name, real_path). If the channel cannot be determined then returns |
34 (None, path). | 52 (None, path). |
35 """ | 53 ''' |
36 if '/' in path: | 54 if '/' in path: |
37 first, second = path.split('/', 1) | 55 first, second = path.split('/', 1) |
38 else: | 56 else: |
39 first, second = (path, '') | 57 first, second = (path, '') |
40 if first in ['trunk', 'dev', 'beta', 'stable']: | 58 if first in BranchUtility.GetAllChannelNames(): |
41 return (first, second) | 59 return (first, second) |
42 return (None, path) | 60 return (None, path) |
43 | 61 |
44 def GetBranchForChannel(self, channel_name): | 62 def GetAllBranchNumbers(self): |
45 """Returns the branch number for a channel name. | 63 return ((channel, self.GetChannelInfo(channel).branch) |
46 """ | 64 for channel in BranchUtility.GetAllChannelNames()) |
| 65 |
| 66 def GetAllVersionNumbers(self): |
| 67 return (self.GetChannelInfo(channel).version |
| 68 for channel in BranchUtility.GetAllChannelNames()) |
| 69 |
| 70 def GetAllChannelInfo(self): |
| 71 return (self.GetChannelInfo(channel) |
| 72 for channel in BranchUtility.GetAllChannelNames()) |
| 73 |
| 74 |
| 75 def GetChannelInfo(self, channel): |
| 76 return ChannelInfo(channel, |
| 77 self._ExtractFromVersionJson(channel, 'branch'), |
| 78 self._ExtractFromVersionJson(channel, 'version')) |
| 79 |
| 80 def _ExtractFromVersionJson(self, channel_name, data_type): |
| 81 '''Returns the branch or version number for a channel name. |
| 82 ''' |
47 if channel_name == 'trunk': | 83 if channel_name == 'trunk': |
48 return 'trunk' | 84 return 'trunk' |
49 | 85 |
50 branch_number = self._object_store.Get(channel_name).Get() | 86 if data_type == 'branch': |
51 if branch_number is not None: | 87 object_store = self._branch_object_store |
52 return branch_number | 88 elif data_type == 'version': |
| 89 object_store = self._version_object_store |
| 90 |
| 91 data = object_store.Get(channel_name).Get() |
| 92 if data is not None: |
| 93 return data |
53 | 94 |
54 try: | 95 try: |
55 version_json = json.loads(self._fetcher.Fetch(self._fetch_url).content) | 96 version_json = json.loads(self._fetch_result.Get().content) |
56 except Exception as e: | 97 except Exception as e: |
57 # This can happen if omahaproxy is misbehaving, which we've seen before. | 98 # This can happen if omahaproxy is misbehaving, which we've seen before. |
58 # Quick hack fix: just serve from trunk until it's fixed. | 99 # Quick hack fix: just serve from trunk until it's fixed. |
59 logging.error('Failed to fetch or parse branch from omahaproxy: %s! ' | 100 logging.error('Failed to fetch or parse branch from omahaproxy: %s! ' |
60 'Falling back to "trunk".' % e) | 101 'Falling back to "trunk".' % e) |
61 return 'trunk' | 102 return 'trunk' |
62 | 103 |
63 branch_numbers = {} | 104 numbers = {} |
64 for entry in version_json: | 105 for entry in version_json: |
65 if entry['os'] not in ['win', 'linux', 'mac', 'cros']: | 106 if entry['os'] not in ['win', 'linux', 'mac', 'cros']: |
66 continue | 107 continue |
67 for version in entry['versions']: | 108 for version in entry['versions']: |
68 if version['channel'] != channel_name: | 109 if version['channel'] != channel_name: |
69 continue | 110 continue |
70 branch = version['version'].split('.')[2] | 111 if data_type == 'branch': |
71 if branch not in branch_numbers: | 112 number = version['version'].split('.')[2] |
72 branch_numbers[branch] = 0 | 113 elif data_type == 'version': |
| 114 number = version['version'].split('.')[0] |
| 115 if number not in numbers: |
| 116 numbers[number] = 0 |
73 else: | 117 else: |
74 branch_numbers[branch] += 1 | 118 numbers[number] += 1 |
75 | 119 |
76 sorted_branches = sorted(branch_numbers.iteritems(), | 120 sorted_numbers = sorted(numbers.iteritems(), |
77 None, | 121 None, |
78 operator.itemgetter(1), | 122 operator.itemgetter(1), |
79 True) | 123 True) |
80 self._object_store.Set(channel_name, sorted_branches[0][0]) | 124 object_store.Set(channel_name, int(sorted_numbers[0][0])) |
| 125 return int(sorted_numbers[0][0]) |
81 | 126 |
82 return sorted_branches[0][0] | 127 def GetBranchForVersion(self, version): |
| 128 '''Returns the most recent branch number for a given chrome version number |
| 129 using data stored on omahaproxy (see url_constants). |
| 130 ''' |
| 131 if version == 'trunk': |
| 132 return 'trunk' |
| 133 |
| 134 branch = self._branch_object_store.Get(version).Get() |
| 135 if branch is not None: |
| 136 return branch |
| 137 |
| 138 version_json = json.loads(self._history_result.Get().content) |
| 139 for entry in version_json['events']: |
| 140 # Here, entry['title'] looks like: 'title - version#.#.branch#.#' |
| 141 version_title = entry['title'].split(' - ')[1].split('.') |
| 142 if version_title[0] == str(version): |
| 143 self._branch_object_store.Set(str(version), version_title[2]) |
| 144 return int(version_title[2]) |
| 145 |
| 146 raise ValueError( |
| 147 'The branch number for %s could not be found.' % version) |
| 148 |
| 149 def GetLatestVersionNumber(self): |
| 150 '''Returns the most recent version number found using data stored on |
| 151 omahaproxy. |
| 152 ''' |
| 153 latest_version = self._version_object_store.Get('latest').Get() |
| 154 if latest_version is not None: |
| 155 return latest_version |
| 156 |
| 157 version_json = json.loads(self._history_result.Get().content) |
| 158 latest_version = 0 |
| 159 for entry in version_json['events']: |
| 160 version_title = entry['title'].split(' - ')[1].split('.') |
| 161 version = int(version_title[0]) |
| 162 if version > latest_version: |
| 163 latest_version = version |
| 164 |
| 165 self._version_object_store.Set('latest', latest_version) |
| 166 return latest_version |
OLD | NEW |