Index: appengine/chrome_infra_mon_proxy/main.py |
diff --git a/appengine/chrome_infra_mon_proxy/main.py b/appengine/chrome_infra_mon_proxy/main.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0eaf309a5af18bfe5d43ad20fefc33298f3a2c62 |
--- /dev/null |
+++ b/appengine/chrome_infra_mon_proxy/main.py |
@@ -0,0 +1,103 @@ |
+# Copyright 2015 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+import logging |
+import random |
+import os |
+import sys |
+import time |
+import urllib2 |
+import webapp2 |
+ |
+from protorpc import messages |
+from protorpc import message_types |
+from protorpc import remote |
+ |
+from google.appengine.api import app_identity, taskqueue |
+ |
+import common |
+from components import auth |
+ |
+VM_MODULES = ['vm1', 'vm2', 'vm3'] |
+ |
+ |
+def require_group_membership(group_name): |
+ """Authenticating decorator for handler methods. |
+ |
+ Requires the user to be part of group in production, and |
+ skips authorization for dev appserver. |
+ """ |
+ if common.is_development_server(): |
+ auth_decorator = auth.public |
+ else: |
+ auth_decorator = auth.require( # pragma: no branch |
+ lambda: auth.is_group_member(group_name)) |
+ |
+ def decorator(fn): |
+ return auth_decorator(fn) |
+ return decorator |
+ |
+ |
+class NoBackendException(Exception): |
+ pass |
+ |
+ |
+class LoadBalancer(object): |
+ """Balance the load among VM modules. |
+ |
+ TODO(sergeyberezin): take into account health checks on the |
+ corresponding NAT boxes. Specifically, fetch the health status |
+ from the datastore in __init__(), and update it periodically as needed. |
+ """ |
+ def __init__(self): |
+ pass |
+ |
+ def choose_module(self): |
+ """Select a module to send the data to.""" |
+ # TODO(sergeyberezin) Implement load percentages for modules, for |
+ # draining / canary / live rolling updates. |
+ # TODO(sergeyberezin): perform health checks for the corresponding |
+ # NAT boxes and drain modules appropriately. |
+ return random.choice(VM_MODULES) |
+ |
+ |
+def forward_data(data): |
agable
2015/04/27 20:51:07
Not in this CL, but immediately after: I'd like th
Sergey Berezin
2015/04/27 21:45:51
Good idea, I'll add it to the list. I was thinking
|
+ """Forwards the raw data to the backend.""" |
+ # Task queue should work correctly both in dev and prod server. |
+ lb = LoadBalancer() |
+ module_name = lb.choose_module() |
+ logging.info('Forwarding request to module: %s', module_name) |
+ hostname = app_identity.get_default_version_hostname() |
+ if common.is_development_server(): |
+ protocol = 'http' |
+ else: |
+ protocol = 'https' |
+ url = '%s://%s/%s' % (protocol, hostname, module_name) |
+ request = urllib2.Request(url, data) |
+ urllib2.urlopen(request) |
+ |
+ |
+class MonacqHandler(auth.AuthenticatingHandler): |
+ # Disable XSRF in local dev appserver; otherwise requests will fail. |
+ if common.is_development_server(): |
+ xsrf_token_enforce_on = [] # pragma: no cover |
+ |
+ @require_group_membership('service-account-monitoring-proxy') |
+ def post(self): |
+ forward_data(self.request.body) |
+ |
+ |
+class MainHandler(common.BaseHandler): |
+ def get(self): |
+ self.render_response('main.html', title='Chrome Infra Monitoring Proxy') |
+ |
+ |
+logging.basicConfig(level=logging.DEBUG) |
+ |
+main_handlers = [ |
+ (r'/', MainHandler), |
+ (r'/monacq', MonacqHandler), |
+] |
+ |
+app = webapp2.WSGIApplication(main_handlers, debug=True) |