OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # Copyright 2015 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. |
| 5 |
| 6 """CQ config validation library.""" |
| 7 |
| 8 import argparse |
| 9 # The 'from google import protobuf' below was replaced to fix an issue where |
| 10 # some users may have built-in google package installed on their system, which |
| 11 # is incompatible with cq_pb2 below. This hack can be removed after |
| 12 # http://crbug.com/503067 is resolved. |
| 13 import protobuf26 as protobuf |
| 14 import logging |
| 15 import re |
| 16 import sys |
| 17 |
| 18 from cq_client import cq_pb2 |
| 19 |
| 20 |
| 21 REQUIRED_FIELDS = [ |
| 22 'version', |
| 23 'rietveld', |
| 24 'rietveld.url', |
| 25 'verifiers', |
| 26 'cq_name', |
| 27 ] |
| 28 |
| 29 LEGACY_FIELDS = [ |
| 30 'svn_repo_url', |
| 31 'server_hooks_missing', |
| 32 'verifiers_with_patch', |
| 33 ] |
| 34 |
| 35 EMAIL_REGEXP = '^[^@]+@[^@]+\.[^@]+$' |
| 36 |
| 37 |
| 38 def _HasField(message, field_path): |
| 39 """Checks that at least one field with given path exist in the proto message. |
| 40 |
| 41 This function correctly handles repeated fields and will make sure that each |
| 42 repeated field will have required sub-path, e.g. if 'abc' is a repeated field |
| 43 and field_path is 'abc.def', then the function will only return True when each |
| 44 entry for 'abc' will contain at least one value for 'def'. |
| 45 |
| 46 Args: |
| 47 message (google.protobuf.message.Message): Protocol Buffer message to check. |
| 48 field_path (string): Path to the target field separated with ".". |
| 49 |
| 50 Return: |
| 51 True if at least one such field is explicitly set in the message. |
| 52 """ |
| 53 path_parts = field_path.split('.', 1) |
| 54 field_name = path_parts[0] |
| 55 sub_path = path_parts[1] if len(path_parts) == 2 else None |
| 56 |
| 57 field_labels = {fd.name: fd.label for fd in message.DESCRIPTOR.fields} |
| 58 repeated_field = (field_labels[field_name] == |
| 59 protobuf.descriptor.FieldDescriptor.LABEL_REPEATED) |
| 60 |
| 61 if sub_path: |
| 62 field = getattr(message, field_name) |
| 63 if repeated_field: |
| 64 if not field: |
| 65 return False |
| 66 return all(_HasField(entry, sub_path) for entry in field) |
| 67 else: |
| 68 return _HasField(field, sub_path) |
| 69 else: |
| 70 if repeated_field: |
| 71 return len(getattr(message, field_name)) > 0 |
| 72 else: |
| 73 return message.HasField(field_name) |
| 74 |
| 75 |
| 76 def IsValid(cq_config): |
| 77 """Validates a CQ config and prints errors/warnings to the screen. |
| 78 |
| 79 Args: |
| 80 cq_config (string): Unparsed text format of the CQ config proto. |
| 81 |
| 82 Returns: |
| 83 True if the config is valid. |
| 84 """ |
| 85 try: |
| 86 config = cq_pb2.Config() |
| 87 protobuf.text_format.Merge(cq_config, config) |
| 88 except protobuf.text_format.ParseError as e: |
| 89 logging.error('Failed to parse config as protobuf:\n%s', e) |
| 90 return False |
| 91 |
| 92 for fname in REQUIRED_FIELDS: |
| 93 if not _HasField(config, fname): |
| 94 logging.error('%s is a required field', fname) |
| 95 return False |
| 96 |
| 97 for fname in LEGACY_FIELDS: |
| 98 if _HasField(config, fname): |
| 99 logging.warn('%s is a legacy field', fname) |
| 100 |
| 101 |
| 102 for base in config.rietveld.project_bases: |
| 103 try: |
| 104 re.compile(base) |
| 105 except re.error: |
| 106 logging.error('failed to parse "%s" in project_bases as a regexp', base) |
| 107 return False |
| 108 |
| 109 # TODO(sergiyb): For each field, check valid values depending on its |
| 110 # semantics, e.g. email addresses, regular expressions etc. |
| 111 |
| 112 return True |
OLD | NEW |