43
43
from swift .common .exceptions import ChunkWriteTimeout , ShortReadError , \
44
44
ChunkReadTimeout , RangeAlreadyComplete
45
45
from swift .common .utils import Timestamp , list_from_csv , md5 , FileLikeIter , \
46
- ShardRange , Namespace , NamespaceBoundList
46
+ ShardRange , Namespace , NamespaceBoundList , quorum_size
47
47
from swift .proxy import server as proxy_server
48
48
from swift .proxy .controllers import obj
49
49
from swift .proxy .controllers .base import \
@@ -524,6 +524,30 @@ def test_repl_object_DELETE_backend_update_container_repl_ip(self):
524
524
for n in container_nodes }
525
525
self .assertEqual (container_hosts , expected_container_hosts )
526
526
527
+ def test_DELETE_all_found (self ):
528
+ req = swift .common .swob .Request .blank ('/v1/a/c/o' , method = 'DELETE' )
529
+ codes = [204 ] * self .replicas ()
530
+ headers = []
531
+ ts = self .ts ()
532
+ for _ in codes :
533
+ headers .append ({'x-backend-timestamp' : ts .internal })
534
+ with mocked_http_conn (* codes , headers = headers ):
535
+ resp = req .get_response (self .app )
536
+ self .assertEqual (resp .status_int , 204 )
537
+ self .assertEqual (ts .internal , resp .headers .get ('X-Backend-Timestamp' ))
538
+
539
+ def test_DELETE_none_found (self ):
540
+ req = swift .common .swob .Request .blank ('/v1/a/c/o' , method = 'DELETE' )
541
+ codes = [404 ] * self .replicas ()
542
+ headers = []
543
+ ts = self .ts ()
544
+ for _ in codes :
545
+ headers .append ({'x-backend-timestamp' : ts .internal })
546
+ with mocked_http_conn (* codes , headers = headers ):
547
+ resp = req .get_response (self .app )
548
+ self .assertEqual (resp .status_int , 404 )
549
+ self .assertEqual (ts .internal , resp .headers .get ('X-Backend-Timestamp' ))
550
+
527
551
def test_DELETE_missing_one (self ):
528
552
# Obviously this test doesn't work if we're testing 1 replica.
529
553
# In that case, we don't have any failovers to check.
@@ -536,7 +560,7 @@ def test_DELETE_missing_one(self):
536
560
resp = req .get_response (self .app )
537
561
self .assertEqual (resp .status_int , 204 )
538
562
539
- def test_DELETE_not_found (self ):
563
+ def test_DELETE_one_found (self ):
540
564
# Obviously this test doesn't work if we're testing 1 replica.
541
565
# In that case, we don't have any failovers to check.
542
566
if self .replicas () == 1 :
@@ -565,6 +589,94 @@ def test_DELETE_mostly_not_found(self):
565
589
resp = req .get_response (self .app )
566
590
self .assertEqual (resp .status_int , 404 )
567
591
592
+ def test_DELETE_insufficient_found_plus_404_507 (self ):
593
+ # one less 204 than a quorum...
594
+ primary_success = quorum_size (self .replicas ()) - 1
595
+ primary_failure = self .replicas () - primary_success - 1
596
+ primary_codes = [204 ] * primary_success + [404 ] + \
597
+ [507 ] * primary_failure
598
+ handoff_codes = [404 ] * primary_failure
599
+ ts = self .ts ()
600
+ headers = []
601
+ for status in primary_codes + handoff_codes :
602
+ if status in (204 , 404 ):
603
+ headers .append ({'x-backend-timestamp' : ts .internal })
604
+ else :
605
+ headers .append ({})
606
+ req = swift .common .swob .Request .blank ('/v1/a/c/o' , method = 'DELETE' )
607
+ with mocked_http_conn (* (primary_codes + handoff_codes ),
608
+ headers = headers ):
609
+ resp = req .get_response (self .app )
610
+ # primary and handoff 404s form a quorum...
611
+ self .assertEqual (resp .status_int , 404 ,
612
+ 'replicas = %s' % self .replicas ())
613
+ self .assertEqual (ts .internal , resp .headers .get ('X-Backend-Timestamp' ))
614
+
615
+ def test_DELETE_insufficient_found_plus_timeouts (self ):
616
+ req = swift .common .swob .Request .blank ('/v1/a/c/o' )
617
+ req .method = 'DELETE'
618
+ primary_success = quorum_size (self .replicas ()) - 1
619
+ primary_failure = self .replicas () - primary_success
620
+ primary_codes = [204 ] * primary_success + [Timeout ()] * primary_failure
621
+ handoff_codes = [404 ] * primary_failure
622
+ ts = self .ts ()
623
+ headers = []
624
+ for status in primary_codes + handoff_codes :
625
+ if status in (204 , 404 ):
626
+ headers .append ({'x-backend-timestamp' : ts .internal })
627
+ else :
628
+ headers .append ({})
629
+ with mocked_http_conn (* (primary_codes + handoff_codes ),
630
+ headers = headers ):
631
+ resp = req .get_response (self .app )
632
+ # handoff 404s form a quorum...
633
+ self .assertEqual (404 , resp .status_int ,
634
+ 'replicas = %s' % self .replicas ())
635
+ self .assertEqual (ts .internal , resp .headers .get ('X-Backend-Timestamp' ))
636
+
637
+ def test_DELETE_insufficient_found_plus_404_507_and_handoffs_fail (self ):
638
+ if self .replicas () < 3 :
639
+ return
640
+ primary_success = quorum_size (self .replicas ()) - 1
641
+ primary_failure = self .replicas () - primary_success - 1
642
+ primary_codes = [204 ] * primary_success + [404 ] + \
643
+ [507 ] * primary_failure
644
+ handoff_codes = [507 ] * self .replicas ()
645
+ req = swift .common .swob .Request .blank ('/v1/a/c/o' , method = 'DELETE' )
646
+ ts = self .ts ()
647
+ headers = []
648
+ for status in primary_codes + handoff_codes :
649
+ if status in (204 , 404 ):
650
+ headers .append ({'x-backend-timestamp' : ts .internal })
651
+ else :
652
+ headers .append ({})
653
+ with mocked_http_conn (* (primary_codes + handoff_codes ),
654
+ headers = headers ):
655
+ resp = req .get_response (self .app )
656
+ # overrides convert the 404 to a 204 so a quorum is formed...
657
+ self .assertEqual (resp .status_int , 204 ,
658
+ 'replicas = %s' % self .replicas ())
659
+
660
+ def test_DELETE_insufficient_found_plus_507_and_handoffs_fail (self ):
661
+ primary_success = quorum_size (self .replicas ()) - 1
662
+ primary_failure = self .replicas () - primary_success
663
+ primary_codes = [204 ] * primary_success + [507 ] * primary_failure
664
+ handoff_codes = [507 ] * self .replicas ()
665
+ req = swift .common .swob .Request .blank ('/v1/a/c/o' , method = 'DELETE' )
666
+ ts = self .ts ()
667
+ headers = []
668
+ for status in primary_codes + handoff_codes :
669
+ if status in (204 , 404 ):
670
+ headers .append ({'x-backend-timestamp' : ts .internal })
671
+ else :
672
+ headers .append ({})
673
+ with mocked_http_conn (* (primary_codes + handoff_codes ),
674
+ headers = headers ):
675
+ resp = req .get_response (self .app )
676
+ # no quorum...
677
+ self .assertEqual (resp .status_int , 503 ,
678
+ 'replicas = %s' % self .replicas ())
679
+
568
680
def test_DELETE_half_not_found_statuses (self ):
569
681
self .obj_ring .set_replicas (4 )
570
682
@@ -1300,6 +1412,96 @@ def test_write_affinity_per_policy_config_overrides_and_inherits(self):
1300
1412
self ._check_write_affinity (conf , policy_conf , POLICIES [1 ], [0 ],
1301
1413
3 * self .replicas (POLICIES [1 ]))
1302
1414
1415
+ def test_POST_all_primaries_succeed (self ):
1416
+ req = swift .common .swob .Request .blank ('/v1/a/c/o' , method = 'POST' )
1417
+ primary_codes = [202 ] * self .replicas ()
1418
+ with mocked_http_conn (* primary_codes ):
1419
+ resp = req .get_response (self .app )
1420
+ self .assertEqual (202 , resp .status_int ,
1421
+ 'replicas = %s' % self .replicas ())
1422
+
1423
+ def test_POST_sufficient_primaries_succeed_others_404 (self ):
1424
+ req = swift .common .swob .Request .blank ('/v1/a/c/o' , method = 'POST' )
1425
+ # NB: for POST to EC object quorum_size is sufficient for success
1426
+ # rather than policy.quorum
1427
+ primary_success = quorum_size (self .replicas ())
1428
+ primary_failure = self .replicas () - primary_success
1429
+ primary_codes = [202 ] * primary_success + [404 ] * primary_failure
1430
+ with mocked_http_conn (* primary_codes ):
1431
+ resp = req .get_response (self .app )
1432
+ self .assertEqual (202 , resp .status_int ,
1433
+ 'replicas = %s' % self .replicas ())
1434
+
1435
+ def test_POST_sufficient_primaries_succeed_others_fail (self ):
1436
+ req = swift .common .swob .Request .blank ('/v1/a/c/o' , method = 'POST' )
1437
+ # NB: for POST to EC object quorum_size is sufficient for success
1438
+ # rather than policy.quorum
1439
+ primary_success = quorum_size (self .replicas ())
1440
+ primary_failure = self .replicas () - primary_success
1441
+ primary_codes = [202 ] * primary_success + [Timeout ()] * primary_failure
1442
+ handoff_codes = [404 ] * primary_failure
1443
+ with mocked_http_conn (* (primary_codes + handoff_codes )):
1444
+ resp = req .get_response (self .app )
1445
+ self .assertEqual (202 , resp .status_int ,
1446
+ 'replicas = %s' % self .replicas ())
1447
+
1448
+ def test_POST_insufficient_primaries_succeed_others_404 (self ):
1449
+ req = swift .common .swob .Request .blank ('/v1/a/c/o' , method = 'POST' )
1450
+ primary_success = quorum_size (self .replicas ()) - 1
1451
+ primary_failure = self .replicas () - primary_success
1452
+ primary_codes = [404 ] * primary_failure + [202 ] * primary_success
1453
+ with mocked_http_conn (* primary_codes ):
1454
+ resp = req .get_response (self .app )
1455
+ # TODO: should this be a 503?
1456
+ self .assertEqual (404 , resp .status_int ,
1457
+ 'replicas = %s' % self .replicas ())
1458
+
1459
+ def test_POST_insufficient_primaries_others_fail_handoffs_404 (self ):
1460
+ req = swift .common .swob .Request .blank ('/v1/a/c/o' , method = 'POST' )
1461
+ primary_success = quorum_size (self .replicas ()) - 1
1462
+ primary_failure = self .replicas () - primary_success
1463
+ primary_codes = [Timeout ()] * primary_failure + [202 ] * primary_success
1464
+ handoff_codes = [404 ] * primary_failure
1465
+ with mocked_http_conn (* (primary_codes + handoff_codes )):
1466
+ resp = req .get_response (self .app )
1467
+ # TODO: this should really be a 503
1468
+ self .assertEqual (404 , resp .status_int ,
1469
+ 'replicas = %s' % self .replicas ())
1470
+
1471
+ def test_POST_insufficient_primaries_others_fail_handoffs_fail (self ):
1472
+ req = swift .common .swob .Request .blank ('/v1/a/c/o' , method = 'POST' )
1473
+ primary_success = quorum_size (self .replicas ()) - 1
1474
+ primary_failure = self .replicas () - primary_success
1475
+ primary_codes = [Timeout ()] * primary_failure + [202 ] * primary_success
1476
+ handoff_codes = [507 ] * self .replicas ()
1477
+ with mocked_http_conn (* (primary_codes + handoff_codes )):
1478
+ resp = req .get_response (self .app )
1479
+ self .assertEqual (503 , resp .status_int ,
1480
+ 'replicas = %s' % self .replicas ())
1481
+
1482
+ def test_POST_all_primaries_fail_insufficient_handoff_succeeds (self ):
1483
+ req = swift .common .swob .Request .blank ('/v1/a/c/o' , method = 'POST' )
1484
+ handoff_success = quorum_size (self .replicas ()) - 1
1485
+ handoff_not_found = self .replicas () - handoff_success
1486
+ primary_codes = [Timeout ()] * self .replicas ()
1487
+ handoff_codes = [202 ] * handoff_success + [404 ] * handoff_not_found
1488
+ with mocked_http_conn (* (primary_codes + handoff_codes )):
1489
+ resp = req .get_response (self .app )
1490
+ # TODO: this should really be a 503
1491
+ self .assertEqual (404 , resp .status_int ,
1492
+ 'replicas = %s' % self .replicas ())
1493
+
1494
+ def test_POST_all_primaries_fail_sufficient_handoff_succeeds (self ):
1495
+ req = swift .common .swob .Request .blank ('/v1/a/c/o' , method = 'POST' )
1496
+ handoff_success = quorum_size (self .replicas ())
1497
+ handoff_not_found = self .replicas () - handoff_success
1498
+ primary_codes = [Timeout ()] * self .replicas ()
1499
+ handoff_codes = [202 ] * handoff_success + [404 ] * handoff_not_found
1500
+ with mocked_http_conn (* (primary_codes + handoff_codes )):
1501
+ resp = req .get_response (self .app )
1502
+ self .assertEqual (202 , resp .status_int ,
1503
+ 'replicas = %s' % self .replicas ())
1504
+
1303
1505
# end of CommonObjectControllerMixin
1304
1506
1305
1507
0 commit comments