1# Copyright 2015 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""The database model Sheriff, for sheriff rotations.""" 6 7import re 8import urlparse 9 10from google.appengine.ext import ndb 11 12from dashboard.models import internal_only_model 13 14# Acceptable suffixes for internal-only sheriff rotation email addresses. 15_ALLOWED_INTERNAL_ONLY_EMAIL_DOMAINS = ( 16 '@google.com', 17 '@chromium.org', 18 '@grotations.appspotmail.com', 19) 20 21 22class ValidationError(Exception): 23 """Represents a failed validation.""" 24 pass 25 26 27def _UrlValidator(prop, val): # pylint: disable=unused-argument 28 """Validates an URL property. 29 30 The first argument is required, since this function is used as a validator 31 function for a property. For more about validator functions in ndb, see: 32 https://developers.google.com/appengine/docs/python/ndb/properties#options 33 34 Args: 35 prop: The property object. 36 val: The string value to be validated as a URL. 37 38 Raises: 39 ValidationError: The input string is invalid. 40 """ 41 parsed = urlparse.urlparse(val) 42 if not (parsed.scheme and parsed.netloc and parsed.path): 43 raise ValidationError('Invalid URL %s' % val) 44 45 46def _EmailValidator(prop, val): # pylint: disable=unused-argument 47 """Validates an email address property.""" 48 # Note, this doesn't check the domain of the email address; 49 # that is done in the pre-put hook in Sheriff. 50 if not re.match(r'[\w.+-]+@[\w.-]', val): 51 raise ValidationError('Invalid email %s' % val) 52 53 54class Sheriff(internal_only_model.InternalOnlyModel): 55 """Configuration options for sheriffing alerts. One per sheriff.""" 56 57 # Many perf sheriff email addresses are stored at a url. If there is a url 58 # specified here, it will be used as the perf sheriff address. But the email 59 # below will also be cc-ed, so that alerts can be archived to groups. 60 url = ndb.StringProperty(validator=_UrlValidator, indexed=False) 61 email = ndb.StringProperty(validator=_EmailValidator, indexed=False) 62 63 internal_only = ndb.BooleanProperty(indexed=True) 64 summarize = ndb.BooleanProperty(indexed=True, default=False) 65 66 # A list of patterns. Each pattern is a string which can match parts of the 67 # test path either exactly, or use '*' as a wildcard. Test paths matching 68 # these patterns will use this sheriff configuration for alerting. 69 patterns = ndb.StringProperty(repeated=True, indexed=False) 70 71 # A list of labels to add to all bugs for the sheriffing rotation. 72 labels = ndb.StringProperty(repeated=True, indexed=False) 73 74 # Number of days before the Sheriff should get an alert about not having 75 # received any data for a particular TestMetadata entity; -1 means no alerts. 76 # If a sheriff wants to receive these alerts, the number should be greater 77 # than zero but less than two weeks (the amount of time before tests become 78 # marked as deprecated). 79 stoppage_alert_delay = ndb.IntegerProperty(default=-1, indexed=True) 80 81 def _pre_put_hook(self): 82 """Checks that the Sheriff properties are OK before putting.""" 83 if (self.internal_only and self.email 84 and not _IsAllowedInternalOnlyEmail(self.email)): 85 raise ValidationError('Invalid internal sheriff email %s' % self.email) 86 87 88def _IsAllowedInternalOnlyEmail(email): 89 """Checks whether an email address is OK for an internal-only sheriff.""" 90 return email.endswith(_ALLOWED_INTERNAL_ONLY_EMAIL_DOMAINS) 91