def ListAllConfigs(client): """Yield tuples of (config_set, revision, subscription).""" with client.transaction(read_only=True): # First look up the global index. subscription_index_key = client.key('SubscriptionIndex', 'global') subscription_index = client.get(subscription_index_key) if subscription_index is None: return # Then for each instance in the 'config_sets', create a key based on the # subscription_index_key as a parent, and look those up in one go. config_sets = client.get_multi([ client.key('Subscription', key, parent=subscription_index_key) for key in subscription_index['config_sets'] ]) # From there we can then go through each of the subscriptions in the retrieved # configs subscriptions = [] for config in config_sets: sheriff_config = sheriff_pb2.SheriffConfig() sheriff_config.ParseFromString(config['sheriff_config']) for subscription in sheriff_config.subscriptions: subscriptions.append(( config['config_set'], config['revision'], subscription, )) return subscriptions
def Validate(content): """Takes raw content to determine whether it is a valid configuration. This function ensures that the protocol buffer contents are a syntactically and semantically valid sheriff configuration. Args: content: Bytes representing a text format protocol buffer. Returns: A valid SherifConfig object. """ try: result = text_format.Parse(content, sheriff_pb2.SheriffConfig()) except text_format.ParseError as error: raise InvalidConfig('SheriffConfig Validation Error: %s' % (error)) # Go through each of the subscriptions, and ensure we find the semantically # required fields. for (index, subscription) in enumerate(result.subscriptions): if subscription.contact_email is None or len( subscription.contact_email) == 0: raise MissingEmail(result, index) if subscription.name is None or len(subscription.name) == 0: raise MissingName(result, index) if subscription.patterns is None or len(subscription.patterns) == 0: raise MissingPatterns(result, index) for (pattern_idx, pattern) in enumerate(subscription.patterns): field = pattern.WhichOneof('pattern') if field is None: raise InvalidPattern( result, index, pattern_idx, 'must provide either \'glob\' or \'regex\'') elif field == 'glob' and len(pattern.glob) == 0: raise InvalidPattern(result, index, pattern_idx, 'glob must not be empty') elif field == 'regex' and len(pattern.regex) == 0: raise InvalidPattern(result, index, pattern_idx, 'regex must not be empty') return result
def FindMatchingConfigs(client, request): """Yield tuples of (config_set, revision, subscription).""" with client.transaction(read_only=True): # First look up the global index. subscription_index_key = client.key('SubscriptionIndex', 'global') subscription_index = client.get(subscription_index_key) if subscription_index is None: return # Then for each instance in the 'config_sets', create a key based on the # subscription_index_key as a parent, and look those up in one go. config_sets = client.get_multi([ client.key('Subscription', key, parent=subscription_index_key) for key in subscription_index['config_sets'] ]) # From there we can then go through each of the subscriptions in the retrieved # configs, and yield the ones that have patterns applying to the request. for config in config_sets: sheriff_config = sheriff_pb2.SheriffConfig() sheriff_config.ParseFromString(config['sheriff_config']) for subscription in sheriff_config.subscriptions: for pattern in subscription.patterns: if pattern.HasField('glob'): matcher = re.compile(fnmatch.translate(pattern.glob)) elif pattern.HasField('regex'): matcher = re.compile(pattern.regex) else: # TODO(dberris): this is the extension point for supporting new # matchers; for now we'll skip the new patterns we don't handle yet. continue result = matcher.match(request.path) if result is not None: yield (config['config_set'], config['revision'], subscription) # We break immediately after the yield, so that we can ignore the rest # of the patterns defined for this subscription. break
def Validate(content): """Takes raw content to determine whether it is a valid configuration. This function ensures that the protocol buffer contents are a syntactically and semantically valid sheriff configuration. Args: content: Bytes representing a text format protocol buffer. Returns: A valid SherifConfig object. """ try: result = text_format.Parse(content, sheriff_pb2.SheriffConfig()) except text_format.ParseError as error: raise InvalidConfig('SheriffConfig Validation Error: %s' % (error)) # Go through each of the subscriptions, and ensure we find the semantically # required fields. def ValidatePattern(index, pattern_idx, pattern, group): field = pattern.WhichOneof('pattern') if field is None: raise InvalidPattern(result, index, pattern_idx, 'must provide either \'glob\' or \'regex\'', group) elif field == 'glob' and len(pattern.glob) == 0: raise InvalidPattern(result, index, pattern_idx, 'glob must not be empty', group) elif field == 'regex' and len(pattern.regex) == 0: raise InvalidPattern(result, index, pattern_idx, 'regex must not be empty', group) try: matcher.CompilePattern(pattern) except re2.error as e: raise InvalidPattern(result, index, pattern_idx, 'failed: %s' % (e, ), group) for (index, subscription) in enumerate(result.subscriptions): if subscription.contact_email is None or len( subscription.contact_email) == 0: raise MissingEmail(result, index) if subscription.name is None or len(subscription.name) == 0: raise MissingName(result, index) if not subscription.rules.match: raise MissingPatterns(result, index) for (pattern_idx, pattern) in enumerate(subscription.rules.match): ValidatePattern(index, pattern_idx, pattern, 'rules.match') for (pattern_idx, pattern) in enumerate(subscription.rules.exclude): ValidatePattern(index, pattern_idx, pattern, 'rules.exclude') for (pattern_idx, pattern) in enumerate(subscription.auto_triage.rules.match): ValidatePattern(index, pattern_idx, pattern, 'auto_triage.rules.match') for (pattern_idx, pattern) in enumerate(subscription.auto_triage.rules.exclude): ValidatePattern(index, pattern_idx, pattern, 'auto_triage.rules.exclude') for (pattern_idx, pattern) in enumerate(subscription.auto_bisection.rules.match): ValidatePattern(index, pattern_idx, pattern, 'auto_bisection.rules.match') for (pattern_idx, pattern) in enumerate(subscription.auto_bisection.rules.exclude): ValidatePattern(index, pattern_idx, pattern, 'auto_bisection.rules.exclude') # Validate patterns for anomaly configurations. for (anomaly_config_idx, anomaly_config) in enumerate(subscription.anomaly_configs): for (pattern_idx, pattern) in enumerate(anomaly_config.rules.match): ValidatePattern( index, pattern_idx, pattern, 'anomaly_configs[{}].rules.match'.format( anomaly_config_idx)) for (pattern_idx, pattern) in enumerate(anomaly_config.rules.exclude): ValidatePattern( index, pattern_idx, pattern, 'anomaly_configs[{}].rules.exclude'.format( anomaly_config_idx)) return result