github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/gubernator/view_base.py (about) 1 # Copyright 2016 The Kubernetes Authors. 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 15 import functools 16 import logging 17 import os 18 import re 19 20 import cloudstorage as gcs 21 import jinja2 22 import webapp2 23 24 from google.appengine.api import urlfetch 25 from google.appengine.api import memcache 26 from webapp2_extras import sessions 27 28 import filters as jinja_filters 29 30 JINJA_ENVIRONMENT = jinja2.Environment( 31 loader=jinja2.FileSystemLoader(os.path.dirname(__file__) + '/templates'), 32 extensions=['jinja2.ext.autoescape', 'jinja2.ext.loopcontrols'], 33 trim_blocks=True, 34 autoescape=True) 35 JINJA_ENVIRONMENT.line_statement_prefix = '%' 36 jinja_filters.register(JINJA_ENVIRONMENT.filters) 37 38 39 class BaseHandler(webapp2.RequestHandler): 40 """Base class for Handlers that render Jinja templates.""" 41 def __init__(self, *args, **kwargs): 42 super(BaseHandler, self).__init__(*args, **kwargs) 43 # The default deadline of 5 seconds is too aggressive of a target for GCS 44 # directory listing operations. 45 urlfetch.set_default_fetch_deadline(60) 46 47 # This example code is from: 48 # http://webapp2.readthedocs.io/en/latest/api/webapp2_extras/sessions.html 49 def dispatch(self): 50 # pylint: disable=attribute-defined-outside-init 51 # Get a session store for this request. 52 self.session_store = sessions.get_store(request=self.request) 53 54 try: 55 # Dispatch the request. 56 webapp2.RequestHandler.dispatch(self) 57 finally: 58 # Save all sessions. 59 self.session_store.save_sessions(self.response) 60 61 @webapp2.cached_property 62 def session(self): 63 # Returns a session using the default cookie key. 64 return self.session_store.get_session() 65 66 def render(self, template, context): 67 """Render a context dictionary using a given template.""" 68 template = JINJA_ENVIRONMENT.get_template(template) 69 self.response.write(template.render(context)) 70 71 72 class IndexHandler(BaseHandler): 73 """Render the index.""" 74 def get(self): 75 self.render("index.html", {'jobs': self.app.config['jobs']}) 76 77 78 def memcache_memoize(prefix, expires=60 * 60, neg_expires=60): 79 """Decorate a function to memoize its results using memcache. 80 81 The function must take a single string as input, and return a pickleable 82 type. 83 84 Args: 85 prefix: A prefix for memcache keys to use for memoization. 86 expires: How long to memoized values, in seconds. 87 neg_expires: How long to memoize falsey values, in seconds 88 Returns: 89 A decorator closure to wrap the function. 90 """ 91 # setting the namespace based on the current version prevents different 92 # versions from sharing cache values -- meaning there's no need to worry 93 # about incompatible old key/value pairs 94 namespace = os.environ['CURRENT_VERSION_ID'] 95 def wrapper(func): 96 @functools.wraps(func) 97 def wrapped(*args): 98 key = '%s%s' % (prefix, args) 99 data = memcache.get(key, namespace=namespace) 100 if data is not None: 101 return data 102 else: 103 data = func(*args) 104 try: 105 if data: 106 memcache.add(key, data, expires, namespace=namespace) 107 else: 108 memcache.add(key, data, neg_expires, namespace=namespace) 109 except ValueError: 110 logging.exception('unable to write to memcache') 111 return data 112 return wrapped 113 return wrapper 114 115 116 @memcache_memoize('gs-ls://', expires=60) 117 def gcs_ls(path): 118 """Enumerate files in a GCS directory. Returns a list of FileStats.""" 119 if path[-1] != '/': 120 path += '/' 121 return list(gcs.listbucket(path, delimiter='/')) 122 123 124 def pad_numbers(s): 125 """Modify a string to make its numbers suitable for natural sorting.""" 126 return re.sub(r'\d+', lambda m: m.group(0).rjust(16, '0'), s)