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)
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})