Skip to content

Commit 598f07d

Browse files
authored
fix(storage): better error code for CreateBucket() and 409 errors (#10480)
It seems that 409 should be treated as a `kAlreadyExists` in this case, and this case only.
1 parent a46f9a8 commit 598f07d

File tree

3 files changed

+41
-6
lines changed

3 files changed

+41
-6
lines changed

google/cloud/storage/internal/curl_client.cc

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -204,13 +204,20 @@ StatusOr<BucketMetadata> CurlClient::CreateBucket(
204204
// Assume the bucket name is validated by the caller.
205205
CurlRequestBuilder builder(storage_endpoint_ + "/b", storage_factory_);
206206
auto status = SetupBuilder(builder, request, "POST");
207-
if (!status.ok()) {
208-
return status;
209-
}
207+
if (!status.ok()) return status;
210208
builder.AddQueryParameter("project", request.project_id());
211209
builder.AddHeader("Content-Type: application/json");
212-
return CheckedFromString<BucketMetadataParser>(
210+
auto response = CheckedFromString<BucketMetadataParser>(
213211
std::move(builder).BuildRequest().MakeRequest(request.json_payload()));
212+
// GCS returns a 409 when buckets already exist:
213+
// https://cloud.google.com/storage/docs/json_api/v1/status-codes#409-conflict
214+
// This seems to be the only case where kAlreadyExists is a better match
215+
// for 409 than kAborted.
216+
if (!response && response.status().code() == StatusCode::kAborted) {
217+
return Status(StatusCode::kAlreadyExists, response.status().message(),
218+
response.status().error_info());
219+
}
220+
return response;
214221
}
215222

216223
StatusOr<BucketMetadata> CurlClient::GetBucketMetadata(

google/cloud/storage/internal/rest_client.cc

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,18 @@ StatusOr<BucketMetadata> RestClient::CreateBucket(
210210
builder.AddQueryParameter("project", request.project_id());
211211
builder.AddHeader("Content-Type", "application/json");
212212
auto payload = request.json_payload();
213-
return CheckedFromString<BucketMetadataParser>(storage_rest_client_->Post(
214-
std::move(builder).BuildRequest(), {absl::MakeConstSpan(payload)}));
213+
auto response =
214+
CheckedFromString<BucketMetadataParser>(storage_rest_client_->Post(
215+
std::move(builder).BuildRequest(), {absl::MakeConstSpan(payload)}));
216+
// GCS returns a 409 when buckets already exist:
217+
// https://cloud.google.com/storage/docs/json_api/v1/status-codes#409-conflict
218+
// This seems to be the only case where kAlreadyExists is a better match
219+
// for 409 than kAborted.
220+
if (!response && response.status().code() == StatusCode::kAborted) {
221+
return Status(StatusCode::kAlreadyExists, response.status().message(),
222+
response.status().error_info());
223+
}
224+
return response;
215225
}
216226

217227
StatusOr<BucketMetadata> RestClient::GetBucketMetadata(

google/cloud/storage/tests/bucket_integration_test.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ namespace {
3333
using ::google::cloud::storage::testing::AclEntityNames;
3434
using ::google::cloud::testing_util::ContainsOnce;
3535
using ::google::cloud::testing_util::IsOk;
36+
using ::google::cloud::testing_util::StatusIs;
3637
using ::testing::Contains;
3738
using ::testing::HasSubstr;
3839
using ::testing::IsEmpty;
@@ -155,6 +156,23 @@ TEST_F(BucketIntegrationTest, BasicCRUD) {
155156
EXPECT_THAT(list_bucket_names(), Not(Contains(bucket_name)));
156157
}
157158

159+
TEST_F(BucketIntegrationTest, CreateDuplicate) {
160+
StatusOr<Client> client = MakeBucketIntegrationTestClient();
161+
ASSERT_STATUS_OK(client);
162+
auto const bucket_name = MakeRandomBucketName();
163+
auto metadata = client->CreateBucketForProject(bucket_name, project_id_,
164+
BucketMetadata());
165+
ASSERT_STATUS_OK(metadata);
166+
ScheduleForDelete(*metadata);
167+
EXPECT_EQ(bucket_name, metadata->name());
168+
// Wait at least 2 seconds before trying to create / delete another bucket.
169+
if (!UsingEmulator()) std::this_thread::sleep_for(std::chrono::seconds(2));
170+
171+
auto dup = client->CreateBucketForProject(bucket_name, project_id_,
172+
BucketMetadata());
173+
EXPECT_THAT(dup, StatusIs(StatusCode::kAlreadyExists));
174+
}
175+
158176
TEST_F(BucketIntegrationTest, CreatePredefinedAcl) {
159177
std::vector<PredefinedAcl> test_values{
160178
PredefinedAcl::AuthenticatedRead(), PredefinedAcl::Private(),

0 commit comments

Comments
 (0)