1
1
use std:: {
2
+ fs,
2
3
path:: { Path , PathBuf } ,
3
4
sync:: Arc ,
4
5
} ;
5
6
6
- use anyhow:: anyhow ;
7
+ use anyhow:: Context ;
7
8
use async_trait:: async_trait;
8
- use mithril_common:: { entities:: DigestLocation , logging:: LoggerExtensions , StdResult } ;
9
+ use mithril_common:: {
10
+ entities:: DigestLocation , logging:: LoggerExtensions ,
11
+ messages:: CardanoDatabaseDigestListItemMessage , StdResult ,
12
+ } ;
9
13
use reqwest:: Url ;
10
- use slog:: { debug , error, Logger } ;
14
+ use slog:: { error, Logger } ;
11
15
12
- use crate :: { file_uploaders :: url_sanitizer :: sanitize_url_path , snapshotter :: OngoingSnapshot } ;
16
+ use crate :: ImmutableFileDigestMapper ;
13
17
14
18
/// The [DigestFileUploader] trait allows identifying uploaders that return locations for digest archive files.
15
19
#[ cfg_attr( test, mockall:: automock) ]
@@ -24,6 +28,9 @@ pub struct DigestArtifactBuilder {
24
28
aggregator_url_prefix : Url ,
25
29
/// Uploaders
26
30
uploaders : Vec < Arc < dyn DigestFileUploader > > ,
31
+
32
+ immutable_file_digest_mapper : Arc < dyn ImmutableFileDigestMapper > ,
33
+
27
34
logger : Logger ,
28
35
}
29
36
@@ -32,27 +39,60 @@ impl DigestArtifactBuilder {
32
39
pub fn new (
33
40
aggregator_url_prefix : Url ,
34
41
uploaders : Vec < Arc < dyn DigestFileUploader > > ,
42
+ immutable_file_digest_mapper : Arc < dyn ImmutableFileDigestMapper > ,
35
43
logger : Logger ,
36
44
) -> StdResult < Self > {
37
45
Ok ( Self {
38
46
aggregator_url_prefix,
39
47
uploaders,
48
+ immutable_file_digest_mapper,
40
49
logger : logger. new_with_component_name :: < Self > ( ) ,
41
50
} )
42
51
}
43
52
44
53
pub async fn upload ( & self ) -> StdResult < Vec < DigestLocation > > {
45
- // let snapshot = self.create_digest_archive(beacon)?;
46
- // let digest_path = snapshot.get_file_path();
47
- let digest_path = Path :: new ( "" ) ;
54
+ let digest_path = self . create_digest_archive ( ) . await ?;
48
55
49
- self . upload_digest_archive ( digest_path) . await
56
+ let locations = self . upload_digest_archive ( & digest_path) . await ;
57
+ fs:: remove_file ( & digest_path) . with_context ( || {
58
+ format ! (
59
+ "Could not remove digest archive file: '{}'" ,
60
+ digest_path. display( )
61
+ )
62
+ } ) ?;
63
+ locations
50
64
}
51
65
52
- async fn create_digest_archive ( ) -> StdResult < OngoingSnapshot > {
53
- // get message service::get_cardano_database_digest_list_message
54
- // output json (created from message) to file
55
- todo ! ( )
66
+ async fn create_digest_archive ( & self ) -> StdResult < PathBuf > {
67
+ let immutable_file_digest_map = self
68
+ . immutable_file_digest_mapper
69
+ . get_immutable_file_digest_map ( )
70
+ . await ?
71
+ . into_iter ( )
72
+ . map (
73
+ |( immutable_file_name, digest) | CardanoDatabaseDigestListItemMessage {
74
+ immutable_file_name,
75
+ digest,
76
+ } ,
77
+ )
78
+ . collect :: < Vec < _ > > ( ) ;
79
+
80
+ // TODO : change that injecting the path or using snapshotter
81
+ let digests_file_path = Path :: new ( "/tmp" ) . join ( "mithril" ) . join ( "digests.json" ) ;
82
+
83
+ if let Some ( digests_dir) = digests_file_path. parent ( ) {
84
+ fs:: create_dir_all ( digests_dir) . with_context ( || {
85
+ format ! (
86
+ "Can not create digests directory: '{}'" ,
87
+ digests_dir. display( )
88
+ )
89
+ } ) ?;
90
+ }
91
+
92
+ let digest_file = fs:: File :: create ( digests_file_path. clone ( ) ) . unwrap ( ) ;
93
+ serde_json:: to_writer ( digest_file, & immutable_file_digest_map) ?;
94
+
95
+ Ok ( digests_file_path)
56
96
}
57
97
58
98
/// Uploads the digest archive and returns the locations of the uploaded files.
@@ -94,8 +134,16 @@ impl DigestArtifactBuilder {
94
134
95
135
#[ cfg( test) ]
96
136
mod tests {
97
- use crate :: test_tools:: TestLogger ;
98
- use mithril_common:: test_utils:: { assert_equivalent, TempDir } ;
137
+ use std:: { collections:: BTreeMap , fs:: read_to_string} ;
138
+
139
+ use crate :: {
140
+ immutable_file_digest_mapper:: MockImmutableFileDigestMapper , test_tools:: TestLogger ,
141
+ } ;
142
+ use anyhow:: anyhow;
143
+ use mithril_common:: {
144
+ messages:: { CardanoDatabaseDigestListItemMessage , CardanoDatabaseDigestListMessage } ,
145
+ test_utils:: { assert_equivalent, TempDir } ,
146
+ } ;
99
147
100
148
use super :: * ;
101
149
@@ -121,9 +169,15 @@ mod tests {
121
169
122
170
#[ tokio:: test]
123
171
async fn digest_artifact_builder_return_digests_route_on_aggregator ( ) {
172
+ let mut immutable_file_digest_mapper = MockImmutableFileDigestMapper :: new ( ) ;
173
+ immutable_file_digest_mapper
174
+ . expect_get_immutable_file_digest_map ( )
175
+ . returning ( || Ok ( BTreeMap :: new ( ) ) ) ;
176
+
124
177
let builder = DigestArtifactBuilder :: new (
125
178
Url :: parse ( "https://aggregator/" ) . unwrap ( ) ,
126
179
vec ! [ ] ,
180
+ Arc :: new ( immutable_file_digest_mapper) ,
127
181
TestLogger :: stdout ( ) ,
128
182
)
129
183
. unwrap ( ) ;
@@ -151,11 +205,14 @@ mod tests {
151
205
let builder = DigestArtifactBuilder :: new (
152
206
Url :: parse ( "https://aggregator/" ) . unwrap ( ) ,
153
207
vec ! [ Arc :: new( uploader) ] ,
208
+ Arc :: new ( MockImmutableFileDigestMapper :: new ( ) ) ,
154
209
TestLogger :: file ( & log_path) ,
155
210
)
156
211
. unwrap ( ) ;
157
212
158
- let _ = builder. upload_digest_archive ( & Path :: new ( "" ) ) . await ;
213
+ let _ = builder
214
+ . upload_digest_archive ( Path :: new ( "digest_file" ) )
215
+ . await ;
159
216
}
160
217
161
218
let logs = std:: fs:: read_to_string ( & log_path) . unwrap ( ) ;
@@ -169,11 +226,15 @@ mod tests {
169
226
let builder = DigestArtifactBuilder :: new (
170
227
Url :: parse ( "https://aggregator/" ) . unwrap ( ) ,
171
228
vec ! [ Arc :: new( uploader) ] ,
229
+ Arc :: new ( MockImmutableFileDigestMapper :: new ( ) ) ,
172
230
TestLogger :: stdout ( ) ,
173
231
)
174
232
. unwrap ( ) ;
175
233
176
- let locations = builder. upload_digest_archive ( & Path :: new ( "" ) ) . await . unwrap ( ) ;
234
+ let locations = builder
235
+ . upload_digest_archive ( Path :: new ( "digest_file" ) )
236
+ . await
237
+ . unwrap ( ) ;
177
238
178
239
assert ! ( !locations. is_empty( ) ) ;
179
240
}
@@ -193,11 +254,15 @@ mod tests {
193
254
let builder = DigestArtifactBuilder :: new (
194
255
Url :: parse ( "https://aggregator/" ) . unwrap ( ) ,
195
256
uploaders,
257
+ Arc :: new ( MockImmutableFileDigestMapper :: new ( ) ) ,
196
258
TestLogger :: stdout ( ) ,
197
259
)
198
260
. unwrap ( ) ;
199
261
200
- let locations = builder. upload_digest_archive ( & Path :: new ( "" ) ) . await . unwrap ( ) ;
262
+ let locations = builder
263
+ . upload_digest_archive ( Path :: new ( "digest_file" ) )
264
+ . await
265
+ . unwrap ( ) ;
201
266
202
267
assert_equivalent (
203
268
locations,
@@ -223,11 +288,15 @@ mod tests {
223
288
let builder = DigestArtifactBuilder :: new (
224
289
Url :: parse ( "https://aggregator/" ) . unwrap ( ) ,
225
290
uploaders,
291
+ Arc :: new ( MockImmutableFileDigestMapper :: new ( ) ) ,
226
292
TestLogger :: stdout ( ) ,
227
293
)
228
294
. unwrap ( ) ;
229
295
230
- let locations = builder. upload_digest_archive ( & Path :: new ( "" ) ) . await . unwrap ( ) ;
296
+ let locations = builder
297
+ . upload_digest_archive ( Path :: new ( "digest_file" ) )
298
+ . await
299
+ . unwrap ( ) ;
231
300
232
301
assert_equivalent (
233
302
locations,
@@ -244,4 +313,71 @@ mod tests {
244
313
] ,
245
314
) ;
246
315
}
316
+
317
+ #[ tokio:: test]
318
+ async fn create_digest_archive_should_create_json_file_with_all_digests ( ) {
319
+ let mut immutable_file_digest_mapper = MockImmutableFileDigestMapper :: new ( ) ;
320
+ immutable_file_digest_mapper
321
+ . expect_get_immutable_file_digest_map ( )
322
+ . returning ( || {
323
+ Ok ( BTreeMap :: from ( [ (
324
+ "06685.chunk" . to_string ( ) ,
325
+ "0af556ab2620dd9363bf76963a231abe8948a500ea6be31b131d87907ab09b1e" . to_string ( ) ,
326
+ ) ] ) )
327
+ } ) ;
328
+
329
+ let builder = DigestArtifactBuilder :: new (
330
+ Url :: parse ( "https://aggregator/" ) . unwrap ( ) ,
331
+ vec ! [ ] ,
332
+ Arc :: new ( immutable_file_digest_mapper) ,
333
+ TestLogger :: stdout ( ) ,
334
+ )
335
+ . unwrap ( ) ;
336
+
337
+ let archive_path = builder. create_digest_archive ( ) . await . unwrap ( ) ;
338
+ let file_content = read_to_string ( archive_path) . unwrap ( ) ;
339
+ let digest_content: CardanoDatabaseDigestListMessage =
340
+ serde_json:: from_str ( & file_content) . unwrap ( ) ;
341
+
342
+ assert_eq ! (
343
+ digest_content,
344
+ vec![ CardanoDatabaseDigestListItemMessage {
345
+ immutable_file_name: "06685.chunk" . to_string( ) ,
346
+ digest: "0af556ab2620dd9363bf76963a231abe8948a500ea6be31b131d87907ab09b1e"
347
+ . to_string( ) ,
348
+ } ]
349
+ ) ;
350
+ }
351
+
352
+ #[ tokio:: test]
353
+ async fn upload_should_call_upload_with_created_digest_file_and_delete_the_file ( ) {
354
+ // TODO : This test is flaky because we create and remove a file with an hard coded path
355
+ let mut immutable_file_digest_mapper = MockImmutableFileDigestMapper :: new ( ) ;
356
+ immutable_file_digest_mapper
357
+ . expect_get_immutable_file_digest_map ( )
358
+ . returning ( || Ok ( BTreeMap :: new ( ) ) ) ;
359
+
360
+ let mut digest_file_uploader = MockDigestFileUploader :: new ( ) ;
361
+ digest_file_uploader
362
+ . expect_upload ( )
363
+ . withf ( |path| path == Path :: new ( "/tmp/mithril/digests.json" ) && path. exists ( ) )
364
+ . times ( 1 )
365
+ . return_once ( |_| {
366
+ Ok ( DigestLocation :: CloudStorage {
367
+ uri : "an_uri" . to_string ( ) ,
368
+ } )
369
+ } ) ;
370
+
371
+ let builder = DigestArtifactBuilder :: new (
372
+ Url :: parse ( "https://aggregator/" ) . unwrap ( ) ,
373
+ vec ! [ Arc :: new( digest_file_uploader) ] ,
374
+ Arc :: new ( immutable_file_digest_mapper) ,
375
+ TestLogger :: stdout ( ) ,
376
+ )
377
+ . unwrap ( ) ;
378
+
379
+ let _locations = builder. upload ( ) . await . unwrap ( ) ;
380
+
381
+ assert ! ( !Path :: new( "/tmp/mithril/digests.json" ) . exists( ) ) ;
382
+ }
247
383
}
0 commit comments