Mercurial > mozilla > hg > dogfood
annotate dogdish/dispatcher.py @ 19:608df6b3de80
interpolate all the things
author | jhammel |
---|---|
date | Fri, 26 Oct 2012 15:27:41 +0000 |
parents | 6ffbad8cc31f |
children | 71c36a7842d1 |
rev | line source |
---|---|
1 | 1 #!/usr/bin/env python |
2 | |
0 | 3 """ |
1 | 4 dogdish |
7
65d0a1153864
a stub that sorta does something
Jeff Hammel <jhammel@mozilla.com>
parents:
6
diff
changeset
|
5 https://bugzilla.mozilla.org/show_bug.cgi?id=800118 |
0 | 6 """ |
7 | |
12
234c2427e52b
beginnings of a scan function
Jeff Hammel <jhammel@mozilla.com>
parents:
11
diff
changeset
|
8 import fnmatch |
14 | 9 import hashlib |
8 | 10 import os |
4 | 11 import sys |
0 | 12 from urlparse import urlparse |
4 | 13 from webob import Request |
0 | 14 from webob import Response, exc |
11 | 15 from ConfigParser import RawConfigParser as ConfigParser |
0 | 16 |
8 | 17 here = os.path.dirname(os.path.abspath(__file__)) |
18 | |
15
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
19 ### models |
11 | 20 |
21 class Application(object): | |
22 """class for storing application.ini data""" | |
23 | |
24 def __init__(self, filename): | |
25 """ | |
26 - filename : path to an application.ini file | |
27 """ | |
14 | 28 config = ConfigParser() |
29 config.read(filename) | |
30 self.build_id = config.get('App', 'BuildID') | |
31 self.version = config.get('App', 'Version') | |
11 | 32 |
15
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
33 |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
34 class Update(object): |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
35 """class representing a .mar update file""" |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
36 |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
37 prefix = 'b2g_update_' |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
38 suffix = '.mar' |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
39 |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
40 @classmethod |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
41 def updates(cls, directory): |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
42 """returns the updates in a directory""" |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
43 |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
44 contents = [i for i in os.listdir(directory) |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
45 if i.startswith(cls.prefix) and i.endswith(cls.suffix)] |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
46 contents = set(contents) |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
47 return contents |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
48 |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
49 def __init__(self, directory, filename): |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
50 self.directory = directory |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
51 self.filename = filename |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
52 self.path = os.path.join(directory, filename) |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
53 self.stamp = filename[len(self.prefix):-len(self.suffix)] |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
54 self.size = os.path.getsize(self.path) |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
55 |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
56 # cached properties |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
57 self._application = None |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
58 self._hash = None |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
59 |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
60 def application(self): |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
61 """ |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
62 returns the path to the application.ini |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
63 associated with this update |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
64 """ |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
65 |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
66 if not self._application: |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
67 application_ini = 'application_%s.ini' % self.stamp |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
68 application_ini = os.path.join(self.directory, application_ini) |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
69 assert os.path.exists(application_ini) |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
70 self._application = Application(application_ini) |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
71 return self._application |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
72 |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
73 def hash(self): |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
74 if not self._hash: |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
75 # compute the hash |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
76 with file(self.path) as f: |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
77 self._hash = hashlib.sha512(f.read()).hexdigest() |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
78 return self._hash |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
79 |
17 | 80 class UpdateStable(Update): |
81 prefix = 'b2g_stable_update_' | |
82 | |
83 | |
11 | 84 ### request handlers |
85 | |
0 | 86 class Handler(object): |
9 | 87 """abstract handler object for a request""" |
1 | 88 |
14 | 89 def __init__(self, app, request): |
90 self.app = app | |
0 | 91 self.request = request |
92 self.application_path = urlparse(request.application_url)[2] | |
93 | |
94 def link(self, path=(), permanant=False): | |
95 if isinstance(path, basestring): | |
96 path = [ path ] | |
97 path = [ i.strip('/') for i in path ] | |
98 if permanant: | |
99 application_url = [ self.request.application_url ] | |
100 else: | |
101 application_url = [ self.application_path ] | |
102 path = application_url + path | |
103 return '/'.join(path) | |
104 | |
105 def redirect(self, location): | |
106 raise exc.HTTPSeeOther(location=location) | |
107 | |
108 class Get(Handler): | |
9 | 109 """handle GET requests""" |
0 | 110 |
9 | 111 # template for response body |
0 | 112 body = """<?xml version="1.0"?> |
113 <updates> | |
19 | 114 <update type="minor" appVersion="%(version)s" version="%(version)s" extensionVersion="%(version)s" buildID="%(build_id)s" licenseURL="http://www.mozilla.com/test/sample-eula.html" detailsURL="http://www.mozilla.com/test/sample-details.html"> |
18 | 115 <patch type="complete" URL="http://update.boot2gecko.org/%(path)s/%(update)s%(query)s" hashFunction="SHA512" hashValue="%(hash)s" size="%(size)s"/> |
0 | 116 </update> |
117 </updates>""" | |
118 | |
9 | 119 ### methods for request handler |
120 | |
0 | 121 @classmethod |
122 def match(cls, request): | |
123 return request.method == 'GET' | |
124 | |
125 def __call__(self): | |
14 | 126 |
7
65d0a1153864
a stub that sorta does something
Jeff Hammel <jhammel@mozilla.com>
parents:
6
diff
changeset
|
127 body = self.body |
65d0a1153864
a stub that sorta does something
Jeff Hammel <jhammel@mozilla.com>
parents:
6
diff
changeset
|
128 query = {} |
65d0a1153864
a stub that sorta does something
Jeff Hammel <jhammel@mozilla.com>
parents:
6
diff
changeset
|
129 dogfood_id = self.request.GET.get('dogfood_id') |
65d0a1153864
a stub that sorta does something
Jeff Hammel <jhammel@mozilla.com>
parents:
6
diff
changeset
|
130 if dogfood_id: |
12
234c2427e52b
beginnings of a scan function
Jeff Hammel <jhammel@mozilla.com>
parents:
11
diff
changeset
|
131 query['dogfooding_prerelease_id'] = dogfood_id |
15
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
132 current_update = self.app.current_update |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
133 application = current_update.application() |
14 | 134 query['build_id'] = application.build_id |
135 query['version'] = application.version | |
7
65d0a1153864
a stub that sorta does something
Jeff Hammel <jhammel@mozilla.com>
parents:
6
diff
changeset
|
136 |
65d0a1153864
a stub that sorta does something
Jeff Hammel <jhammel@mozilla.com>
parents:
6
diff
changeset
|
137 # build query string |
65d0a1153864
a stub that sorta does something
Jeff Hammel <jhammel@mozilla.com>
parents:
6
diff
changeset
|
138 if query: |
14 | 139 query = '?' + '&'.join(['%s=%s' % (key, value) for key, value in query.items()]) |
7
65d0a1153864
a stub that sorta does something
Jeff Hammel <jhammel@mozilla.com>
parents:
6
diff
changeset
|
140 else: |
65d0a1153864
a stub that sorta does something
Jeff Hammel <jhammel@mozilla.com>
parents:
6
diff
changeset
|
141 query = '' |
65d0a1153864
a stub that sorta does something
Jeff Hammel <jhammel@mozilla.com>
parents:
6
diff
changeset
|
142 |
14 | 143 |
12
234c2427e52b
beginnings of a scan function
Jeff Hammel <jhammel@mozilla.com>
parents:
11
diff
changeset
|
144 # template variables |
15
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
145 variables = dict(update=current_update.filename, |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
146 size=current_update.size, |
19 | 147 version=application.version, |
15
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
148 hash=current_update.hash(), |
18 | 149 path=self.app.path, |
19 | 150 build_id=application.build_id, |
14 | 151 query=query) |
12
234c2427e52b
beginnings of a scan function
Jeff Hammel <jhammel@mozilla.com>
parents:
11
diff
changeset
|
152 |
1 | 153 return Response(content_type='text/xml', |
12
234c2427e52b
beginnings of a scan function
Jeff Hammel <jhammel@mozilla.com>
parents:
11
diff
changeset
|
154 body=body % variables) |
0 | 155 |
156 class Dispatcher(object): | |
9 | 157 """web application""" |
158 | |
0 | 159 |
160 ### class level variables | |
18 | 161 defaults = {'directory': here, |
162 'path': None, | |
17 | 163 'update_class': Update} |
0 | 164 |
165 def __init__(self, **kw): | |
11 | 166 |
167 # set defaults | |
0 | 168 for key in self.defaults: |
169 setattr(self, key, kw.get(key, self.defaults[key])) | |
170 self.handlers = [ Get ] | |
14 | 171 |
18 | 172 # path |
173 if not self.path: | |
19 | 174 self.path = os.path.split(self.directory.strip(os.path.sep))[-1] |
18 | 175 |
14 | 176 # cache |
11 | 177 self.updates = {} |
178 self.current_update = None | |
0 | 179 |
11 | 180 # scan directory |
181 self.scan() | |
16 | 182 assert self.current_update, "No updates found in %s" % self.directory |
11 | 183 |
0 | 184 def __call__(self, environ, start_response): |
11 | 185 """WSGI application""" |
186 | |
0 | 187 request = Request(environ) |
14 | 188 new = self.scan() |
0 | 189 for h in self.handlers: |
190 if h.match(request): | |
14 | 191 handler = h(self, request) |
192 res = handler() | |
0 | 193 break |
194 else: | |
14 | 195 res = exc.HTTPNotFound() |
196 | |
0 | 197 return res(environ, start_response) |
198 | |
11 | 199 def scan(self): |
14 | 200 """ |
201 scan the directory for updates | |
202 returns True if new updates are found, False otherwise | |
203 """ | |
15
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
204 |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
205 # check for new updates |
17 | 206 contents = self.update_class.updates(self.directory) |
12
234c2427e52b
beginnings of a scan function
Jeff Hammel <jhammel@mozilla.com>
parents:
11
diff
changeset
|
207 new = contents.difference(self.updates.keys()) |
234c2427e52b
beginnings of a scan function
Jeff Hammel <jhammel@mozilla.com>
parents:
11
diff
changeset
|
208 if not new: |
234c2427e52b
beginnings of a scan function
Jeff Hammel <jhammel@mozilla.com>
parents:
11
diff
changeset
|
209 # directory contents unchanged from cached values |
14 | 210 return False |
11 | 211 |
13
71f9f68986b5
directory scanning + caching
Jeff Hammel <jhammel@mozilla.com>
parents:
12
diff
changeset
|
212 for update in new: |
17 | 213 self.updates[update] = self.update_class(self.directory, update) |
15
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
214 if self.current_update: |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
215 if self.updates[update].stamp > self.current_update.stamp: |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
216 self.current_update = self.updates[update] |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
217 else: |
fc5b842de4e9
be a little less aggressive when caching
Jeff Hammel <jhammel@mozilla.com>
parents:
14
diff
changeset
|
218 self.current_update = self.updates[update] |
13
71f9f68986b5
directory scanning + caching
Jeff Hammel <jhammel@mozilla.com>
parents:
12
diff
changeset
|
219 |
14 | 220 # TODO: could remove old files from the cache if not found in contents |
221 | |
222 return True | |
223 | |
0 | 224 def main(args=sys.argv[1:]): |
225 """CLI entry point""" | |
226 | |
11 | 227 # imports for CLI |
0 | 228 import optparse |
229 from wsgiref import simple_server | |
230 | |
11 | 231 # parse CLI options |
0 | 232 parser = optparse.OptionParser() |
233 parser.add_option('-p', '--port', dest='port', | |
234 default=8080, type='int', | |
235 help="port to serve on") | |
11 | 236 parser.add_option('-d', '--directory', dest='directory', |
237 default=os.getcwd(), | |
238 help="directory of update files") | |
0 | 239 options, args = parser.parse_args() |
240 | |
11 | 241 # create the app |
242 app = Dispatcher(directory=options.directory) | |
0 | 243 |
11 | 244 # serve the app |
7
65d0a1153864
a stub that sorta does something
Jeff Hammel <jhammel@mozilla.com>
parents:
6
diff
changeset
|
245 print "http://localhost:%s/" % options.port |
0 | 246 server = simple_server.make_server(host='0.0.0.0', port=options.port, app=app) |
247 server.serve_forever() | |
248 | |
249 if __name__ == '__main__': | |
250 main() |