Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / common / extensions / docs / server2 / branch_utility.py
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
3 # found in the LICENSE file.
4
5 import json
6 import logging
7 import operator
8
9 from appengine_url_fetcher import AppEngineUrlFetcher
10 import url_constants
11
12
13 class ChannelInfo(object):
14   '''Represents a Chrome channel with three pieces of information. |channel| is
15   one of 'stable', 'beta', 'dev', or 'master'. |branch| and |version| correspond
16   with each other, and represent different releases of Chrome. Note that
17   |branch| and |version| can occasionally be the same for separate channels
18   (i.e. 'beta' and 'dev'), so all three fields are required to uniquely
19   identify a channel.
20   '''
21
22   def __init__(self, channel, branch, version):
23     assert isinstance(channel, basestring), channel
24     assert isinstance(branch, basestring), branch
25     # TODO(kalman): Assert that this is a string. One day Chromium will probably
26     # be served out of a git repository and the versions will no longer be ints.
27     assert isinstance(version, int) or version == 'master', version
28     self.channel = channel
29     self.branch = branch
30     self.version = version
31
32   def __eq__(self, other):
33     return self.__dict__ == other.__dict__
34
35   def __ne__(self, other):
36     return not (self == other)
37
38   def __repr__(self):
39     return '%s%s' % (type(self).__name__, repr(self.__dict__))
40
41   def __str__(self):
42     return repr(self)
43
44
45 class BranchUtility(object):
46   '''Provides methods for working with Chrome channel, branch, and version
47   data served from OmahaProxy.
48   '''
49
50   def __init__(self, fetch_url, history_url, fetcher, object_store_creator):
51     self._fetcher = fetcher
52     def create_object_store(category):
53       return object_store_creator.Create(BranchUtility, category=category)
54     self._branch_object_store = create_object_store('branch')
55     self._version_object_store = create_object_store('version')
56     self._fetch_result = self._fetcher.FetchAsync(fetch_url)
57     self._history_result = self._fetcher.FetchAsync(history_url)
58
59   @staticmethod
60   def Create(object_store_creator):
61     return BranchUtility(url_constants.OMAHA_PROXY_URL,
62                          url_constants.OMAHA_DEV_HISTORY,
63                          AppEngineUrlFetcher(),
64                          object_store_creator)
65
66   @staticmethod
67   def GetAllChannelNames():
68     return ('stable', 'beta', 'dev', 'master')
69
70   @staticmethod
71   def NewestChannel(channels):
72     channels = set(channels)
73     for channel in reversed(BranchUtility.GetAllChannelNames()):
74       if channel in channels:
75         return channel
76
77   def Newer(self, channel_info):
78     '''Given a ChannelInfo object, returns a new ChannelInfo object
79     representing the next most recent Chrome version/branch combination.
80     '''
81     if channel_info.channel == 'master':
82       return None
83     if channel_info.channel == 'stable':
84       stable_info = self.GetChannelInfo('stable')
85       if channel_info.version < stable_info.version:
86         return self.GetStableChannelInfo(channel_info.version + 1)
87     names = self.GetAllChannelNames()
88     return self.GetAllChannelInfo()[names.index(channel_info.channel) + 1]
89
90   def Older(self, channel_info):
91     '''Given a ChannelInfo object, returns a new ChannelInfo object
92     representing the previous Chrome version/branch combination.
93     '''
94     if channel_info.channel == 'stable':
95       if channel_info.version <= 5:
96         # BranchUtility can't access branch data from before Chrome version 5.
97         return None
98       return self.GetStableChannelInfo(channel_info.version - 1)
99     names = self.GetAllChannelNames()
100     return self.GetAllChannelInfo()[names.index(channel_info.channel) - 1]
101
102   @staticmethod
103   def SplitChannelNameFromPath(path):
104     '''Splits the channel name out of |path|, returning the tuple
105     (channel_name, real_path). If the channel cannot be determined then returns
106     (None, path).
107     '''
108     if '/' in path:
109       first, second = path.split('/', 1)
110     else:
111       first, second = (path, '')
112     if first in BranchUtility.GetAllChannelNames():
113       return (first, second)
114     return (None, path)
115
116   def GetAllBranches(self):
117     return tuple((channel, self.GetChannelInfo(channel).branch)
118             for channel in BranchUtility.GetAllChannelNames())
119
120   def GetAllVersions(self):
121     return tuple(self.GetChannelInfo(channel).version
122             for channel in BranchUtility.GetAllChannelNames())
123
124   def GetAllChannelInfo(self):
125     return tuple(self.GetChannelInfo(channel)
126             for channel in BranchUtility.GetAllChannelNames())
127
128
129   def GetChannelInfo(self, channel):
130     version = self._ExtractFromVersionJson(channel, 'version')
131     if version != 'master':
132       version = int(version)
133     return ChannelInfo(channel,
134                        self._ExtractFromVersionJson(channel, 'branch'),
135                        version)
136
137   def GetStableChannelInfo(self, version):
138     '''Given a |version| corresponding to a 'stable' version of Chrome, returns
139     a ChannelInfo object representing that version.
140     '''
141     return ChannelInfo('stable', self.GetBranchForVersion(version), version)
142
143   def _ExtractFromVersionJson(self, channel_name, data_type):
144     '''Returns the branch or version number for a channel name.
145     '''
146     if channel_name == 'master':
147       return 'master'
148
149     if data_type == 'branch':
150       object_store = self._branch_object_store
151     elif data_type == 'version':
152       object_store = self._version_object_store
153
154     data = object_store.Get(channel_name).Get()
155     if data is not None:
156       return data
157
158     try:
159       version_json = json.loads(self._fetch_result.Get().content)
160     except Exception as e:
161       # This can happen if omahaproxy is misbehaving, which we've seen before.
162       # Quick hack fix: just serve from master until it's fixed.
163       logging.error('Failed to fetch or parse branch from omahaproxy: %s! '
164                     'Falling back to "master".' % e)
165       return 'master'
166
167     numbers = {}
168     for entry in version_json:
169       if entry['os'] not in ('win', 'linux', 'mac', 'cros'):
170         continue
171       for version in entry['versions']:
172         if version['channel'] != channel_name:
173           continue
174         if data_type == 'branch':
175           number = version['version'].split('.')[2]
176         elif data_type == 'version':
177           number = version['version'].split('.')[0]
178         if number not in numbers:
179           numbers[number] = 0
180         else:
181           numbers[number] += 1
182
183     sorted_numbers = sorted(numbers.iteritems(),
184                             key=operator.itemgetter(1),
185                             reverse=True)
186     object_store.Set(channel_name, sorted_numbers[0][0])
187     return sorted_numbers[0][0]
188
189   def GetBranchForVersion(self, version):
190     '''Returns the most recent branch for a given chrome version number using
191     data stored on omahaproxy (see url_constants).
192     '''
193     if version == 'master':
194       return 'master'
195
196     branch = self._branch_object_store.Get(str(version)).Get()
197     if branch is not None:
198       return branch
199
200     version_json = json.loads(self._history_result.Get().content)
201     for entry in version_json:
202       version_title = entry['version'].split('.')
203       if version_title[0] == str(version):
204         self._branch_object_store.Set(str(version), version_title[2])
205         return version_title[2]
206
207     raise ValueError('The branch for %s could not be found.' % version)
208
209   def GetChannelForVersion(self, version):
210     '''Returns the name of the development channel corresponding to a given
211     version number.
212     '''
213     for channel_info in self.GetAllChannelInfo():
214       if channel_info.channel == 'stable' and version <= channel_info.version:
215         return channel_info.channel
216       if version == channel_info.version:
217         return channel_info.channel
218
219   def GetLatestVersionNumber(self):
220     '''Returns the most recent version number found using data stored on
221     omahaproxy.
222     '''
223     latest_version = self._version_object_store.Get('latest').Get()
224     if latest_version is not None:
225       return latest_version
226
227     version_json = json.loads(self._history_result.Get().content)
228     latest_version = 0
229     for entry in version_json:
230       version_title = entry['version'].split('.')
231       version = int(version_title[0])
232       if version > latest_version:
233         latest_version = version
234
235     self._version_object_store.Set('latest', latest_version)
236     return latest_version