github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/gubernator/github/admin.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 collections 16 import cPickle as pickle 17 import logging 18 import os 19 20 import webapp2 21 22 from google.appengine.api import urlfetch 23 from google.appengine.ext import deferred 24 from google.appengine.ext import ndb 25 26 import models 27 import handlers 28 29 # ndb model.query likes to use == True 30 # pylint: disable=singleton-comparison 31 32 class RecomputeOpenPRs(object): 33 keys_only = True 34 35 @staticmethod 36 def query(): 37 return models.GHIssueDigest.query( 38 models.GHIssueDigest.is_open == True, 39 models.GHIssueDigest.is_pr == True 40 ) 41 42 @staticmethod 43 def handle_entity(entity): 44 repo, number = entity.id().split(' ') 45 handlers.update_issue_digest(repo, number, always_put=True) 46 return {'puts': 1} 47 48 @ndb.toplevel 49 def migrate(migration, cursor=None, last_parent=None, stop=False): 50 entities, next_cursor, more = migration.query().fetch_page( 51 10, start_cursor=cursor, keys_only=migration.keys_only) 52 53 counters = collections.Counter() 54 55 for entity in entities: 56 changes = migration.handle_entity(entity) 57 counters.update(changes) 58 59 summary = ', '.join('%s: %d' % x for x in sorted(counters.items())) 60 if entities: 61 logging.info('fetched %d. %s. (%r-%r)', 62 len(entities), summary, entities[0], entities[-1]) 63 64 if stop: 65 return 66 67 if more and next_cursor: 68 deferred.defer(migrate, migration, cursor=next_cursor, last_parent=last_parent) 69 70 71 class Digest(webapp2.RequestHandler): 72 def get(self): 73 results = models.GHIssueDigest.query( 74 models.GHIssueDigest.is_open == True) 75 self.response.headers['content-type'] = 'text/plain' 76 self.response.write(pickle.dumps(list(results), pickle.HIGHEST_PROTOCOL)) 77 78 79 class AdminDash(webapp2.RequestHandler): 80 def get(self): 81 self.response.write(""" 82 <form action="/admin/reprocess" method="post"> 83 <button>Reprocess Open Issues/PRs</button><input type="checkbox" name="background">Background 84 </form> 85 <form action="/admin/digest_sync" method="post"> 86 <button>Download GHIssueDigest from production</button> 87 </form> 88 """) 89 90 def check_csrf(self): 91 # https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet 92 # #Checking_The_Referer_Header 93 origin = self.request.headers.get('origin') + '/' 94 expected = self.request.host_url + '/' 95 if not (origin and origin == expected): 96 logging.error('csrf check failed for %s, origin: %r', self.request.url, origin) 97 self.abort(403) 98 99 100 class Reprocessor(AdminDash): 101 def post(self): 102 self.check_csrf() 103 migration = RecomputeOpenPRs() 104 if self.request.get('background'): 105 deferred.defer(migrate, migration) 106 self.response.write('running.') 107 else: 108 migrate(migration, stop=True) 109 110 111 class DigestSync(AdminDash): 112 def post(self): 113 if not os.environ['SERVER_SOFTWARE'].startswith('Development/'): 114 self.abort(400) 115 # For local development, download GHIssueDigests from the production 116 # server. 117 result = urlfetch.fetch( 118 'https://github-dot-k8s-gubernator.appspot.com/digest', deadline=60) 119 if result.status_code != 200: 120 self.abort(result.status_code) 121 body = result.content 122 self.response.headers['content-type'] = 'text/plain' 123 self.response.write('%s\n' % len(body)) 124 self.response.write(repr(body[:8])) 125 results = pickle.loads(body) 126 for res in results: 127 res.key = ndb.Key(models.GHIssueDigest, res.key.id()) 128 self.response.write('%s\n' % res.key) 129 res.put() 130 131 132 app = webapp2.WSGIApplication([ 133 (r'/digest', Digest), 134 (r'/admin/?', AdminDash), 135 (r'/admin/reprocess', Reprocessor), 136 (r'/admin/digest_sync', DigestSync), 137 ], debug=True)