#!/usr/bin/env python3 # Validate the API package structure. Usage: # # ./tools/api/validate_structure.py import pathlib import re import sys # Only v2 protos are allowed in these trees. V2_ONLY_PATHS = [ 'api', 'config/filter', 'config/transport_socket', 'config/common/dynamic_forward_proxy', 'config/common/tap', ] # These are the only legacy trees that we permit not to terminate with a versioned suffix. VERSIONLESS_PATHS = [ 'annotations', 'api/v2/ratelimit', 'api/v2/auth', 'api/v2/listener', 'api/v2/core', 'api/v2/endpoint', 'api/v2/route', 'api/v2/cluster', 'type', 'type/matcher', 'config/cluster/redis', 'config/retry/previous_priorities', ] class ValidationError(Exception): pass # Extract major version and full API version string from a proto path. def ProtoApiVersion(proto_path): match = re.match('v(\d+).*', proto_path.parent.name) if match: return str(proto_path.parent.name)[1:], int(match.group(1)) return None, 0 # Validate a single proto path. def ValidateProtoPath(proto_path): version_str, major_version = ProtoApiVersion(proto_path) # Validate version-less paths. if major_version == 0: if not any(str(proto_path.parent) == p for p in VERSIONLESS_PATHS): raise ValidationError('Package is missing a version') # Validate that v3+ versions are regular. if major_version >= 3: if not re.match('\d+(alpha)?$', version_str): raise ValidationError('Invalid v3+ version: %s' % version_str) # Validate v2-only paths. for p in V2_ONLY_PATHS: if str(proto_path).startswith(p): raise ValidationError('v3+ protos are not allowed in %s' % p) # Validate a list of proto paths. def ValidateProtoPaths(proto_paths): error_msgs = [] for proto_path in proto_paths: try: ValidateProtoPath(proto_path) except ValidationError as e: error_msgs.append('Invalid .proto location [%s]: %s' % (proto_path, e)) return error_msgs if __name__ == '__main__': api_root = 'api/envoy' api_protos = pathlib.Path(api_root).rglob('*.proto') error_msgs = ValidateProtoPaths(p.relative_to(api_root) for p in api_protos) if error_msgs: for m in error_msgs: print(m) sys.exit(1) sys.exit(0)