Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Protobuf support #296

Merged
merged 209 commits into from
Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
209 commits
Select commit Hold shift + click to select a range
ba5c9c1
protbuf support skeleton
libretto Mar 30, 2021
6e782ca
Revert "protbuf support skeleton"
amrutha-shanbhag Apr 20, 2021
3e5f83b
Merge pull request #3 from instaclustr/revert-direct-master-commits
amrutha-shanbhag Apr 20, 2021
c7cf56f
Merge pull request #4 from aiven/master
amrutha-shanbhag Apr 20, 2021
d3aff39
Add protobuf skeleton
libretto Apr 21, 2021
46e23c7
Add skeleton files
libretto Apr 21, 2021
f6be627
remove unfinished tests
libretto Apr 21, 2021
4b2fdb7
fixup lint errors
libretto Apr 21, 2021
e28b735
Changed project structure, and added one test and debugged issues for…
libretto Apr 22, 2021
3063f58
fixup lint issues
libretto Apr 22, 2021
62e56eb
protobuf parser draft save
libretto Apr 25, 2021
da4fa3b
beta version of prot_parser class (no dependencies)
libretto Apr 30, 2021
8ffba9c
lint issues fuxup
libretto May 1, 2021
9ca84d8
fixup by @hackaugusto suggestions
libretto May 6, 2021
99dfb79
Merge branch 'aiven:master' into master
libretto May 19, 2021
adb145c
Ported first part of tests from Wire project. ProtoParser code debugg…
libretto May 19, 2021
e37d3e8
Merge remote-tracking branch 'origin/master' into protobuf-parser
libretto May 19, 2021
ad490fb
fixup lint problem
libretto May 19, 2021
4b88525
Add protobuf skeleton (#6)
libretto May 19, 2021
0677729
Merge branch 'master' into protobuf-parser
libretto May 20, 2021
af054c7
fixup lint issues after conflict remove
libretto May 20, 2021
fcfabb3
another part of tests added
libretto May 25, 2021
e460350
change project to pytest and add references to square/wire project files
libretto May 31, 2021
a9abaee
Merge pull request #7 from instaclustr/protobuf-parser
h3nd24 Jun 3, 2021
620759f
merge for sync with aiven/karapace
libretto Jun 3, 2021
26928e4
sync with latest dev branch
libretto Jun 3, 2021
a9dda6f
finished porting of test_proto_parser module, ported test_proto_file_…
libretto Jun 3, 2021
9ff4580
Merge with master of aiven/karapace to our repository (#8)
libretto Jun 3, 2021
39f5b08
add next part of unittests for protoparser library
libretto Jun 9, 2021
29eb67f
fixup lint issues
libretto Jun 9, 2021
b68ebba
Merge remote-tracking branch 'origin/master' into protobuf-skeleton
libretto Jun 9, 2021
1271cf3
fixup lint changing trailing whitespace strings issue
libretto Jun 10, 2021
35834a8
fixup set of PR issues
libretto Jun 11, 2021
67224a7
fixup style issues (mypy and other)
libretto Jun 16, 2021
20f884a
fixup class variable issues
libretto Jun 21, 2021
1ac76f1
lint issues
libretto Jun 21, 2021
3ed6dd5
Merge pull request #9 from instaclustr/protobuf-skeleton
h3nd24 Jul 8, 2021
4ea2d8a
square.wire.protobuf parser utilization (parse/generate protobuf sche…
libretto Aug 17, 2021
021c5de
Merge branch 'master' of https://github.com/aiven/karapace into merge…
libretto Aug 31, 2021
7ded052
Merge pull request #10 from instaclustr/merge-with-aiven
h3nd24 Sep 1, 2021
67a3c8a
commit after merge
libretto Sep 8, 2021
f465399
add support of PROTOBUF to /schemas/types
libretto Sep 8, 2021
0c9f686
fixup /schemas/types test
libretto Sep 11, 2021
e587b58
Fixup workaround issue
libretto Sep 11, 2021
9341a8e
Update karapace/protobuf/schema.py
libretto Sep 14, 2021
81cb911
fixup by PR comments of Augusto Hack
libretto Sep 14, 2021
04b3d3b
remove unused decorator
libretto Sep 16, 2021
40e3eb9
Update schema.py
libretto Sep 27, 2021
e3bf15c
Update schema.py
libretto Sep 27, 2021
da951fe
Update schema.py
libretto Sep 27, 2021
9f5d120
Update schema.py
libretto Sep 27, 2021
6b2b134
Merge pull request #12 from instaclustr/protobuf-utilize
h3nd24 Oct 1, 2021
a333625
backup
libretto Oct 1, 2021
d56b9dd
backup compatibility workaround
libretto Oct 3, 2021
31a399a
Tests and Debug workaround
libretto Oct 11, 2021
810b54d
debugging workaround
libretto Oct 12, 2021
1fc7f39
fixup file name
libretto Oct 12, 2021
cc1266d
fixup bugs with tests
libretto Oct 12, 2021
008fb07
integration test workaround backup
libretto Oct 15, 2021
1cead31
add more integration tests/ tests workarond
libretto Oct 17, 2021
c638b5a
add more tests/test driven bugfixes
libretto Oct 17, 2021
cba78b4
Merge branch 'master' into protobuf-skeleton
libretto Oct 17, 2021
c77833b
remove debug workaround
libretto Oct 17, 2021
048964c
pylint fixup
libretto Oct 17, 2021
c88847a
Sync to aiven/karapace master (#14)
libretto Oct 17, 2021
8d314a1
Merge branch 'master' into protobuf-skeleton
libretto Oct 19, 2021
c7063a9
message verification workaround
libretto Nov 8, 2021
1570b4c
style improving workaround
libretto Nov 9, 2021
93fc461
PR/styles fixup workaround
libretto Nov 10, 2021
0b14b5b
fixup
libretto Nov 10, 2021
cb9b72a
style workarounds
libretto Nov 10, 2021
e11d1e7
commit after merge
libretto Nov 13, 2021
09b788f
backup of debug workaround
libretto Nov 20, 2021
4409b59
backup of debug workaround
libretto Nov 20, 2021
74b615a
workarounds
libretto Nov 23, 2021
9423ba2
fixup annotations
libretto Nov 23, 2021
f1a0812
backup workaround
libretto Nov 24, 2021
628ec6f
Merge branch 'protobuf-skeleton' into protobuf-utilize
libretto Nov 24, 2021
11db3c5
Update karapace/protobuf/proto_file_element.py
libretto Nov 24, 2021
924f60a
Update karapace/protobuf/proto_file_element.py
libretto Nov 24, 2021
deadd7c
fuxup
libretto Nov 24, 2021
08e66c4
fixup
libretto Nov 24, 2021
0d22250
fixup minor bugf
libretto Nov 24, 2021
4d8caa3
fixup
libretto Nov 26, 2021
68d0a0a
backup working unit_test
libretto Nov 26, 2021
fd90560
Merge pull request #13 from instaclustr/protobuf-skeleton
h3nd24 Nov 28, 2021
71b13fc
fixup
libretto Nov 29, 2021
fba48bb
sync with aiven/karapace
libretto Nov 29, 2021
bac33cf
::Merge branch 'master' into protobuf-skeleton
libretto Nov 29, 2021
7f4a9c8
Merge pull request #15 from instaclustr/merge-with-aiven
h3nd24 Nov 30, 2021
9f71ee8
merge with master
libretto Nov 30, 2021
529e63f
fixups
libretto Nov 30, 2021
bf1e374
fixup lint warning
libretto Nov 30, 2021
bab3f4c
fixup
libretto Nov 30, 2021
01e776c
fixup
libretto Nov 30, 2021
ced47af
fixup
libretto Nov 30, 2021
9696121
fixup
libretto Nov 30, 2021
217b217
investigate github issue
libretto Dec 1, 2021
1009450
lint fix
libretto Dec 1, 2021
5ff8dcb
debug
libretto Dec 1, 2021
dd2c6ba
fixup requirements
libretto Dec 1, 2021
a7c943a
debug
libretto Dec 1, 2021
34545e4
Update tests.yml
libretto Dec 1, 2021
d9d9332
debug
libretto Dec 1, 2021
1cb26dc
Merge branch 'protobuf-utilize' of https://github.com/instaclustr/kar…
libretto Dec 1, 2021
7c83782
fixup
libretto Dec 1, 2021
0548f93
fixup
libretto Dec 1, 2021
28c71de
debug
libretto Dec 1, 2021
f3420d7
sync to protobuf-utilize
libretto Dec 1, 2021
2004a1e
debug
libretto Dec 1, 2021
df13004
debug
libretto Dec 1, 2021
cd362be
lint fixups
libretto Dec 1, 2021
caa4fdd
fixup python workflow to 3.7 and 3.8
libretto Dec 1, 2021
0e479c0
improving code style/tests
libretto Dec 2, 2021
49e6d3b
merge with protobuf-utilize
libretto Dec 4, 2021
57579d9
debugging
libretto Dec 4, 2021
ecf9cba
debug
libretto Dec 4, 2021
7b9ef6a
debug
libretto Dec 5, 2021
1e3c317
debug
libretto Dec 7, 2021
35eac2e
debug
libretto Dec 7, 2021
46e732b
debug
libretto Dec 7, 2021
76afe9f
debug
libretto Dec 7, 2021
54717ee
debug
libretto Dec 7, 2021
724b03b
debug workflow
libretto Dec 10, 2021
7ff389e
debug
libretto Dec 10, 2021
12166b9
Update karapace/protobuf/exception.py
libretto Dec 11, 2021
6e6b30e
Update karapace/protobuf/io.py
libretto Dec 11, 2021
853038f
fixup
libretto Dec 11, 2021
84095fa
fixup
libretto Dec 11, 2021
eb57ff0
Merge branch 'protobuf-utilize' of https://github.com/instaclustr/kar…
libretto Dec 11, 2021
50f68fd
Update karapace/protobuf/io.py
libretto Dec 11, 2021
b7e4dfc
Update karapace/protobuf/io.py
libretto Dec 11, 2021
5bdafdf
fixup
libretto Dec 11, 2021
f0d63ab
Merge branch 'protobuf-utilize' of https://github.com/instaclustr/kar…
libretto Dec 11, 2021
06bccf7
Update karapace/protobuf/protobuf_to_dict.py
libretto Dec 11, 2021
b5c3453
fixup by hackaugusto suggestions
libretto Dec 11, 2021
85c825a
Merge branch 'protobuf-utilize' of https://github.com/instaclustr/kar…
libretto Dec 11, 2021
5b3794a
Update karapace/protobuf/protobuf_to_dict.py
libretto Dec 11, 2021
6bfa9c4
Update karapace/protobuf/protobuf_to_dict.py
libretto Dec 11, 2021
0cb7ec5
Update karapace/protobuf/protobuf_to_dict.py
libretto Dec 11, 2021
684fb90
Update karapace/protobuf/protobuf_to_dict.py
libretto Dec 11, 2021
0fc9153
Update karapace/protobuf/protobuf_to_dict.py
libretto Dec 11, 2021
e1ae30c
Update tests/unit/conftest.py
libretto Dec 11, 2021
b77a991
fixup
libretto Dec 11, 2021
3e24091
Merge branch 'protobuf-utilize' of https://github.com/instaclustr/kar…
libretto Dec 11, 2021
bc719a6
fixup test
libretto Dec 11, 2021
05f43c9
Update karapace/protobuf/protobuf_to_dict.py
libretto Dec 11, 2021
0b64ebc
Update karapace/protobuf/protobuf_to_dict.py
libretto Dec 11, 2021
3d8ed00
Update karapace/protobuf/protobuf_to_dict.py
libretto Dec 11, 2021
27ce006
Update karapace/protobuf/protobuf_to_dict.py
libretto Dec 11, 2021
675d140
fixup
libretto Dec 11, 2021
e0da122
Merge branch 'protobuf-utilize' of https://github.com/instaclustr/kar…
libretto Dec 11, 2021
26e8196
fixup
libretto Dec 11, 2021
53908ba
fixup gitignore
libretto Dec 11, 2021
25d9112
fixup by PR
libretto Dec 17, 2021
78fb9ee
last commit of this PR
libretto Dec 19, 2021
acd633b
last commit of this PR
libretto Dec 19, 2021
17d8aeb
decrease test load
libretto Dec 19, 2021
063c95e
decrease test load
libretto Dec 19, 2021
4a89533
decrease test load
libretto Dec 19, 2021
70ab64f
decrease test load
libretto Dec 19, 2021
5dbf938
decrease test load
libretto Dec 19, 2021
3116d63
Merge pull request #16 from instaclustr/protobuf-utilize
h3nd24 Dec 20, 2021
6e99fa6
remove unneded dependency
libretto Dec 20, 2021
c096dbd
Merge pull request #18 from instaclustr/protobuf-utilize
libretto Dec 21, 2021
84d4e76
merge with master
libretto Dec 22, 2021
1858e7e
Merge branch 'aiven:master' into master
libretto Jan 11, 2022
60e9d78
fixup dependencies because of https://github.blog/2021-09-01-improvin…
libretto Jan 11, 2022
c34974b
Update checks.py
libretto Jan 13, 2022
abee245
Update config.py
libretto Jan 13, 2022
447d46d
Update __init__.py
libretto Jan 13, 2022
899212c
Update karapace/protobuf/compare_result.py
libretto Jan 13, 2022
5d8df5a
Update karapace/protobuf/compare_type_storage.py
libretto Jan 13, 2022
03842e1
Update karapace/protobuf/compare_type_storage.py
libretto Jan 13, 2022
1a0473a
fixup
libretto Jan 13, 2022
36e6032
Merge branch 'master' of https://github.com/instaclustr/karapace
libretto Jan 13, 2022
383f715
Update karapace/protobuf/compare_type_storage.py
libretto Jan 13, 2022
876996a
fixup circular dependency
libretto Jan 13, 2022
fe60f7c
Update karapace/protobuf/compare_type_storage.py
libretto Jan 13, 2022
b658e81
Update karapace/protobuf/compare_type_storage.py
libretto Jan 13, 2022
e082ab3
Update field.py
libretto Jan 13, 2022
aad40a3
Apply suggestions from code review
libretto Jan 13, 2022
db3f592
fixup style
libretto Jan 13, 2022
f8745d2
fixup annotations part one
libretto Jan 18, 2022
8c90d06
Update karapace/protobuf/proto_type.py
libretto Jan 21, 2022
3789079
Update karapace/protobuf/proto_type.py
libretto Jan 21, 2022
64bb4b0
fixups of styles
libretto Jan 21, 2022
340ca13
Merge branch 'master' of https://github.com/instaclustr/karapace
libretto Jan 21, 2022
dcb72ac
Update karapace/protobuf/proto_type.py
libretto Jan 21, 2022
00fe7d8
Update karapace/protobuf/proto_type.py
libretto Jan 21, 2022
34f4ccc
Update karapace/protobuf/proto_type.py
libretto Jan 21, 2022
ca4ce17
Update karapace/protobuf/proto_type.py
libretto Jan 21, 2022
a7bed1f
fixup by review
libretto Jan 21, 2022
fa07747
Update karapace/protobuf/syntax_reader.py
libretto Jan 21, 2022
f68fcbc
fixup by review
libretto Jan 21, 2022
e626424
Merge branch 'master' of https://github.com/instaclustr/karapace
libretto Jan 21, 2022
9436285
Update karapace/protobuf/type_element.py
libretto Jan 21, 2022
344ca20
Merge branch 'master' of https://github.com/instaclustr/karapace
libretto Jan 21, 2022
1780139
Update karapace/schema_reader.py
libretto Jan 21, 2022
9cac2aa
fixup by review
libretto Jan 21, 2022
b4dd33a
Merge branch 'master' of https://github.com/instaclustr/karapace
libretto Jan 21, 2022
9371d07
fixup by review
libretto Jan 21, 2022
3f22da7
Update checks.py
libretto Jan 24, 2022
153bbb1
Update checks.py
libretto Jan 24, 2022
42a365a
Update compare_result.py
libretto Jan 24, 2022
8c9b0ee
Update group_element.py
libretto Jan 24, 2022
8e2f2d3
Update karapace/protobuf/io.py
libretto Jan 24, 2022
7967d49
fixup minor issues
libretto Jan 24, 2022
d50be12
Merge branch 'master' of https://github.com/instaclustr/karapace
libretto Jan 24, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ jobs:
- name: Install dependencies
run: python -m pip install -r requirements-dev.txt

- name: Install Protoc
uses: arduino/setup-protoc@v1
with:
version: '3.13.0'


- name: Execute unit-tests
run: make unittest

Expand Down
27 changes: 27 additions & 0 deletions karapace/compatibility/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
SchemaIncompatibilityType
)
from karapace.compatibility.jsonschema.checks import compatibility as jsonschema_compatibility
from karapace.compatibility.protobuf.checks import check_protobuf_schema_compatibility
from karapace.schema_reader import SchemaType, TypedSchema

import logging
Expand Down Expand Up @@ -63,6 +64,10 @@ def check_jsonschema_compatibility(reader: Draft7Validator, writer: Draft7Valida
return jsonschema_compatibility(reader, writer)


def check_protobuf_compatibility(reader, writer) -> SchemaCompatibilityResult:
hackaugusto marked this conversation as resolved.
Show resolved Hide resolved
return check_protobuf_schema_compatibility(reader, writer)


def check_compatibility(
old_schema: TypedSchema, new_schema: TypedSchema, compatibility_mode: CompatibilityModes
) -> SchemaCompatibilityResult:
Expand Down Expand Up @@ -128,6 +133,28 @@ def check_compatibility(
)
)

elif old_schema.schema_type is SchemaType.PROTOBUF:
if compatibility_mode in {CompatibilityModes.BACKWARD, CompatibilityModes.BACKWARD_TRANSITIVE}:
result = check_protobuf_compatibility(
reader=new_schema.schema,
writer=old_schema.schema,
)
elif compatibility_mode in {CompatibilityModes.FORWARD, CompatibilityModes.FORWARD_TRANSITIVE}:
result = check_protobuf_compatibility(
reader=old_schema.schema,
writer=new_schema.schema,
)

elif compatibility_mode in {CompatibilityModes.FULL, CompatibilityModes.FULL_TRANSITIVE}:
result = check_protobuf_compatibility(
reader=new_schema.schema,
writer=old_schema.schema,
)
result = result.merged_with(check_protobuf_compatibility(
reader=old_schema.schema,
writer=new_schema.schema,
))

else:
result = SchemaCompatibilityResult.incompatible(
incompat_type=SchemaIncompatibilityType.type_mismatch,
Expand Down
Empty file.
33 changes: 33 additions & 0 deletions karapace/compatibility/protobuf/checks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from karapace.avro_compatibility import SchemaCompatibilityResult, SchemaCompatibilityType
from karapace.protobuf.compare_result import CompareResult
from karapace.protobuf.schema import ProtobufSchema

import logging

log = logging.getLogger(__name__)


def check_protobuf_schema_compatibility(reader: ProtobufSchema, writer: ProtobufSchema) -> SchemaCompatibilityResult:
result = CompareResult()
log.debug("READER: %s", reader.to_schema())
log.debug("WRITER: %s", writer.to_schema())
writer.compare(reader, result)
log.debug("IS_COMPATIBLE %s", result.is_compatible())
if result.is_compatible():
return SchemaCompatibilityResult.compatible()

incompatibilities = []
locations = set()
messages = set()
for record in result.result:
if not record.modification.is_compatible():
incompatibilities.append(record.modification.__str__())
locations.add(record.path)
messages.add(record.message)

return SchemaCompatibilityResult(
compatibility=SchemaCompatibilityType.incompatible,
incompatibilities=list(incompatibilities),
locations=set(locations),
messages=set(messages),
)
3 changes: 2 additions & 1 deletion karapace/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@
"session_timeout_ms": 10000,
"karapace_rest": False,
"karapace_registry": False,
"master_election_strategy": "lowest"
"master_election_strategy": "lowest",
"protobuf_runtime_directory": "runtime",
}
DEFAULT_LOG_FORMAT_JOURNAL = "%(name)-20s\t%(threadName)s\t%(levelname)-8s\t%(message)s"

Expand Down
8 changes: 4 additions & 4 deletions karapace/kafka_rest_apis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
RECORD_KEYS = ["key", "value", "partition"]
PUBLISH_KEYS = {"records", "value_schema", "value_schema_id", "key_schema", "key_schema_id"}
RECORD_CODES = [42201, 42202]
KNOWN_FORMATS = {"json", "avro", "binary"}
KNOWN_FORMATS = {"json", "avro", "protobuf", "binary"}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you didn't introduce this variable in your PR, but this is dead code, should be removed.

OFFSET_RESET_STRATEGIES = {"latest", "earliest"}
SCHEMA_MAPPINGS = {"avro": SchemaType.AVRO, "jsonschema": SchemaType.JSONSCHEMA}
SCHEMA_MAPPINGS = {"avro": SchemaType.AVRO, "jsonschema": SchemaType.JSONSCHEMA, "protobuf": SchemaType.PROTOBUF}
TypedConsumer = namedtuple("TypedConsumer", ["consumer", "serialization_format", "config"])


Expand Down Expand Up @@ -536,7 +536,7 @@ async def serialize(
return json.dumps(obj).encode("utf8")
if ser_format == "binary":
return base64.b64decode(obj)
if ser_format in {"avro", "jsonschema"}:
if ser_format in {"avro", "jsonschema", "protobuf"}:
return await self.schema_serialize(obj, schema_id)
raise FormatError(f"Unknown format: {ser_format}")

Expand Down Expand Up @@ -565,7 +565,7 @@ async def validate_publish_request_format(self, data: dict, formats: dict, conte
sub_code=RESTErrorCodes.HTTP_UNPROCESSABLE_ENTITY.value,
)
# disallow missing id and schema for any key/value list that has at least one populated element
if formats["embedded_format"] in {"avro", "jsonschema"}:
if formats["embedded_format"] in {"avro", "jsonschema", "protobuf"}:
for prefix, code in zip(RECORD_KEYS, RECORD_CODES):
if self.all_empty(data, prefix):
continue
Expand Down
4 changes: 2 additions & 2 deletions karapace/kafka_rest_apis/consumer_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import time
import uuid

KNOWN_FORMATS = {"json", "avro", "binary", "jsonschema"}
KNOWN_FORMATS = {"json", "avro", "binary", "jsonschema", "protobuf"}
OFFSET_RESET_STRATEGIES = {"latest", "earliest"}

TypedConsumer = namedtuple("TypedConsumer", ["consumer", "serialization_format", "config"])
Expand Down Expand Up @@ -481,7 +481,7 @@ async def fetch(self, internal_name: Tuple[str, str], content_type: str, formats
async def deserialize(self, bytes_: bytes, fmt: str):
if not bytes_:
return None
if fmt in {"avro", "jsonschema"}:
if fmt in {"avro", "jsonschema", "protobuf"}:
return await self.deserializer.deserialize(bytes_)
if fmt == "json":
return json.loads(bytes_.decode('utf-8'))
Expand Down
Empty file added karapace/protobuf/__init__.py
Empty file.
78 changes: 78 additions & 0 deletions karapace/protobuf/compare_result.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from dataclasses import dataclass, field
from enum import auto, Enum


class Modification(Enum):
PACKAGE_ALTER = auto()
SYNTAX_ALTER = auto()
MESSAGE_ADD = auto()
MESSAGE_DROP = auto()
MESSAGE_MOVE = auto()
ENUM_CONSTANT_ADD = auto()
ENUM_CONSTANT_ALTER = auto()
ENUM_CONSTANT_DROP = auto()
ENUM_ADD = auto()
ENUM_DROP = auto()
TYPE_ALTER = auto()
FIELD_ADD = auto()
FIELD_DROP = auto()
FIELD_MOVE = auto()
FIELD_LABEL_ALTER = auto()
FIELD_NAME_ALTER = auto()
FIELD_KIND_ALTER = auto()
FIELD_TYPE_ALTER = auto()
ONE_OF_ADD = auto()
ONE_OF_DROP = auto()
ONE_OF_MOVE = auto()
ONE_OF_FIELD_ADD = auto()
ONE_OF_FIELD_DROP = auto()
ONE_OF_FIELD_MOVE = auto()
FEW_FIELDS_CONVERTED_TO_ONE_OF = auto()

# protobuf compatibility issues is described in at
# https://yokota.blog/2021/08/26/understanding-protobuf-compatibility/
def is_compatible(self) -> bool:
return self not in [
self.MESSAGE_MOVE, self.MESSAGE_DROP, self.FIELD_LABEL_ALTER, self.FIELD_KIND_ALTER, self.FIELD_TYPE_ALTER,
self.ONE_OF_FIELD_DROP, self.FEW_FIELDS_CONVERTED_TO_ONE_OF
]


@dataclass
class ModificationRecord:
hackaugusto marked this conversation as resolved.
Show resolved Hide resolved
modification: Modification
path: str
message: str = field(init=False)

def __post_init__(self) -> None:
if self.modification.is_compatible():
self.message = f"Compatible modification {self.modification} found"
else:
self.message = f"Incompatible modification {self.modification} found"

def to_str(self) -> str:
return self.message


class CompareResult:
def __init__(self) -> None:
self.result = []
self.path = []
self.canonical_name = []

def push_path(self, name_element: str, canonical: bool = False) -> None:
if canonical:
self.canonical_name.append(name_element)
self.path.append(name_element)

def pop_path(self, canonical: bool = False) -> None:
if canonical:
self.canonical_name.pop()
self.path.pop()

def add_modification(self, modification: Modification) -> None:
record = ModificationRecord(modification, ".".join(self.path))
self.result.append(record)

def is_compatible(self) -> bool:
return all(record.modification.is_compatible() for record in self.result)
134 changes: 134 additions & 0 deletions karapace/protobuf/compare_type_storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
from dataclasses import dataclass
from karapace.protobuf.compare_result import CompareResult
from karapace.protobuf.exception import IllegalArgumentException
from karapace.protobuf.proto_type import ProtoType
from karapace.protobuf.type_element import TypeElement
from typing import Dict, List, Optional, TYPE_CHECKING, Union

if TYPE_CHECKING:
from karapace.protobuf.message_element import MessageElement
from karapace.protobuf.field_element import FieldElement


def compute_name(t: ProtoType, result_path: List[str], package_name: str, types: dict) -> Optional[str]:
string = t.string

if string.startswith("."):
name = string[1:]
if types.get(name):
return name
return None
canonical_name = list(result_path)
if package_name:
canonical_name.insert(0, package_name)
while len(canonical_name) > 0:
pretender: str = ".".join(canonical_name) + "." + string
pt = types.get(pretender)
if pt is not None:
return pretender
canonical_name.pop()
if types.get(string):
return string
return None


class CompareTypes:
def __init__(self, self_package_name: str, other_package_name: str, result: CompareResult) -> None:

self.self_package_name = self_package_name
self.other_package_name = other_package_name
self.self_types: Dict[str, Union[TypeRecord, TypeRecordMap]] = {}
self.other_types: Dict[str, Union[TypeRecord, TypeRecordMap]] = {}
self.locked_messages: List['MessageElement'] = []
self.environment: List['MessageElement'] = []
self.result = result

def add_a_type(self, prefix: str, package_name: str, type_element: TypeElement, types: dict) -> None:
name: str
if prefix:
name = prefix + "." + type_element.name
else:
name = type_element.name
from karapace.protobuf.message_element import MessageElement
from karapace.protobuf.field_element import FieldElement

if isinstance(type_element, MessageElement): # add support of MapEntry messages
if "map_entry" in type_element.options:
key: Optional[FieldElement] = next((f for f in type_element.fields if f.name == "key"), None)
value: Optional[FieldElement] = next((f for f in type_element.fields if f.name == "value"), None)
types[name] = TypeRecordMap(package_name, type_element, key, value)
else:
types[name] = TypeRecord(package_name, type_element)
else:
types[name] = TypeRecord(package_name, type_element)

for t in type_element.nested_types:
self.add_a_type(name, package_name, t, types)

def add_self_type(self, package_name: str, type_element: TypeElement) -> None:
self.add_a_type(package_name, package_name, type_element, self.self_types)

def add_other_type(self, package_name: str, type_element: TypeElement) -> None:
self.add_a_type(package_name, package_name, type_element, self.other_types)

def get_self_type(self, t: ProtoType) -> Union[None, 'TypeRecord', 'TypeRecordMap']:
name = compute_name(t, self.result.path, self.self_package_name, self.self_types)
if name is not None:
type_record = self.self_types.get(name)
return type_record
return None

def get_other_type(self, t: ProtoType) -> Union[None, 'TypeRecord', 'TypeRecordMap']:
name = compute_name(t, self.result.path, self.other_package_name, self.other_types)
if name is not None:
type_record = self.other_types.get(name)
return type_record
return None

def self_type_short_name(self, t: ProtoType) -> Optional[str]:
name = compute_name(t, self.result.path, self.self_package_name, self.self_types)
if name is None:
raise IllegalArgumentException(f"Cannot determine message type {t}")
type_record: TypeRecord = self.self_types.get(name)
if name.startswith(type_record.package_name):
return name[(len(type_record.package_name) + 1):]
return name

def other_type_short_name(self, t: ProtoType) -> Optional[str]:
name = compute_name(t, self.result.path, self.other_package_name, self.other_types)
if name is None:
raise IllegalArgumentException(f"Cannot determine message type {t}")
type_record: TypeRecord = self.other_types.get(name)
if name.startswith(type_record.package_name):
return name[(len(type_record.package_name) + 1):]
return name

def lock_message(self, message: 'MessageElement') -> bool:
if message in self.locked_messages:
return False
self.locked_messages.append(message)
return True

def unlock_message(self, message: 'MessageElement') -> bool:
if message in self.locked_messages:
self.locked_messages.remove(message)
return True
return False


@dataclass
class TypeRecord:
package_name: str
type_element: TypeElement


class TypeRecordMap(TypeRecord):
def __init__(
self, package_name: str, type_element: TypeElement, key: Optional['FieldElement'], value: Optional['FieldElement']
) -> None:
super().__init__(package_name, type_element)
self.key = key
self.value = value

def map_type(self) -> ProtoType:
return ProtoType.get2(f"map<{self.key.element_type}, {self.value.element_type}>")
Loading