@@ -108,9 +108,6 @@ ss::future<std::vector<self_test_result>> cloudcheck::run_benchmarks() {
108
108
const auto bucket = cloud_storage_clients::bucket_name{
109
109
cloud_storage::configuration::get_bucket_config ()().value ()};
110
110
111
- const auto self_test_prefix = cloud_storage_clients::object_key{
112
- " self-test/" };
113
-
114
111
const auto uuid = cloud_storage_clients::object_key{
115
112
ss::sstring{uuid_t::create ()}};
116
113
const auto self_test_key = cloud_storage_clients::object_key{
@@ -209,6 +206,13 @@ ss::future<std::vector<self_test_result>> cloudcheck::run_benchmarks() {
209
206
&cloudcheck::verify_deletes, bucket, num_default_objects);
210
207
results.push_back (std::move (deletes_test_result.test_result ));
211
208
209
+ // Test Puts and Gets with conditional headers (`If-None-Match`,
210
+ // `If-Match`).
211
+ auto conditional_puts_and_gets_test_result = co_await do_run_test (
212
+ &cloudcheck::verify_conditional_puts_and_gets, bucket);
213
+ results.push_back (
214
+ std::move (conditional_puts_and_gets_test_result.test_result ));
215
+
212
216
co_return results;
213
217
}
214
218
@@ -461,8 +465,10 @@ ss::future<cloudcheck::verify_deletes_result> cloudcheck::verify_deletes(
461
465
}
462
466
463
467
std::vector<cloud_storage_clients::object_key> keys (num_objects);
464
- std::generate (keys.begin (), keys.end (), []() {
465
- return cloud_storage_clients::object_key{ss::sstring{uuid_t::create ()}};
468
+ std::generate (keys.begin (), keys.end (), [this ]() {
469
+ const auto uuid = cloud_storage_clients::object_key{
470
+ ss::sstring{uuid_t::create ()}};
471
+ return cloud_storage_clients::object_key{self_test_prefix / uuid};
466
472
});
467
473
468
474
for (const auto & key : keys) {
@@ -495,4 +501,145 @@ ss::future<cloudcheck::verify_deletes_result> cloudcheck::verify_deletes(
495
501
co_return result;
496
502
}
497
503
504
+ ss::future<cloudcheck::verify_upload_result>
505
+ cloudcheck::verify_conditional_puts_and_gets (
506
+ cloud_storage_clients::bucket_name bucket) {
507
+ auto result = self_test_result{
508
+ .name = _opts.name ,
509
+ .info = " Conditional Puts and Gets" ,
510
+ .test_type = " cloud" };
511
+
512
+ if (_cancelled) {
513
+ result.warning = " Run was manually cancelled." ;
514
+ co_return result;
515
+ }
516
+
517
+ const auto uuid = cloud_storage_clients::object_key{
518
+ ss::sstring{uuid_t::create ()}};
519
+ auto key = cloud_storage_clients::object_key{self_test_prefix / uuid};
520
+ auto payload = make_random_payload ();
521
+
522
+ try {
523
+ ss::sstring etag = {};
524
+ // Attempt the first upload - expect it to succeed.
525
+ {
526
+ auto rtc = retry_chain_node (_opts.timeout , _opts.backoff , &_rtc);
527
+ cloud_storage::upload_request upload_request = make_upload_request (
528
+ bucket, key, payload.copy (), rtc);
529
+ upload_request.etag = &etag;
530
+ const cloud_storage::upload_result upload_result
531
+ = co_await _cloud_storage_api.local ().upload_object (
532
+ std::move (upload_request));
533
+ switch (upload_result) {
534
+ case cloud_storage::upload_result::success:
535
+ break ;
536
+ case cloud_storage::upload_result::timedout:
537
+ [[fallthrough]];
538
+ case cloud_storage::upload_result::failed:
539
+ [[fallthrough]];
540
+ case cloud_storage::upload_result::precondition_failed:
541
+ [[fallthrough]];
542
+ case cloud_storage::upload_result::cancelled:
543
+ result.error = " Failed to upload to cloud storage." ;
544
+ break ;
545
+ }
546
+ }
547
+
548
+ // Make a conditional read with the recorded etag - expect it to
549
+ // succeed.
550
+ {
551
+ iobuf download_payload;
552
+ auto rtc = retry_chain_node (_opts.timeout , _opts.backoff , &_rtc);
553
+ cloud_storage::download_request download_request
554
+ = make_download_request (
555
+ bucket, key, std::ref (download_payload), rtc);
556
+ download_request.set_download_if_matches (etag);
557
+ const cloud_storage::download_result download_result
558
+ = co_await _cloud_storage_api.local ().download_object (
559
+ std::move (download_request));
560
+
561
+ switch (download_result) {
562
+ case cloud_storage::download_result::success:
563
+ break ;
564
+ case cloud_storage::download_result::timedout:
565
+ [[fallthrough]];
566
+ case cloud_storage::download_result::failed:
567
+ [[fallthrough]];
568
+ case cloud_storage::download_result::precondition_failed:
569
+ [[fallthrough]];
570
+ case cloud_storage::download_result::notfound:
571
+ result.error = " Failed to download from cloud storage using "
572
+ " recorded ETag and If-Match header." ;
573
+ break ;
574
+ }
575
+ }
576
+
577
+ // Make a conditional read with an invalid etag - expect it to fail.
578
+ {
579
+ iobuf download_payload;
580
+ auto rtc = retry_chain_node (_opts.timeout , _opts.backoff , &_rtc);
581
+ cloud_storage::download_request download_request
582
+ = make_download_request (
583
+ bucket, key, std::ref (download_payload), rtc);
584
+ download_request.set_download_if_matches (" DEADBEEF" );
585
+ const cloud_storage::download_result download_result
586
+ = co_await _cloud_storage_api.local ().download_object (
587
+ std::move (download_request));
588
+
589
+ switch (download_result) {
590
+ case cloud_storage::download_result::precondition_failed:
591
+ break ;
592
+ case cloud_storage::download_result::success:
593
+ result.error
594
+ = " Failed to respect conditional read from cloud storage." ;
595
+ break ;
596
+ case cloud_storage::download_result::timedout:
597
+ [[fallthrough]];
598
+ case cloud_storage::download_result::failed:
599
+ [[fallthrough]];
600
+ case cloud_storage::download_result::notfound:
601
+ result.error = " Failed to download from cloud storage." ;
602
+ break ;
603
+ }
604
+ }
605
+
606
+ // Attempt the second upload, with If-None-Match header - expect it to
607
+ // fail.
608
+ {
609
+ auto rtc = retry_chain_node (_opts.timeout , _opts.backoff , &_rtc);
610
+ cloud_storage::upload_request upload_request = make_upload_request (
611
+ bucket, key, payload.copy (), rtc);
612
+ upload_request.set_upload_if_not_exists ();
613
+ const cloud_storage::upload_result upload_result
614
+ = co_await _cloud_storage_api.local ().upload_object (
615
+ std::move (upload_request));
616
+
617
+ switch (upload_result) {
618
+ case cloud_storage::upload_result::precondition_failed:
619
+ break ;
620
+ case cloud_storage::upload_result::success:
621
+ [[fallthrough]];
622
+ case cloud_storage::upload_result::timedout:
623
+ [[fallthrough]];
624
+ case cloud_storage::upload_result::failed:
625
+ [[fallthrough]];
626
+ case cloud_storage::upload_result::cancelled:
627
+ result.error = " Failed to respect If-None-Match header." ;
628
+ break ;
629
+ }
630
+ }
631
+
632
+ // Clean-up the uploaded file.
633
+ {
634
+ auto rtc = retry_chain_node (_opts.timeout , _opts.backoff , &_rtc);
635
+ std::ignore = co_await _cloud_storage_api.local ().delete_object (
636
+ bucket, key, rtc);
637
+ }
638
+ } catch (const std::exception& e) {
639
+ result.error = e.what ();
640
+ }
641
+
642
+ co_return result;
643
+ }
644
+
498
645
} // namespace cluster::self_test
0 commit comments