github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/gubernator/filters.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 logging
    16  import datetime
    17  import hashlib
    18  import os
    19  import re
    20  import time
    21  import urllib
    22  
    23  import jinja2
    24  
    25  
    26  GITHUB_VIEW_TEMPLATE = 'https://github.com/%s/blob/%s/%s#L%s'
    27  GITHUB_COMMIT_TEMPLATE = 'https://github.com/%s/commit/%s'
    28  LINKIFY_RE = re.compile(
    29      r'(^\s*/\S*/)(kubernetes/(\S+):(\d+)(?: \+0x[0-9a-f]+)?)$',
    30      flags=re.MULTILINE)
    31  
    32  
    33  def do_timestamp(unix_time, css_class='timestamp', tmpl='%F %H:%M'):
    34      """Convert an int Unix timestamp into a human-readable datetime."""
    35      t = datetime.datetime.utcfromtimestamp(unix_time)
    36      return jinja2.Markup('<span class="%s" data-epoch="%s">%s</span>' %
    37                           (css_class, unix_time, t.strftime(tmpl)))
    38  
    39  
    40  def do_dt_to_epoch(dt):
    41      return time.mktime(dt.timetuple())
    42  
    43  
    44  def do_shorttimestamp(unix_time):
    45      t = datetime.datetime.utcfromtimestamp(unix_time)
    46      return jinja2.Markup('<span class="shorttimestamp" data-epoch="%s">%s</span>' %
    47                           (unix_time, t.strftime('%d %H:%M')))
    48  
    49  
    50  def do_duration(seconds):
    51      """Convert a numeric duration in seconds into a human-readable string."""
    52      hours, seconds = divmod(seconds, 3600)
    53      minutes, seconds = divmod(seconds, 60)
    54      if hours:
    55          return '%dh%dm' % (hours, minutes)
    56      if minutes:
    57          return '%dm%ds' % (minutes, seconds)
    58      else:
    59          if seconds < 10:
    60              return '%.2fs' % seconds
    61          return '%ds' % seconds
    62  
    63  
    64  def do_slugify(inp):
    65      """Convert an arbitrary string into a url-safe slug."""
    66      inp = re.sub(r'[^\w\s-]+', '', inp)
    67      return re.sub(r'\s+', '-', inp).lower()
    68  
    69  
    70  def do_linkify_stacktrace(inp, commit, repo):
    71      """Add links to a source code viewer for every mentioned source line."""
    72      inp = unicode(jinja2.escape(inp))
    73      if not commit:
    74          return jinja2.Markup(inp)  # this was already escaped, mark it safe!
    75      def rep(m):
    76          prefix, full, path, line = m.groups()
    77          return '%s<a href="%s">%s</a>' % (
    78              prefix,
    79              GITHUB_VIEW_TEMPLATE % (repo, commit, path, line),
    80              full)
    81      return jinja2.Markup(LINKIFY_RE.sub(rep, inp))
    82  
    83  
    84  def do_github_commit_link(commit, repo):
    85      commit_url = jinja2.escape(GITHUB_COMMIT_TEMPLATE % (repo, commit))
    86      return jinja2.Markup('<a href="%s">%s</a>' % (commit_url, commit[:8]))
    87  
    88  
    89  def do_testcmd(name):
    90      if name.startswith('k8s.io/'):
    91          try:
    92              pkg, name = name.split(' ')
    93          except ValueError:  # don't block the page render
    94              logging.error('Unexpected Go unit test name %r', name)
    95              return name
    96          return 'go test -v %s -run %s$' % (pkg, name)
    97      elif name.startswith('//'):
    98          return 'bazel test %s' % name
    99      else:
   100          name = re.sub(r'^\[k8s\.io\] ', '', name)
   101          name_escaped = re.escape(name).replace('\\ ', '\\s')
   102  
   103          test_args = ('--ginkgo.focus=%s$' % name_escaped)
   104          return "go run hack/e2e.go -v -test --test_args='%s'" % test_args
   105  
   106  
   107  def do_parse_pod_name(text):
   108      """Find the pod name from the failure and return the pod name."""
   109      p = re.search(r' pod (\S+)', text)
   110      if p:
   111          return re.sub(r'[\'"\\:]', '', p.group(1))
   112      else:
   113          return ""
   114  
   115  
   116  def do_label_attr(labels, name):
   117      '''
   118      >> do_label_attr(['needs-rebase', 'size/XS'], 'size')
   119      'XS'
   120      '''
   121      name += '/'
   122      for label in labels:
   123          if label.startswith(name):
   124              return label[len(name):]
   125      return ''
   126  
   127  def do_classify_size(payload):
   128      '''
   129      Determine the size class for a PR, based on either its labels or
   130      on the magnitude of its changes.
   131      '''
   132      size = do_label_attr(payload['labels'], 'size')
   133      if not size and 'additions' in payload and 'deletions' in payload:
   134          lines = payload['additions'] + payload['deletions']
   135          # based on mungegithub/mungers/size.go
   136          for limit, label in [
   137              (10, 'XS'),
   138              (30, 'S'),
   139              (100, 'M'),
   140              (500, 'L'),
   141              (1000, 'XL')
   142          ]:
   143              if lines < limit:
   144                  return label
   145          return 'XXL'
   146      return size
   147  
   148  
   149  def do_render_status(payload, user):
   150      states = set()
   151  
   152      text = 'Pending'
   153      if 'lgtm' in payload.get('labels', []):
   154          text = 'LGTM'
   155      elif user in payload.get('attn', {}):
   156          text = payload['attn'][user].title()
   157          if '#' in text:  # strip start/end attn timestamps
   158              text = text[:text.index('#')]
   159  
   160      for ctx, (state, _url, desc) in payload.get('status', {}).items():
   161          if ctx == 'Submit Queue' and state == 'pending':
   162              if 'does not have lgtm' in desc.lower():
   163                  # Don't show overall status as pending when Submit
   164                  # won't continue without LGTM.
   165                  continue
   166          if ctx == 'code-review/reviewable' and state == 'pending':
   167              # Reviewable isn't a CI, so we don't care if it's pending.
   168              # Its dashboard might replace all of this eventually.
   169              continue
   170          states.add(state)
   171  
   172      icon = ''
   173      if 'failure' in states:
   174          icon = 'x'
   175          state = 'failure'
   176      elif 'pending' in states:
   177          icon = 'primitive-dot'
   178          state = 'pending'
   179      elif 'success' in states:
   180          icon = 'check'
   181          state = 'success'
   182      if icon:
   183          icon = '<span class="text-%s octicon octicon-%s"></span>' % (
   184              state, icon)
   185      return jinja2.Markup('%s%s' % (icon, text))
   186  
   187  
   188  def do_get_latest(payload, user):
   189      text = payload.get('attn', {}).get(user)
   190      if not text:
   191          return None
   192      if '#' not in text:
   193          return None
   194      _text, _start, latest = text.rsplit('#', 2)
   195      return float(latest)
   196  
   197  
   198  def do_ltrim(s, needle):
   199      if s.startswith(needle):
   200          return s[len(needle):]
   201      return s
   202  
   203  
   204  def do_select(seq, pred):
   205      return filter(pred, seq)
   206  
   207  
   208  def do_tg_url(testgrid_query, test_name=''):
   209      if test_name:
   210          regex = '^Overall$|' + re.escape(test_name)
   211          testgrid_query += '&include-filter-by-regex=%s' % urllib.quote(regex)
   212      return 'https://k8s-testgrid.appspot.com/%s' % testgrid_query
   213  
   214  
   215  def do_gcs_browse_url(gcs_path):
   216      if not gcs_path.endswith('/'):
   217          gcs_path += '/'
   218      return 'http://gcsweb.k8s.io/gcs' + gcs_path
   219  
   220  
   221  static_hashes = {}
   222  
   223  def do_static(filename):
   224      filename = 'static/%s' % filename
   225      if filename not in static_hashes:
   226          data = open(filename).read()
   227          static_hashes[filename] = hashlib.sha1(data).hexdigest()[:10]
   228      return '/%s?%s' % (filename, static_hashes[filename])
   229  
   230  
   231  do_basename = os.path.basename
   232  do_dirname = os.path.dirname
   233  do_quote_plus = urllib.quote_plus
   234  
   235  
   236  def register(filters):
   237      """Register do_* functions in this module in a dictionary."""
   238      for name, func in globals().items():
   239          if name.startswith('do_'):
   240              filters[name[3:]] = func