23
23
import java .nio .file .AccessDeniedException ;
24
24
import java .util .ArrayList ;
25
25
import java .util .Arrays ;
26
+ import java .util .Hashtable ;
26
27
import java .util .List ;
27
28
import java .util .UUID ;
28
29
29
30
import org .assertj .core .api .Assertions ;
30
31
import org .junit .Assume ;
31
32
import org .junit .Test ;
33
+ import org .mockito .Mockito ;
32
34
import org .slf4j .Logger ;
33
35
import org .slf4j .LoggerFactory ;
34
36
40
42
import org .apache .hadoop .fs .Path ;
41
43
import org .apache .hadoop .fs .azurebfs .constants .AbfsHttpConstants ;
42
44
import org .apache .hadoop .fs .azurebfs .constants .TestConfigurationKeys ;
45
+ import org .apache .hadoop .fs .azurebfs .contracts .services .ListResultEntrySchema ;
43
46
import org .apache .hadoop .fs .azurebfs .extensions .MockDelegationSASTokenProvider ;
47
+ import org .apache .hadoop .fs .azurebfs .services .AbfsBlobClient ;
48
+ import org .apache .hadoop .fs .azurebfs .services .AbfsClient ;
49
+ import org .apache .hadoop .fs .azurebfs .services .AbfsHttpOperation ;
44
50
import org .apache .hadoop .fs .azurebfs .services .AbfsRestOperation ;
45
51
import org .apache .hadoop .fs .azurebfs .services .AuthType ;
46
- import org .apache .hadoop .fs .azurebfs .services . AbfsHttpOperation ;
52
+ import org .apache .hadoop .fs .azurebfs .utils . TracingContext ;
47
53
import org .apache .hadoop .fs .permission .AclEntry ;
48
54
import org .apache .hadoop .fs .permission .AclEntryScope ;
49
55
import org .apache .hadoop .fs .permission .AclStatus ;
@@ -417,16 +423,75 @@ public void testProperties() throws Exception {
417
423
}
418
424
419
425
@ Test
420
- public void testSignatureMask () throws Exception {
426
+ // FileSystemProperties are not supported by delegation SAS and should throw exception
427
+ public void testSetFileSystemProperties () throws Exception {
421
428
final AzureBlobFileSystem fs = getFileSystem ();
422
- String src = String .format ("/testABC/test%s.xt" , UUID .randomUUID ());
423
- fs .create (new Path (src )).close ();
424
- AbfsRestOperation abfsHttpRestOperation = fs .getAbfsClient ()
425
- .renamePath (src , "/testABC" + "/abc.txt" , null ,
426
- getTestTracingContext (fs , false ), null ,
427
- false )
428
- .getOp ();
429
- AbfsHttpOperation result = abfsHttpRestOperation .getResult ();
429
+ final Hashtable <String , String >
430
+ properties = new Hashtable <>();
431
+ properties .put ("FileSystemProperties" , "true" );
432
+ TracingContext tracingContext = getTestTracingContext (fs , true );
433
+ assertThrows (IOException .class , ()-> fs .getAbfsStore ().setFilesystemProperties (properties , tracingContext ));
434
+ assertThrows (IOException .class , ()-> fs .getAbfsStore ().getFilesystemProperties (tracingContext ));
435
+ }
436
+
437
+ @ Test
438
+ //Test list and delete operation on implicit paths
439
+ public void testListAndDeleteImplicitPaths () throws Exception {
440
+ AzureBlobFileSystem fs = getFileSystem ();
441
+ AbfsBlobClient client = ((AbfsBlobClient ) getFileSystem ().getAbfsClient ());
442
+ assumeBlobServiceType ();
443
+
444
+ Path file1 = new Path ("/testDir/dir1/file1" );
445
+ Path file2 = new Path ("/testDir/dir1/file2" );
446
+ Path implicitDir = file1 .getParent ();
447
+
448
+ createAzCopyFolder (implicitDir );
449
+ createAzCopyFile (file1 );
450
+ createAzCopyFile (file2 );
451
+
452
+ AbfsRestOperation op = client .listPath (
453
+ implicitDir .toString (), false , 2 , null ,
454
+ getTestTracingContext (getFileSystem (), true ));
455
+ List <? extends ListResultEntrySchema > list = op .getResult ()
456
+ .getListResultSchema ()
457
+ .paths ();
458
+ Assertions .assertThat (list ).hasSize (2 );
459
+
460
+ client .deletePath (implicitDir .toString (), true , "" ,
461
+ getTestTracingContext (fs , false ));
462
+
463
+ Assertions .assertThat (fs .exists (file1 ))
464
+ .describedAs ("Deleted file1 should not exist." ).isFalse ();
465
+ Assertions .assertThat (fs .exists (file2 ))
466
+ .describedAs ("Deleted file2 should not exist." ).isFalse ();
467
+ Assertions .assertThat (fs .exists (implicitDir ))
468
+ .describedAs ("The parent dir should not exist." )
469
+ .isFalse ();
470
+ }
471
+
472
+
473
+ /**
474
+ * Spies on the AzureBlobFileSystem's store and client to enable mocking and verification
475
+ * of client interactions in tests. It replaces the actual store and client with mocked versions.
476
+ *
477
+ * @param fs the AzureBlobFileSystem instance
478
+ * @return the spied AbfsClient for interaction verification
479
+ */
480
+ private AbfsClient addSpyHooksOnClient (final AzureBlobFileSystem fs ) {
481
+ AzureBlobFileSystemStore store = Mockito .spy (fs .getAbfsStore ());
482
+ Mockito .doReturn (store ).when (fs ).getAbfsStore ();
483
+ AbfsClient client = Mockito .spy (store .getClient ());
484
+ Mockito .doReturn (client ).when (store ).getClient ();
485
+ return client ;
486
+ }
487
+
488
+ /**
489
+ * Asserts the signature masking in the URL and encoded URL of the AbfsRestOperation.
490
+ *
491
+ * @param op the AbfsRestOperation
492
+ */
493
+ private void checkSignatureMaskAssertions (AbfsRestOperation op ){
494
+ AbfsHttpOperation result = op .getResult ();
430
495
String url = result .getMaskedUrl ();
431
496
String encodedUrl = result .getMaskedEncodedUrl ();
432
497
Assertions .assertThat (url .substring (url .indexOf ("sig=" )))
@@ -437,6 +502,67 @@ public void testSignatureMask() throws Exception {
437
502
.startsWith ("sig%3DXXXXX" );
438
503
}
439
504
505
+ @ Test
506
+ // Test masking of signature for rename operation for Blob
507
+ public void testSignatureMaskforBlob () throws Exception {
508
+ assumeBlobServiceType ();
509
+ final AzureBlobFileSystem fs = Mockito .spy (this .getFileSystem ());
510
+ AbfsBlobClient client = (AbfsBlobClient ) addSpyHooksOnClient (fs );
511
+
512
+ fs .getAbfsStore ().setClient (client );
513
+ String src = String .format ("/testABC/test%s.xt" , UUID .randomUUID ());
514
+ String dest = "/testABC" + "/abc.txt" ;
515
+ fs .create (new Path (src )).close ();
516
+
517
+ Mockito .doAnswer (answer -> {
518
+ Path srcCopy = answer .getArgument (0 );
519
+ Path dstCopy = answer .getArgument (1 );
520
+ String leaseId = answer .getArgument (2 );
521
+ TracingContext tracingContext = answer .getArgument (3 );
522
+ AbfsRestOperation op
523
+ = ((AbfsBlobClient ) getFileSystem ().getAbfsClient ()).copyBlob (srcCopy ,
524
+ dstCopy , leaseId , tracingContext );
525
+ checkSignatureMaskAssertions (op );
526
+ return answer .callRealMethod ();
527
+ })
528
+ .when (client )
529
+ .copyBlob (Mockito .any (Path .class ), Mockito .any (Path .class ),
530
+ Mockito .any (String .class ), Mockito .any (TracingContext .class ));
531
+
532
+ Mockito .doAnswer (answer -> {
533
+ Path blobPath = answer .getArgument (0 );
534
+ String leaseId = answer .getArgument (1 );
535
+ TracingContext tracingContext = answer .getArgument (2 );
536
+ AbfsRestOperation op
537
+ = ((AbfsBlobClient ) getFileSystem ().getAbfsClient ()).deleteBlobPath (blobPath ,
538
+ leaseId , tracingContext );
539
+ checkSignatureMaskAssertions (op );
540
+ return answer .callRealMethod ();
541
+ })
542
+ .when (client )
543
+ .deleteBlobPath (Mockito .any (Path .class ), Mockito .any (String .class ),
544
+ Mockito .any (TracingContext .class ));
545
+
546
+ client .renamePath (src , dest , null ,
547
+ getTestTracingContext (fs , false ), null ,
548
+ false );
549
+ }
550
+
551
+ // Test masking of signature for rename operation for DFS
552
+ @ Test
553
+ public void testSignatureMask () throws Exception {
554
+ assumeDfsServiceType ();
555
+ final AzureBlobFileSystem fs = getFileSystem ();
556
+ String src = String .format ("/testABC/test%s.xt" , UUID .randomUUID ());
557
+ fs .create (new Path (src )).close ();
558
+ AbfsRestOperation abfsHttpRestOperation = fs .getAbfsClient ()
559
+ .renamePath (src , "/testABC" + "/abc.txt" , null ,
560
+ getTestTracingContext (fs , false ), null ,
561
+ false )
562
+ .getOp ();
563
+ checkSignatureMaskAssertions (abfsHttpRestOperation );
564
+ }
565
+
440
566
@ Test
441
567
public void testSignatureMaskOnExceptionMessage () throws Exception {
442
568
intercept (IOException .class , "sig=XXXX" ,
0 commit comments