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