branch_utility.py revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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 # BranchUtility is obviously cross-channel, so set the channel to None. 22 self._branch_object_store = object_store_creator.Create(BranchUtility, 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 40 41 @staticmethod 42 def Create(object_store_creator): 43 return BranchUtility(url_constants.OMAHA_PROXY_URL, 44 url_constants.OMAHA_DEV_HISTORY, 45 AppEngineUrlFetcher(), 46 object_store_creator) 47 48 @staticmethod 49 def SplitChannelNameFromPath(path): 50 '''Splits the channel name out of |path|, returning the tuple 51 (channel_name, real_path). If the channel cannot be determined then returns 52 (None, path). 53 ''' 54 if '/' in path: 55 first, second = path.split('/', 1) 56 else: 57 first, second = (path, '') 58 if first in BranchUtility.GetAllChannelNames(): 59 return (first, second) 60 return (None, path) 61 62 def GetAllBranchNumbers(self): 63 return ((channel, self.GetChannelInfo(channel).branch) 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 ''' 83 if channel_name == 'trunk': 84 return 'trunk' 85 86 if data_type == 'branch': 87 object_store = self._branch_object_store 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 94 95 try: 96 version_json = json.loads(self._fetch_result.Get().content) 97 except Exception as e: 98 # This can happen if omahaproxy is misbehaving, which we've seen before. 99 # Quick hack fix: just serve from trunk until it's fixed. 100 logging.error('Failed to fetch or parse branch from omahaproxy: %s! ' 101 'Falling back to "trunk".' % e) 102 return 'trunk' 103 104 numbers = {} 105 for entry in version_json: 106 if entry['os'] not in ['win', 'linux', 'mac', 'cros']: 107 continue 108 for version in entry['versions']: 109 if version['channel'] != channel_name: 110 continue 111 if data_type == 'branch': 112 number = version['version'].split('.')[2] 113 elif data_type == 'version': 114 number = version['version'].split('.')[0] 115 if number not in numbers: 116 numbers[number] = 0 117 else: 118 numbers[number] += 1 119 120 sorted_numbers = sorted(numbers.iteritems(), 121 None, 122 operator.itemgetter(1), 123 True) 124 object_store.Set(channel_name, int(sorted_numbers[0][0])) 125 return int(sorted_numbers[0][0]) 126 127 def GetBranchForVersion(self, version): 128 '''Returns the most recent branch for a given chrome version number using 129 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 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 167