comparison dogdish/dispatcher.py @ 14:cffb6f681b59

things seem to work
author Jeff Hammel <jhammel@mozilla.com>
date Wed, 17 Oct 2012 13:26:22 -0700
parents 71f9f68986b5
children fc5b842de4e9
comparison
equal deleted inserted replaced
13:71f9f68986b5 14:cffb6f681b59
4 dogdish 4 dogdish
5 https://bugzilla.mozilla.org/show_bug.cgi?id=800118 5 https://bugzilla.mozilla.org/show_bug.cgi?id=800118
6 """ 6 """
7 7
8 import fnmatch 8 import fnmatch
9 import hashlib
9 import os 10 import os
10 import sys 11 import sys
11 from urlparse import urlparse 12 from urlparse import urlparse
12 from webob import Request 13 from webob import Request
13 from webob import Response, exc 14 from webob import Response, exc
21 22
22 def __init__(self, filename): 23 def __init__(self, filename):
23 """ 24 """
24 - filename : path to an application.ini file 25 - filename : path to an application.ini file
25 """ 26 """
27 config = ConfigParser()
28 config.read(filename)
29 self.build_id = config.get('App', 'BuildID')
30 self.version = config.get('App', 'Version')
26 31
27 ### request handlers 32 ### request handlers
28 33
29 class Handler(object): 34 class Handler(object):
30 """abstract handler object for a request""" 35 """abstract handler object for a request"""
31 36
32 def __init__(self, request): 37 def __init__(self, app, request):
38 self.app = app
33 self.request = request 39 self.request = request
34 self.application_path = urlparse(request.application_url)[2] 40 self.application_path = urlparse(request.application_url)[2]
35 41
36 def link(self, path=(), permanant=False): 42 def link(self, path=(), permanant=False):
37 if isinstance(path, basestring): 43 if isinstance(path, basestring):
52 58
53 # template for response body 59 # template for response body
54 body = """<?xml version="1.0"?> 60 body = """<?xml version="1.0"?>
55 <updates> 61 <updates>
56 <update type="minor" appVersion="19.0a1" version="19.0a1" extensionVersion="19.0a1" buildID="20121010114416" licenseURL="http://www.mozilla.com/test/sample-eula.html" detailsURL="http://www.mozilla.com/test/sample-details.html"> 62 <update type="minor" appVersion="19.0a1" version="19.0a1" extensionVersion="19.0a1" buildID="20121010114416" licenseURL="http://www.mozilla.com/test/sample-eula.html" detailsURL="http://www.mozilla.com/test/sample-details.html">
57 <patch type="complete" URL="http://update.boot2gecko.org/nightly/b2g_update_2012-10-10_114416.mar%(query)s" hashFunction="SHA512" hashValue="84edb1f53891cf983bc0f6066d31492f43e2d063aaceb05e1c51876f4fde81635afeb7ce3203dee6f65dd59be0cae5c73c49093b625c99acd4118000ad72dda8" size="42924805"/> 63 <patch type="complete" URL="http://update.boot2gecko.org/nightly/%(update)s%(query)s" hashFunction="SHA512" hashValue="%(hash)s" size="%(size)s"/>
58 </update> 64 </update>
59 </updates>""" 65 </updates>"""
60 66
61 ### methods for request handler 67 ### methods for request handler
62 68
63 @classmethod 69 @classmethod
64 def match(cls, request): 70 def match(cls, request):
65 return request.method == 'GET' 71 return request.method == 'GET'
66 72
67 def __call__(self): 73 def __call__(self):
74
75 update_path = os.path.join(self.app.directory, self.app.current_update)
68 body = self.body 76 body = self.body
69 query = {} 77 query = {}
70 dogfood_id = self.request.GET.get('dogfood_id') 78 dogfood_id = self.request.GET.get('dogfood_id')
71 if dogfood_id: 79 if dogfood_id:
72 query['dogfooding_prerelease_id'] = dogfood_id 80 query['dogfooding_prerelease_id'] = dogfood_id
81 application = self.app.updates[self.app.current_update]
82 query['build_id'] = application.build_id
83 query['version'] = application.version
73 84
74 # build query string 85 # build query string
75 if query: 86 if query:
76 query = '?' + '&'.join(['%s=%s' % (key, value) for key, value in query.items()]) 87 query = '?' + '&amp;'.join(['%s=%s' % (key, value) for key, value in query.items()])
77 else: 88 else:
78 query = '' 89 query = ''
79 90
91 # compute the hash
92 with file(update_path) as f:
93 sha512 = hashlib.sha512(f.read()).hexdigest()
94
80 # template variables 95 # template variables
81 variables = dict(query=query) 96 variables = dict(update=self.app.current_update,
97 size=os.path.getsize(update_path),
98 hash=sha512,
99 query=query)
82 100
83 return Response(content_type='text/xml', 101 return Response(content_type='text/xml',
84 body=body % variables) 102 body=body % variables)
85 103
86 class Dispatcher(object): 104 class Dispatcher(object):
94 112
95 # set defaults 113 # set defaults
96 for key in self.defaults: 114 for key in self.defaults:
97 setattr(self, key, kw.get(key, self.defaults[key])) 115 setattr(self, key, kw.get(key, self.defaults[key]))
98 self.handlers = [ Get ] 116 self.handlers = [ Get ]
117
118 # cache
99 self.updates = {} 119 self.updates = {}
100 self.current_update = None 120 self.current_update = None
101 self.current_stamp = '0000-00-00_000000' 121 self.current_stamp = '0000-00-00_000000'
122 self.cached_response = None
102 123
103 # scan directory 124 # scan directory
104 self.scan() 125 self.scan()
126 assert self.current_update # ensure there is at least one update
105 127
106 def __call__(self, environ, start_response): 128 def __call__(self, environ, start_response):
107 """WSGI application""" 129 """WSGI application"""
108 130
109 request = Request(environ) 131 request = Request(environ)
132 new = self.scan()
133 if (not new) and (self.cached_response is not None):
134 return self.cached_response(environ, start_response)
110 for h in self.handlers: 135 for h in self.handlers:
111 if h.match(request): 136 if h.match(request):
112 handler = h(request) 137 handler = h(self, request)
138 res = handler()
139 self.cached_response = res
113 break 140 break
114 else: 141 else:
115 handler = exc.HTTPNotFound 142 res = exc.HTTPNotFound()
116 res = handler() 143
117 return res(environ, start_response) 144 return res(environ, start_response)
118 145
119 def scan(self): 146 def scan(self):
120 """scan the directory for updates""" 147 """
148 scan the directory for updates
149 returns True if new updates are found, False otherwise
150 """
121 prefix = 'b2g_update_' 151 prefix = 'b2g_update_'
122 suffix = '.mar' 152 suffix = '.mar'
123 contents = [i for i in os.listdir(self.directory) 153 contents = [i for i in os.listdir(self.directory)
124 if i.startswith(prefix) and i.endswith(suffix)] 154 if i.startswith(prefix) and i.endswith(suffix)]
125 contents = set(contents) 155 contents = set(contents)
126 new = contents.difference(self.updates.keys()) 156 new = contents.difference(self.updates.keys())
127 if not new: 157 if not new:
128 # directory contents unchanged from cached values 158 # directory contents unchanged from cached values
129 return 159 return False
130 160
131 for update in new: 161 for update in new:
132 stamp = update[len(prefix):-len(suffix)] 162 stamp = update[len(prefix):-len(suffix)]
133 application_ini = 'application_%s.ini' % stamp 163 application_ini = 'application_%s.ini' % stamp
134 application_ini = os.path.join(self.directory, application_ini) 164 application_ini = os.path.join(self.directory, application_ini)
136 self.updates[update] = Application(application_ini) 166 self.updates[update] = Application(application_ini)
137 if stamp > self.current_stamp: 167 if stamp > self.current_stamp:
138 self.current_update = update 168 self.current_update = update
139 self.current_stamp = stamp 169 self.current_stamp = stamp
140 170
171 # TODO: could remove old files from the cache if not found in contents
172
173 return True
174
141 def main(args=sys.argv[1:]): 175 def main(args=sys.argv[1:]):
142 """CLI entry point""" 176 """CLI entry point"""
143 177
144 # imports for CLI 178 # imports for CLI
145 import optparse 179 import optparse