1313# limitations under the License.
1414
1515import jsonschema
16+ import os
17+ import random
18+ import string
19+
20+ import pdo .common .crypto as crypto
21+ from pdo .common .key_value import KeyValueStore
22+
23+ import logging
24+ logger = logging .getLogger (__name__ )
1625
1726# -----------------------------------------------------------------
1827def ValidateJSON (instance , schema ):
@@ -21,3 +30,71 @@ def ValidateJSON(instance, schema):
2130 except jsonschema .exceptions .ValidationError as err :
2231 return False
2332 return True
33+
34+ # -----------------------------------------------------------------
35+ # Size of chunks to store per key; this is the maximum size of a
36+ # single key in the KeyValueStore
37+ __CHUNK_SIZE__ = 1024
38+
39+ # -----------------------------------------------------------------
40+ def send_file (file_name , block_store = None , ** kwargs ):
41+ """
42+ Store the contents of a file in the KeyValueStore under a specified key. Sync any updated
43+ blocks to the block store if specified. Returns a dictionary containing information that
44+ can be used to receive the file from the KeyValueStore later.
45+
46+ :param file_name: Name of the file to be stored.
47+ :param block_store: Optional parameter of type pdo.service_client.storage.StorageServiceClient
48+
49+ :return: A dictionary containing the base key, number of chunks, encryption key, and state hash.
50+ """
51+
52+ key = '' .join (random .choice (string .ascii_letters ) for _ in range (16 ))
53+
54+ kv = KeyValueStore ()
55+
56+ chunks = int (os .path .getsize (file_name ) / __CHUNK_SIZE__ ) + 1
57+ with open (file_name , 'rb' ) as fp :
58+ for chunk_number in range (0 , chunks ) :
59+ chunk = fp .read (__CHUNK_SIZE__ )
60+ with kv : _ = kv .set (f'{ key } _{ chunk_number } ' , chunk , input_encoding = 'str' , output_encoding = 'raw' )
61+
62+ if block_store :
63+ n = kv .sync_to_block_store (block_store , ** kwargs )
64+ if n != chunk_number :
65+ raise ValueError (f'incorrect blocks writen; { n } != { chunk_number } ' )
66+
67+ file_information = dict ()
68+ file_information ['key_base' ] = key
69+ file_information ['chunks' ] = chunks
70+ file_information ['encryption_key' ] = kv .encryption_key
71+ file_information ['state_hash' ] = kv .hash_identity
72+
73+ return file_information
74+
75+ # -----------------------------------------------------------------
76+ def recv_file (file_information , file_name , block_store = None , ** kwargs ) :
77+ """
78+ Receive the contents of a file in the KeyValueStore under a specified key. Sync any updated
79+ blocks from the block store if specified. Takes a dictionary containing the file information
80+ as generated by `send_file`.
81+
82+ :param file_information: Dictionary containing the base key, number of chunks, encryption key, and state hash.
83+ :param file_name: Name of the file to be received.
84+ :param block_store: Optional parameter of type pdo.service_client.storage.StorageServiceClient
85+ """
86+ key_base = file_information ['key_base' ]
87+ chunks = file_information ['chunks' ]
88+ encryption_key = file_information ['encryption_key' ]
89+ state_hash = file_information ['state_hash' ]
90+
91+ kv = KeyValueStore (encryption_key , state_hash )
92+ if block_store :
93+ _ = kv .sync_from_block_store (state_hash , block_store , ** kwargs )
94+
95+ with open (file_name , 'wb' ) as fp :
96+ for chunk_number in range (chunks ) :
97+ with kv : chunk = kv .get (f'{ key_base } _{ chunk_number } ' , input_encoding = 'str' , output_encoding = 'raw' )
98+ fp .write (bytes (chunk ))
99+
100+ return True
0 commit comments