|
7 | 7 | import re |
8 | 8 | import shutil |
9 | 9 | import sys |
| 10 | +import urllib.parse |
10 | 11 | from collections import defaultdict |
11 | 12 | from contextlib import suppress |
12 | 13 | from copy import deepcopy |
|
50 | 51 |
|
51 | 52 | LOG = logging.getLogger(__name__) |
52 | 53 |
|
| 54 | +REMOTE_SCHEMA_BASE = "https://raw.githubusercontent.com/canonical/cloud-init/main/cloudinit/config/schemas/" # noqa: E501 |
| 55 | + |
53 | 56 |
|
54 | 57 | # Note versions.schema.json is publicly consumed by schemastore.org. |
55 | 58 | # If we change the location of versions.schema.json in github, we need |
@@ -658,6 +661,50 @@ def netplan_validate_network_schema( |
658 | 661 | return True |
659 | 662 |
|
660 | 663 |
|
| 664 | +def _resource_retriever(uri): |
| 665 | + """Retrieve a "referencing" Resource from a given URI.""" |
| 666 | + import referencing |
| 667 | + import referencing.exceptions |
| 668 | + |
| 669 | + parsed = urllib.parse.urlparse(uri) |
| 670 | + if parsed.scheme == "file": |
| 671 | + local_path = urllib.parse.unquote(parsed.path) |
| 672 | + else: |
| 673 | + raise referencing.exceptions.NoSuchResource(f"Unsupported URI: {uri}") |
| 674 | + return referencing.Resource.from_contents( |
| 675 | + json.loads(load_text_file(local_path)) |
| 676 | + ) |
| 677 | + |
| 678 | + |
| 679 | +def _get_validator(schema: dict, strict_metaschema: bool): |
| 680 | + """Get a JSON schema validator for the given schema.""" |
| 681 | + try: |
| 682 | + (cloudinitValidator, FormatChecker) = get_jsonschema_validator() |
| 683 | + if strict_metaschema: |
| 684 | + validate_cloudconfig_metaschema( |
| 685 | + cloudinitValidator, schema, throw=False |
| 686 | + ) |
| 687 | + except ImportError: |
| 688 | + LOG.debug("Ignoring schema validation. jsonschema is not present") |
| 689 | + return None |
| 690 | + |
| 691 | + # The lowest supported version of jsonschema does not use "referencing", |
| 692 | + # but newer versions of jsonschema threaten to throw errors in the future |
| 693 | + # when using functionality that doesn't include "referencing". |
| 694 | + # Once the lowest supported version of jsonschema is 4.18.0 or greater, the |
| 695 | + # except clause can be removed. |
| 696 | + try: |
| 697 | + import referencing |
| 698 | + |
| 699 | + registry = referencing.Registry(retrieve=_resource_retriever) # type: ignore |
| 700 | + validator = cloudinitValidator( |
| 701 | + schema, format_checker=FormatChecker(), registry=registry |
| 702 | + ) |
| 703 | + except ImportError: |
| 704 | + validator = cloudinitValidator(schema, format_checker=FormatChecker()) |
| 705 | + return validator |
| 706 | + |
| 707 | + |
661 | 708 | @performance.timed("Validating schema") |
662 | 709 | def validate_cloudconfig_schema( |
663 | 710 | config: dict, |
@@ -721,17 +768,10 @@ def validate_cloudconfig_schema( |
721 | 768 |
|
722 | 769 | if schema is None: |
723 | 770 | schema = get_schema(schema_type) |
724 | | - try: |
725 | | - (cloudinitValidator, FormatChecker) = get_jsonschema_validator() |
726 | | - if strict_metaschema: |
727 | | - validate_cloudconfig_metaschema( |
728 | | - cloudinitValidator, schema, throw=False |
729 | | - ) |
730 | | - except ImportError: |
731 | | - LOG.debug("Ignoring schema validation. jsonschema is not present") |
732 | | - return False |
733 | 771 |
|
734 | | - validator = cloudinitValidator(schema, format_checker=FormatChecker()) |
| 772 | + validator = _get_validator(schema, strict_metaschema) |
| 773 | + if not validator: |
| 774 | + return False |
735 | 775 |
|
736 | 776 | errors: SchemaProblems = [] |
737 | 777 | deprecations: SchemaProblems = [] |
|
0 commit comments