Skip to content

feat(storage): support Autoclass feature #10003

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions google/cloud/storage/bucket_autoclass.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "google/cloud/storage/bucket_autoclass.h"
#include "google/cloud/internal/format_time_point.h"
#include "google/cloud/internal/ios_flags_saver.h"
#include <iomanip>
#include <iostream>

namespace google {
namespace cloud {
namespace storage {
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN

std::ostream& operator<<(std::ostream& os, BucketAutoclass const& rhs) {
google::cloud::internal::IosFlagsSaver flags(os);
return os << "{enabled=" << std::boolalpha << rhs.enabled << ", toggle_time="
<< google::cloud::internal::FormatRfc3339(rhs.toggle_time) << "}";
}

GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace storage
} // namespace cloud
} // namespace google
64 changes: 64 additions & 0 deletions google/cloud/storage/bucket_autoclass.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_BUCKET_AUTOCLASS_H
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_BUCKET_AUTOCLASS_H

#include "google/cloud/storage/version.h"
#include <chrono>
#include <iosfwd>
#include <tuple>

namespace google {
namespace cloud {
namespace storage {
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN

/**
* The autoclass configuration for a Bucket.
*
* @par Example
*
* @snippet storage_bucket_autoclass_samples.cc get-autoclass
*
* @par Example
*
* @snippet storage_bucket_autoclass_samples.cc set-autoclass
*/
struct BucketAutoclass {
explicit BucketAutoclass(bool e) : enabled(e) {}
explicit BucketAutoclass(bool e, std::chrono::system_clock::time_point tp)
: enabled(e), toggle_time(tp) {}

bool enabled;
std::chrono::system_clock::time_point toggle_time;
};

inline bool operator==(BucketAutoclass const& lhs, BucketAutoclass const& rhs) {
return std::tie(lhs.enabled, lhs.toggle_time) ==
std::tie(rhs.enabled, rhs.toggle_time);
}

inline bool operator!=(BucketAutoclass const& lhs, BucketAutoclass const& rhs) {
return !(lhs == rhs);
}

std::ostream& operator<<(std::ostream& os, BucketAutoclass const& rhs);

GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace storage
} // namespace cloud
} // namespace google

#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_BUCKET_AUTOCLASS_H
16 changes: 16 additions & 0 deletions google/cloud/storage/bucket_metadata.cc
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ nlohmann::json ActionAsPatch(LifecycleRuleAction const& a) {

bool operator==(BucketMetadata const& lhs, BucketMetadata const& rhs) {
return lhs.acl_ == rhs.acl_ //
&& lhs.autoclass_ == rhs.autoclass_ //
&& lhs.billing_ == rhs.billing_ //
&& lhs.cors_ == rhs.cors_ //
&& lhs.custom_placement_config_ == rhs.custom_placement_config_ //
Expand Down Expand Up @@ -121,6 +122,9 @@ std::ostream& operator<<(std::ostream& os, BucketMetadata const& rhs) {
os << absl::StrJoin(rhs.acl(), ", ", absl::StreamFormatter());
os << "]";

if (rhs.has_autoclass()) {
os << ", autoclass=" << rhs.autoclass();
}
if (rhs.has_billing()) {
auto previous_flags = os.flags();
os << ", billing.requesterPays=" << std::boolalpha
Expand Down Expand Up @@ -249,6 +253,18 @@ BucketMetadataPatchBuilder& BucketMetadataPatchBuilder::ResetAcl() {
return *this;
}

BucketMetadataPatchBuilder& BucketMetadataPatchBuilder::SetAutoclass(
BucketAutoclass const& v) {
impl_.AddSubPatch(
"autoclass", internal::PatchBuilder().SetBoolField("enabled", v.enabled));
return *this;
}

BucketMetadataPatchBuilder& BucketMetadataPatchBuilder::ResetAutoclass() {
impl_.RemoveField("autoclass");
return *this;
}

BucketMetadataPatchBuilder& BucketMetadataPatchBuilder::SetBilling(
BucketBilling const& v) {
impl_.AddSubPatch("billing", internal::PatchBuilder().SetBoolField(
Expand Down
22 changes: 22 additions & 0 deletions google/cloud/storage/bucket_metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_BUCKET_METADATA_H

#include "google/cloud/storage/bucket_access_control.h"
#include "google/cloud/storage/bucket_autoclass.h"
#include "google/cloud/storage/bucket_billing.h"
#include "google/cloud/storage/bucket_cors_entry.h"
#include "google/cloud/storage/bucket_custom_placement_config.h"
Expand Down Expand Up @@ -69,6 +70,23 @@ class BucketMetadata {
}
///@}

/// @name Accessors and modifiers for Autoclass configuration.
///@{
bool has_autoclass() const { return autoclass_.has_value(); }
BucketAutoclass const& autoclass() const { return *autoclass_; }
absl::optional<BucketAutoclass> const& autoclass_as_optional() const {
return autoclass_;
}
BucketMetadata& set_autoclass(BucketAutoclass v) {
autoclass_ = std::move(v);
return *this;
}
BucketMetadata& reset_autoclass() {
autoclass_.reset();
return *this;
}
///@}

/**
* @name Get and set billing configuration for the Bucket.
*
Expand Down Expand Up @@ -549,6 +567,7 @@ class BucketMetadata {
friend std::ostream& operator<<(std::ostream& os, BucketMetadata const& rhs);
// Keep the fields in alphabetical order.
std::vector<BucketAccessControl> acl_;
absl::optional<BucketAutoclass> autoclass_;
absl::optional<BucketBilling> billing_;
std::vector<CorsEntry> cors_;
absl::optional<BucketCustomPlacementConfig> custom_placement_config_;
Expand Down Expand Up @@ -607,6 +626,9 @@ class BucketMetadataPatchBuilder {
*/
BucketMetadataPatchBuilder& ResetAcl();

BucketMetadataPatchBuilder& SetAutoclass(BucketAutoclass const& v);
BucketMetadataPatchBuilder& ResetAutoclass();

BucketMetadataPatchBuilder& SetBilling(BucketBilling const& v);
BucketMetadataPatchBuilder& ResetBilling();

Expand Down
74 changes: 74 additions & 0 deletions google/cloud/storage/bucket_metadata_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ BucketMetadata CreateBucketMetadataForTest() {
"etag": "AYX="
}
],
"autoclass": {
"enabled": true,
"toggleTime": "2022-10-07T01:02:03Z"
},
"billing": {
"requesterPays": true
},
Expand Down Expand Up @@ -178,6 +182,14 @@ TEST(BucketMetadataTest, Parse) {
EXPECT_EQ(2, actual.acl().size());
EXPECT_EQ("acl-id-0", actual.acl().at(0).id());
EXPECT_EQ("acl-id-1", actual.acl().at(1).id());

auto const expected_autoclass_toggle =
google::cloud::internal::ParseRfc3339("2022-10-07T01:02:03Z");
ASSERT_STATUS_OK(expected_autoclass_toggle);
ASSERT_TRUE(actual.has_autoclass());
EXPECT_EQ(actual.autoclass(),
BucketAutoclass(true, *expected_autoclass_toggle));

EXPECT_TRUE(actual.billing().requester_pays);
EXPECT_EQ(2, actual.cors().size());
auto expected_cors_0 =
Expand Down Expand Up @@ -302,6 +314,12 @@ TEST(BucketMetadataTest, IOStream) {
// acl()
EXPECT_THAT(actual, HasSubstr("acl-id-0"));
EXPECT_THAT(actual, HasSubstr("acl-id-1"));

// autoclass()
EXPECT_THAT(
actual,
HasSubstr("autoclass={enabled=true, toggle_time=2022-10-07T01:02:03Z}"));

// billing()
EXPECT_THAT(actual, HasSubstr("enabled=true"));

Expand Down Expand Up @@ -383,6 +401,15 @@ TEST(BucketMetadataTest, ToJsonString) {
EXPECT_EQ("user-test-user", actual["acl"][0].value("entity", ""));
EXPECT_EQ("user-test-user2", actual["acl"][1].value("entity", ""));

// autoclass()
ASSERT_TRUE(actual.contains("autoclass"));
auto const expected_autoclass = nlohmann::json{
{"enabled", true},
// "toggleTime" is OUTPUT_ONLY and thus not included in the
// JSON string for create/update.
};
EXPECT_EQ(actual["autoclass"], expected_autoclass);

// billing()
ASSERT_EQ(1U, actual.count("billing")) << actual;
EXPECT_TRUE(actual["billing"].value("requesterPays", false));
Expand Down Expand Up @@ -573,6 +600,32 @@ TEST(BucketMetadataTest, SetAcl) {
EXPECT_EQ("READER", copy.acl().at(0).role());
}

/// @test Verify we can change the autoclass configuration in BucketMetadata.
TEST(BucketMetadataTest, SetAutoclass) {
auto expected = CreateBucketMetadataForTest();
auto copy = expected;
ASSERT_TRUE(copy.has_autoclass());
ASSERT_TRUE(copy.autoclass().enabled);
copy.set_autoclass(
BucketAutoclass{false, std::chrono::system_clock::time_point()});
EXPECT_NE(expected, copy);
ASSERT_TRUE(copy.has_autoclass());
ASSERT_FALSE(copy.autoclass().enabled);
}

/// @test Verify we can reset the autoclass configuration in BucketMetadata.
TEST(BucketMetadataTest, ResetAutoclass) {
auto expected = CreateBucketMetadataForTest();
EXPECT_TRUE(expected.has_autoclass());
auto copy = expected;
copy.reset_autoclass();
EXPECT_FALSE(copy.has_autoclass());
EXPECT_NE(expected, copy);
std::ostringstream os;
os << copy;
EXPECT_THAT(os.str(), Not(HasSubstr("autoclass")));
}

/// @test Verify we can change the billing configuration in BucketMetadata.
TEST(BucketMetadataTest, SetBilling) {
auto expected = CreateBucketMetadataForTest();
Expand Down Expand Up @@ -945,6 +998,27 @@ TEST(BucketMetadataPatchBuilder, ResetAcl) {
ASSERT_TRUE(json["acl"].is_null()) << json;
}

TEST(BucketMetadataPatchBuilder, SetAutoclass) {
BucketMetadataPatchBuilder builder;
builder.SetAutoclass(BucketAutoclass(true));

auto actual = builder.BuildPatch();
auto const json = nlohmann::json::parse(actual);
ASSERT_TRUE(json.contains("autoclass")) << json;
auto const expected_autoclass = nlohmann::json{{"enabled", true}};
EXPECT_EQ(expected_autoclass, json["autoclass"]);
}

TEST(BucketMetadataPatchBuilder, ResetAutoclass) {
BucketMetadataPatchBuilder builder;
builder.ResetAutoclass();

auto actual = builder.BuildPatch();
auto json = nlohmann::json::parse(actual);
ASSERT_TRUE(json.contains("autoclass")) << json;
ASSERT_TRUE(json["autoclass"].is_null()) << json;
}

TEST(BucketMetadataPatchBuilder, SetBilling) {
BucketMetadataPatchBuilder builder;
builder.SetBilling(BucketBilling{true});
Expand Down
1 change: 1 addition & 0 deletions google/cloud/storage/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ if (BUILD_TESTING)
set(storage_examples
# cmake-format: sort
storage_bucket_acl_samples.cc
storage_bucket_autoclass_samples.cc
storage_bucket_cors_samples.cc
storage_bucket_default_kms_key_samples.cc
storage_bucket_iam_samples.cc
Expand Down
Loading