Mercurial > mozilla > hg > dogfood
comparison dogdish/dispatcher.py @ 15:fc5b842de4e9
be a little less aggressive when caching
author | Jeff Hammel <jhammel@mozilla.com> |
---|---|
date | Wed, 17 Oct 2012 14:59:49 -0700 |
parents | cffb6f681b59 |
children | 31898fb3b7d9 |
comparison
equal
deleted
inserted
replaced
14:cffb6f681b59 | 15:fc5b842de4e9 |
---|---|
14 from webob import Response, exc | 14 from webob import Response, exc |
15 from ConfigParser import RawConfigParser as ConfigParser | 15 from ConfigParser import RawConfigParser as ConfigParser |
16 | 16 |
17 here = os.path.dirname(os.path.abspath(__file__)) | 17 here = os.path.dirname(os.path.abspath(__file__)) |
18 | 18 |
19 ### models | |
19 | 20 |
20 class Application(object): | 21 class Application(object): |
21 """class for storing application.ini data""" | 22 """class for storing application.ini data""" |
22 | 23 |
23 def __init__(self, filename): | 24 def __init__(self, filename): |
26 """ | 27 """ |
27 config = ConfigParser() | 28 config = ConfigParser() |
28 config.read(filename) | 29 config.read(filename) |
29 self.build_id = config.get('App', 'BuildID') | 30 self.build_id = config.get('App', 'BuildID') |
30 self.version = config.get('App', 'Version') | 31 self.version = config.get('App', 'Version') |
32 | |
33 | |
34 class Update(object): | |
35 """class representing a .mar update file""" | |
36 | |
37 prefix = 'b2g_update_' | |
38 suffix = '.mar' | |
39 | |
40 @classmethod | |
41 def updates(cls, directory): | |
42 """returns the updates in a directory""" | |
43 | |
44 contents = [i for i in os.listdir(directory) | |
45 if i.startswith(cls.prefix) and i.endswith(cls.suffix)] | |
46 contents = set(contents) | |
47 return contents | |
48 | |
49 def __init__(self, directory, filename): | |
50 self.directory = directory | |
51 self.filename = filename | |
52 self.path = os.path.join(directory, filename) | |
53 self.stamp = filename[len(self.prefix):-len(self.suffix)] | |
54 self.size = os.path.getsize(self.path) | |
55 | |
56 # cached properties | |
57 self._application = None | |
58 self._hash = None | |
59 | |
60 def application(self): | |
61 """ | |
62 returns the path to the application.ini | |
63 associated with this update | |
64 """ | |
65 | |
66 if not self._application: | |
67 application_ini = 'application_%s.ini' % self.stamp | |
68 application_ini = os.path.join(self.directory, application_ini) | |
69 assert os.path.exists(application_ini) | |
70 self._application = Application(application_ini) | |
71 return self._application | |
72 | |
73 def hash(self): | |
74 if not self._hash: | |
75 # compute the hash | |
76 with file(self.path) as f: | |
77 self._hash = hashlib.sha512(f.read()).hexdigest() | |
78 return self._hash | |
31 | 79 |
32 ### request handlers | 80 ### request handlers |
33 | 81 |
34 class Handler(object): | 82 class Handler(object): |
35 """abstract handler object for a request""" | 83 """abstract handler object for a request""" |
70 def match(cls, request): | 118 def match(cls, request): |
71 return request.method == 'GET' | 119 return request.method == 'GET' |
72 | 120 |
73 def __call__(self): | 121 def __call__(self): |
74 | 122 |
75 update_path = os.path.join(self.app.directory, self.app.current_update) | |
76 body = self.body | 123 body = self.body |
77 query = {} | 124 query = {} |
78 dogfood_id = self.request.GET.get('dogfood_id') | 125 dogfood_id = self.request.GET.get('dogfood_id') |
79 if dogfood_id: | 126 if dogfood_id: |
80 query['dogfooding_prerelease_id'] = dogfood_id | 127 query['dogfooding_prerelease_id'] = dogfood_id |
81 application = self.app.updates[self.app.current_update] | 128 current_update = self.app.current_update |
129 application = current_update.application() | |
82 query['build_id'] = application.build_id | 130 query['build_id'] = application.build_id |
83 query['version'] = application.version | 131 query['version'] = application.version |
84 | 132 |
85 # build query string | 133 # build query string |
86 if query: | 134 if query: |
87 query = '?' + '&'.join(['%s=%s' % (key, value) for key, value in query.items()]) | 135 query = '?' + '&'.join(['%s=%s' % (key, value) for key, value in query.items()]) |
88 else: | 136 else: |
89 query = '' | 137 query = '' |
90 | 138 |
91 # compute the hash | |
92 with file(update_path) as f: | |
93 sha512 = hashlib.sha512(f.read()).hexdigest() | |
94 | 139 |
95 # template variables | 140 # template variables |
96 variables = dict(update=self.app.current_update, | 141 variables = dict(update=current_update.filename, |
97 size=os.path.getsize(update_path), | 142 size=current_update.size, |
98 hash=sha512, | 143 hash=current_update.hash(), |
99 query=query) | 144 query=query) |
100 | 145 |
101 return Response(content_type='text/xml', | 146 return Response(content_type='text/xml', |
102 body=body % variables) | 147 body=body % variables) |
103 | 148 |
116 self.handlers = [ Get ] | 161 self.handlers = [ Get ] |
117 | 162 |
118 # cache | 163 # cache |
119 self.updates = {} | 164 self.updates = {} |
120 self.current_update = None | 165 self.current_update = None |
121 self.current_stamp = '0000-00-00_000000' | |
122 self.cached_response = None | |
123 | 166 |
124 # scan directory | 167 # scan directory |
125 self.scan() | 168 self.scan() |
126 assert self.current_update # ensure there is at least one update | 169 assert self.current_update # ensure there is at least one update |
127 | 170 |
128 def __call__(self, environ, start_response): | 171 def __call__(self, environ, start_response): |
129 """WSGI application""" | 172 """WSGI application""" |
130 | 173 |
131 request = Request(environ) | 174 request = Request(environ) |
132 new = self.scan() | 175 new = self.scan() |
133 if (not new) and (self.cached_response is not None): | |
134 return self.cached_response(environ, start_response) | |
135 for h in self.handlers: | 176 for h in self.handlers: |
136 if h.match(request): | 177 if h.match(request): |
137 handler = h(self, request) | 178 handler = h(self, request) |
138 res = handler() | 179 res = handler() |
139 self.cached_response = res | |
140 break | 180 break |
141 else: | 181 else: |
142 res = exc.HTTPNotFound() | 182 res = exc.HTTPNotFound() |
143 | 183 |
144 return res(environ, start_response) | 184 return res(environ, start_response) |
146 def scan(self): | 186 def scan(self): |
147 """ | 187 """ |
148 scan the directory for updates | 188 scan the directory for updates |
149 returns True if new updates are found, False otherwise | 189 returns True if new updates are found, False otherwise |
150 """ | 190 """ |
151 prefix = 'b2g_update_' | 191 |
152 suffix = '.mar' | 192 # check for new updates |
153 contents = [i for i in os.listdir(self.directory) | 193 contents = Update.updates(self.directory) |
154 if i.startswith(prefix) and i.endswith(suffix)] | |
155 contents = set(contents) | |
156 new = contents.difference(self.updates.keys()) | 194 new = contents.difference(self.updates.keys()) |
157 if not new: | 195 if not new: |
158 # directory contents unchanged from cached values | 196 # directory contents unchanged from cached values |
159 return False | 197 return False |
160 | 198 |
161 for update in new: | 199 for update in new: |
162 stamp = update[len(prefix):-len(suffix)] | 200 self.updates[update] = Update(self.directory, update) |
163 application_ini = 'application_%s.ini' % stamp | 201 if self.current_update: |
164 application_ini = os.path.join(self.directory, application_ini) | 202 if self.updates[update].stamp > self.current_update.stamp: |
165 assert os.path.exists(application_ini) | 203 self.current_update = self.updates[update] |
166 self.updates[update] = Application(application_ini) | 204 else: |
167 if stamp > self.current_stamp: | 205 self.current_update = self.updates[update] |
168 self.current_update = update | |
169 self.current_stamp = stamp | |
170 | 206 |
171 # TODO: could remove old files from the cache if not found in contents | 207 # TODO: could remove old files from the cache if not found in contents |
172 | 208 |
173 return True | 209 return True |
174 | 210 |