예제 #1
0
    def test_annotated_cloudconfig_file_schema_annotates_and_adds_footer(self):
        """With schema_errors, error lines are annotated and a footer added."""
        content = dedent("""\
            #cloud-config
            # comment
            ntp:
              pools: [-99, 75]
            """).encode()
        expected = dedent("""\
            #cloud-config
            # comment
            ntp:		# E1
              pools: [-99, 75]		# E2,E3

            # Errors: -------------
            # E1: Some type error
            # E2: -99 is not a string
            # E3: 75 is not a string

            """)
        parsed_config, schemamarks = load_with_marks(content[13:])
        schema_errors = [
            ("ntp", "Some type error"),
            ("ntp.pools.0", "-99 is not a string"),
            ("ntp.pools.1", "75 is not a string"),
        ]
        assert expected == annotated_cloudconfig_file(parsed_config,
                                                      content,
                                                      schema_errors,
                                                      schemamarks=schemamarks)
예제 #2
0
 def test_annotated_cloudconfig_file_annotates_separate_line_items(self):
     """Errors are annotated for lists with items on separate lines."""
     content = dedent("""\
         #cloud-config
         # comment
         ntp:
           pools:
             - -99
             - 75
         """).encode()
     expected = dedent("""\
         ntp:
           pools:
             - -99		# E1
             - 75		# E2
         """)
     parsed_config, schemamarks = load_with_marks(content[13:])
     schema_errors = [
         ("ntp.pools.0", "-99 is not a string"),
         ("ntp.pools.1", "75 is not a string"),
     ]
     assert expected in annotated_cloudconfig_file(parsed_config,
                                                   content,
                                                   schema_errors,
                                                   schemamarks=schemamarks)
예제 #3
0
 def test_annotated_cloudconfig_file_no_schema_errors(self):
     """With no schema_errors, print the original content."""
     content = b"ntp:\n  pools: [ntp1.pools.com]\n"
     parse_cfg, schemamarks = load_with_marks(content)
     assert content == annotated_cloudconfig_file(parse_cfg,
                                                  content,
                                                  schema_errors=[],
                                                  schemamarks=schemamarks)
예제 #4
0
def validate_cloudconfig_file(config_path, schema, annotate=False):
    """Validate cloudconfig file adheres to a specific jsonschema.

    @param config_path: Path to the yaml cloud-config file to parse, or None
        to default to system userdata from Paths object.
    @param schema: Dict describing a valid jsonschema to validate against.
    @param annotate: Boolean set True to print original config file with error
        annotations on the offending lines.

    @raises SchemaValidationError containing any of schema_errors encountered.
    @raises RuntimeError when config_path does not exist.
    """
    if config_path is None:
        # Use system's raw userdata path
        if os.getuid() != 0:
            raise RuntimeError(
                "Unable to read system userdata as non-root user."
                " Try using sudo")
        paths = read_cfg_paths()
        user_data_file = paths.get_ipath_cur("userdata_raw")
        content = load_file(user_data_file, decode=False)
    else:
        if not os.path.exists(config_path):
            raise RuntimeError(
                "Configfile {0} does not exist".format(config_path))
        content = load_file(config_path, decode=False)
    if not content.startswith(CLOUD_CONFIG_HEADER):
        errors = ((
            "format-l1.c1",
            'File {0} needs to begin with "{1}"'.format(
                config_path, CLOUD_CONFIG_HEADER.decode()),
        ), )
        error = SchemaValidationError(errors)
        if annotate:
            print(
                annotated_cloudconfig_file({}, content, error.schema_errors,
                                           {}))
        raise error
    try:
        if annotate:
            cloudconfig, marks = safeyaml.load_with_marks(content)
        else:
            cloudconfig = safeyaml.load(content)
            marks = {}
    except (yaml.YAMLError) as e:
        line = column = 1
        mark = None
        if hasattr(e, "context_mark") and getattr(e, "context_mark"):
            mark = getattr(e, "context_mark")
        elif hasattr(e, "problem_mark") and getattr(e, "problem_mark"):
            mark = getattr(e, "problem_mark")
        if mark:
            line = mark.line + 1
            column = mark.column + 1
        errors = ((
            "format-l{line}.c{col}".format(line=line, col=column),
            "File {0} is not valid yaml. {1}".format(config_path, str(e)),
        ), )
        error = SchemaValidationError(errors)
        if annotate:
            print(
                annotated_cloudconfig_file({}, content, error.schema_errors,
                                           {}))
        raise error from e
    if not isinstance(cloudconfig, dict):
        # Return a meaningful message on empty cloud-config
        if not annotate:
            raise RuntimeError("Cloud-config is not a YAML dict.")
    try:
        validate_cloudconfig_schema(cloudconfig, schema, strict=True)
    except SchemaValidationError as e:
        if annotate:
            print(
                annotated_cloudconfig_file(cloudconfig, content,
                                           e.schema_errors, marks))
        raise
예제 #5
0
 def test_schema_marks_preserved(self, source_yaml, loaded_yaml,
                                 schemamarks):
     assert (loaded_yaml, schemamarks) == load_with_marks(source_yaml)