async def compatibility_check(self, content_type, *, subject, version,
                                  request):
        """Check for schema compatibility"""
        body = request.json
        self.log.info(
            "Got request to check subject: %r, version_id: %r compatibility",
            subject, version)
        old = await self.subject_version_get(content_type=content_type,
                                             subject=subject,
                                             version=version,
                                             return_dict=True)
        self.log.info("Existing schema: %r, new_schema: %r", old["schema"],
                      body["schema"])
        try:
            schema_type = SchemaType(body.get("schemaType", "AVRO"))
            new = TypedSchema.parse(schema_type, body["schema"])
        except InvalidSchema:
            self.log.warning("Invalid schema: %r", body["schema"])
            self.r(body={
                "error_code": 44201,
                "message": "Invalid Avro schema"
            },
                   content_type=content_type,
                   status=422)
        try:
            old_schema_type = SchemaType(old.get("schemaType", "AVRO"))
            old_schema = TypedSchema.parse(old_schema_type, old["schema"])
        except InvalidSchema:
            self.log.warning("Invalid existing schema: %r", old["schema"])
            self.r(body={
                "error_code": 44201,
                "message": "Invalid Avro schema"
            },
                   content_type=content_type,
                   status=422)

        compat = Compatibility(source=old_schema,
                               target=new,
                               compatibility=old.get(
                                   "compatibility",
                                   self.ksr.config["compatibility"]))
        try:
            compat.check()
        except IncompatibleSchema as ex:
            self.log.warning(
                "Invalid schema %s found by compatibility check: old: %s new: %s",
                ex, old_schema, new)
            self.r({"is_compatible": False}, content_type)
        self.r({"is_compatible": True}, content_type)
    def write_new_schema_local(self, subject, body, content_type):
        """Since we're the master we get to write the new schema"""
        self.log.info("Writing new schema locally since we're the master")
        schema_type = SchemaType(body.get("schemaType", SchemaType.AVRO))
        try:
            new_schema = TypedSchema.parse(schema_type=schema_type,
                                           schema_str=body["schema"])
        except (InvalidSchema, InvalidSchemaType):
            self.log.warning("Invalid schema: %r",
                             body["schema"],
                             exc_info=True)
            self.r(body={
                "error_code": 44201,
                "message": f"Invalid {schema_type} schema"
            },
                   content_type=content_type,
                   status=422)
        if subject not in self.ksr.subjects or not self.ksr.subjects.get(
                subject)["schemas"]:
            schema_id = self.ksr.get_schema_id(new_schema)
            version = 1
            self.log.info(
                "Registering new subject: %r with version: %r to schema %r, schema_id: %r",
                subject, version, new_schema.to_json(), schema_id)
        else:
            # First check if any of the existing schemas for the subject match
            subject_data = self.ksr.subjects[subject]
            schemas = self.ksr.get_schemas(subject)
            if not schemas:  # Previous ones have been deleted by the user.
                version = max(self.ksr.subjects[subject]["schemas"]) + 1
                schema_id = self.ksr.get_schema_id(new_schema)
                self.log.info(
                    "Registering subject: %r, id: %r new version: %r with schema %r, schema_id: %r",
                    subject, schema_id, version, new_schema.to_json(),
                    schema_id)
                self.send_schema_message(
                    subject=subject,
                    schema=new_schema,
                    schema_id=schema_id,
                    version=version,
                    deleted=False,
                )
                self.r({"id": schema_id}, content_type)

            schema_versions = sorted(list(schemas))
            # Go through these in version order
            for version in schema_versions:
                schema = subject_data["schemas"][version]
                if schema["schema"] == new_schema:
                    self.r({"id": schema["id"]}, content_type)
                else:
                    self.log.debug("schema: %s did not match with: %s", schema,
                                   new_schema)

            compatibility = subject_data.get("compatibility",
                                             self.ksr.config["compatibility"])

            # Run a compatibility check between on file schema(s) and the one being submitted now
            # the check is either towards the latest one or against all previous ones in case of
            # transitive mode
            if compatibility in TRANSITIVE_MODES:
                check_against = schema_versions
            else:
                check_against = [schema_versions[-1]]

            for old_version in check_against:
                old_schema = subject_data["schemas"][old_version]["schema"]
                compat = Compatibility(old_schema,
                                       new_schema,
                                       compatibility=compatibility)
                try:
                    compat.check()
                except IncompatibleSchema as ex:
                    self.log.warning("Incompatible schema: %s", ex)
                    self.r(body={
                        "error_code":
                        409,
                        "message":
                        "Schema being registered is incompatible with an earlier schema"
                    },
                           content_type=content_type,
                           status=409)

            # We didn't find an existing schema and the schema is compatible so go and create one
            schema_id = self.ksr.get_schema_id(new_schema)
            version = max(self.ksr.subjects[subject]["schemas"]) + 1
            self.log.info(
                "Registering subject: %r, id: %r new version: %r with schema %r, schema_id: %r",
                subject, schema_id, version, new_schema.to_json(), schema_id)
        self.send_schema_message(
            subject=subject,
            schema=new_schema,
            schema_id=schema_id,
            version=version,
            deleted=False,
        )
        self.r({"id": schema_id}, content_type)
Esempio n. 3
0
    def write_new_schema_local(self, subject, body):
        """Since we're the master we get to write the new schema"""
        self.log.info("Writing new schema locally since we're the master")
        try:
            new_schema = avro.schema.Parse(body["schema"])
        except avro.schema.SchemaParseException:
            self.log.warning("Invalid schema: %r", body["schema"])
            self.r(body={
                "error_code": 44201,
                "message": "Invalid Avro schema"
            },
                   status=422)

        if subject not in self.ksr.subjects:
            schema_id = self.ksr.get_new_schema_id()
            version = 1
            self.log.info(
                "Registering new subject: %r with version: %r to schema %r, schema_id: %r",
                subject, version, new_schema.to_json(), schema_id)
        else:
            # First check if any of the existing schemas for the subject match
            subject_data = self.ksr.subjects[subject]
            schema_versions = sorted(list(subject_data["schemas"]))
            # Go through these in version order
            for version in schema_versions:
                schema = subject_data["schemas"][version]
                parsed_version_schema = avro.schema.Parse(schema["schema"])
                if parsed_version_schema == new_schema:
                    self.r({"id": schema["id"]})
                else:
                    self.log.debug("schema: %s did not match with: %s",
                                   schema["schema"], new_schema.to_json())

            # Run a compatibility check between the newest on file schema and the one being submitted now
            latest_schema = subject_data["schemas"][schema_versions[-1]]
            old_schema = avro.schema.Parse(latest_schema["schema"])
            compat = Compatibility(old_schema,
                                   new_schema,
                                   compatibility=subject_data.get(
                                       "compatibility",
                                       self.ksr.config["compatibility"]))
            try:
                compat.check()
            except IncompatibleSchema as ex:
                self.log.warning("Incompatible schema: %s", ex)
                self.r(body={
                    "error_code":
                    409,
                    "message":
                    "Schema being registered is incompatible with an earlier schema"
                },
                       status=409)

            # We didn't find an existing schema, so go and create one
            schema_id = self.ksr.get_new_schema_id()
            version = max(self.ksr.subjects[subject]["schemas"]) + 1
            self.log.info(
                "Registering subject: %r, id: %r new version: %r with schema %r, schema_id: %r",
                subject, schema_id, version, new_schema.to_json(), schema_id)
        self.send_schema_message(subject,
                                 new_schema.to_json(),
                                 schema_id,
                                 version,
                                 deleted=False)
        self.r({"id": schema_id})