Coverage for /builds/BuildGrid/buildgrid/buildgrid/server/utils/digests.py: 95.65%

23 statements  

« prev     ^ index     » next       coverage.py v7.4.1, created at 2025-02-11 15:07 +0000

1# Copyright (C) 2024 Bloomberg LP 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# <http://www.apache.org/licenses/LICENSE-2.0> 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14 

15 

16from dataclasses import dataclass 

17 

18from buildgrid._protos.build.bazel.remote.execution.v2.remote_execution_pb2 import Digest, DigestFunction 

19from buildgrid.server.settings import HASH, HASH_LENGTH 

20 

21 

22@dataclass(frozen=True) 

23class HashableDigest: 

24 hash: str 

25 size_bytes: int 

26 

27 def to_digest(self) -> Digest: 

28 return Digest(hash=self.hash, size_bytes=self.size_bytes) 

29 

30 

31def get_hash_type() -> "DigestFunction.Value.ValueType": 

32 """Returns the hash type.""" 

33 hash_name = HASH().name 

34 if hash_name == "sha256": 

35 return DigestFunction.SHA256 

36 return DigestFunction.UNKNOWN 

37 

38 

39def create_digest(bytes_to_digest: bytes) -> Digest: 

40 """Computes the :obj:`Digest` of a piece of data. 

41 

42 The :obj:`Digest` of a data is a function of its hash **and** size. 

43 

44 Args: 

45 bytes_to_digest (bytes): byte data to digest. 

46 

47 Returns: 

48 :obj:`Digest`: The :obj:`Digest` for the given byte data. 

49 """ 

50 return Digest(hash=HASH(bytes_to_digest).hexdigest(), size_bytes=len(bytes_to_digest)) 

51 

52 

53def parse_digest(digest_string: str) -> Digest | None: 

54 """Creates a :obj:`Digest` from a digest string. 

55 

56 A digest string should alway be: ``{hash}/{size_bytes}``. 

57 

58 Args: 

59 digest_string (str): the digest string. 

60 

61 Returns: 

62 :obj:`Digest`: The :obj:`Digest` read from the string or None if 

63 `digest_string` is not a valid digest string. 

64 """ 

65 digest_hash, digest_size = digest_string.split("/") 

66 

67 if len(digest_hash) == HASH_LENGTH and digest_size.isdigit(): 

68 return Digest(hash=digest_hash, size_bytes=int(digest_size)) 

69 

70 return None 

71 

72 

73def validate_digest_data(digest: Digest, data: bytes) -> bool: 

74 """Validate that the given digest corresponds to the given data.""" 

75 return len(data) == digest.size_bytes and HASH(data).hexdigest() == digest.hash