Merge pull request #60942 from AliMasarweh/wip-alimasa-bucket-logging-filter

RGW/logging: add filtering for bucket logging 

Reviewed-by: yuvalif<ylifshit@redhat.com>
This commit is contained in:
Ali Masarwa 2024-12-17 20:06:51 +02:00 committed by GitHub
commit a508216657
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 451 additions and 295 deletions

View File

@ -40,8 +40,8 @@ Journal
If logging type is set to "Journal", the records are written to the log bucket before the bucket operation is completed.
This means that if the logging action fails, the operation will not be executed, and an error will be returned to the client.
An exception to the above are "multi/delete" log records: if writing these log records fail, the operation continues and may still be successful.
Note that it may happen that the log records were successfully written, but the bucket operation failed, since the logs are written
before such a failure, there will be no indication for that in the log records.
Journal mode supports filtering out records based on matches of the prefixes and suffixes of the logged object keys. Regular-expression matching can also be used on these to create filters.
Note that it may happen that the log records were successfully written, but the bucket operation failed, since the logs are written.
Bucket Logging REST API

View File

@ -751,6 +751,14 @@ Parameters are XML encoded in the body of the request, in the following format:
<TargetPrefix>string</TargetPrefix>
<LoggingType>Standard|Journal</LoggingType>
<ObjectRollTime>integer</ObjectRollTime>
<Filter>
<S3Key>
<FilterRule>
<Name>suffix/prefix/regex</Name>
<Value></Value>
</FilterRule>
</S3Key>
</Filter>
</LoggingEnabled>
</BucketLoggingStatus>
@ -881,6 +889,14 @@ Response is XML encoded in the body of the request, in the following format:
<TargetPrefix>string</TargetPrefix>
<LoggingType>Standard|Journal</LoggingType>
<ObjectRollTime>integer</ObjectRollTime>
<Filter>
<S3Key>
<FilterRule>
<Name>suffix/prefix/regex</Name>
<Value></Value>
</FilterRule>
</S3Key>
</Filter>
</LoggingEnabled>
</BucketLoggingStatus>

View File

@ -39,6 +39,17 @@ bucket_logging_conf = {'LoggingEnabled': {
},
'ObjectRollTime': 60,
'LoggingType': 'Journal',
"Filter": {
"Key": {
"FilterRules":
[
{
"Name": "prefix",
"Value": "myfile"
}
]
}
}
}
}

View File

@ -310,6 +310,10 @@
"RecordsBatchSize":{
"shape":"RecordsBatchSize",
"documentation":"indicates how many records to batch in memory before writing to the object. if set to zero, records are written syncronously to the object. if <code>ObjectRollTime</code>e is reached, the batch of records will be written to the object regardless of the number of records. </p>"
},
"Filter":{
"shape":"LoggingConfigurationFilter",
"documentation":"<p>A filter for all log object. Filter for the object by its key (prefix, suffix and regex).</p>"
}
},
"documentation":"<p>Describes where logs are stored the prefix assigned to all log object keys for a bucket, and their format. also, the level the delivery guarantee of the records.</p>"
@ -363,6 +367,18 @@
"Standard",
"Journal"
]
},
"LoggingConfigurationFilter":{
"type":"structure",
"members":{
"Key":{
"shape":"S3KeyFilter",
"documentation":"<p/>",
"locationName":"S3Key"
}
},
"documentation":"<p>A filter for all log object. Filter for the object by its key (prefix, suffix and regex).</p>",
"locationName":"Filter"
}
},
"documentation":"<p/>"

View File

@ -90,6 +90,7 @@ set(librgw_common_srcs
rgw_notify_event_type.cc
rgw_period_history.cc
rgw_period_puller.cc
rgw_s3_filter.cc
rgw_pubsub.cc
rgw_coroutine.cc
rgw_cr_rest.cc

View File

@ -31,6 +31,9 @@ bool configuration::decode_xml(XMLObj* obj) {
logging_type = LoggingType::Standard;
} else if (type == "Journal") {
logging_type = LoggingType::Journal;
if (iter = o->find("Filter"); XMLObj* const filter_o = iter.get_next()) {
RGWXMLDecoder::decode_xml("S3Key", key_filter, filter_o);
}
} else {
// we don't allow for type "Any" in the configuration
throw RGWXMLDecoder::err("invalid bucket logging record type: '" + type + "'");
@ -73,6 +76,11 @@ void configuration::dump_xml(Formatter *f) const {
break;
case LoggingType::Journal:
::encode_xml("LoggingType", "Journal", f);
if (key_filter.has_content()) {
f->open_object_section("Filter");
::encode_xml("S3Key", key_filter, f);
f->close_section(); // Filter
}
break;
case LoggingType::Any:
::encode_xml("LoggingType", "", f);
@ -118,6 +126,10 @@ void configuration::dump(Formatter *f) const {
break;
case LoggingType::Journal:
encode_json("loggingType", "Journal", f);
if (key_filter.has_content()) {
Formatter::ObjectSection s(*f, "Filter");
encode_json("S3Key", key_filter, f);
}
break;
case LoggingType::Any:
encode_json("loggingType", "", f);
@ -526,6 +538,11 @@ int log_record(rgw::sal::Driver* driver,
if (type != LoggingType::Any && configuration.logging_type != type) {
return 0;
}
if (configuration.key_filter.has_content()) {
if (!match(configuration.key_filter, obj->get_name())) {
return 0;
}
}
ldpp_dout(dpp, 20) << "INFO: found matching logging configuration of bucket '" << s->bucket->get_name() <<
"' configuration: " << configuration.to_json_str() << dendl;
if (auto ret = log_record(driver, obj, s, op_name, etag, size, configuration, dpp, y, async_completion, log_source_bucket); ret < 0) {

View File

@ -10,6 +10,7 @@
#include "include/buffer.h"
#include "include/encoding.h"
#include "common/async/yield_context.h"
#include "rgw_s3_filter.h"
class XMLObj;
namespace ceph { class Formatter; }
@ -48,6 +49,14 @@ namespace rgw::bucketlogging {
<LoggingType>Standard|Journal</LoggingType> <!-- Ceph extension -->
<ObjectRollTime>integer</ObjectRollTime> <!-- Ceph extension -->
<RecordsBatchSize>integer</RecordsBatchSize> <!-- Ceph extension -->
<Filter>
<S3Key>
<FilterRule>
<Name>suffix/prefix/regex</Name>
<Value></Value>
</FilterRule>
</S3Key>
</Filter>
</LoggingEnabled>
</BucketLoggingStatus>
*/
@ -78,6 +87,7 @@ struct configuration {
PartitionDateSource date_source = PartitionDateSource::DeliveryTime;
// EventTime: use only year, month, and day. The hour, minutes and seconds are set to 00 in the key
// DeliveryTime: the time the log object was created
rgw_s3_key_filter key_filter;
bool decode_xml(XMLObj *obj);
void dump_xml(Formatter *f) const;
void dump(Formatter *f) const; // json
@ -92,6 +102,9 @@ struct configuration {
encode(static_cast<int>(logging_type), bl);
encode(records_batch_size, bl);
encode(static_cast<int>(date_source), bl);
if (logging_type == LoggingType::Journal) {
encode(key_filter, bl);
}
ENCODE_FINISH(bl);
}
@ -108,6 +121,9 @@ struct configuration {
decode(records_batch_size, bl);
decode(type, bl);
date_source = static_cast<PartitionDateSource>(type);
if (logging_type == LoggingType::Journal) {
decode(key_filter, bl);
}
DECODE_FINISH(bl);
}
};

View File

@ -62,214 +62,6 @@ void set_event_id(std::string& id, const std::string& hash, const utime_t& ts) {
}
}
void rgw_s3_key_filter::dump(Formatter *f) const {
if (!has_content()) {
return;
}
f->open_array_section("FilterRules");
if (!prefix_rule.empty()) {
f->open_object_section("");
::encode_json("Name", "prefix", f);
::encode_json("Value", prefix_rule, f);
f->close_section();
}
if (!suffix_rule.empty()) {
f->open_object_section("");
::encode_json("Name", "suffix", f);
::encode_json("Value", suffix_rule, f);
f->close_section();
}
if (!regex_rule.empty()) {
f->open_object_section("");
::encode_json("Name", "regex", f);
::encode_json("Value", regex_rule, f);
f->close_section();
}
f->close_section();
}
bool rgw_s3_key_filter::decode_xml(XMLObj* obj) {
XMLObjIter iter = obj->find("FilterRule");
XMLObj *o;
const auto throw_if_missing = true;
auto prefix_not_set = true;
auto suffix_not_set = true;
auto regex_not_set = true;
std::string name;
while ((o = iter.get_next())) {
RGWXMLDecoder::decode_xml("Name", name, o, throw_if_missing);
if (name == "prefix" && prefix_not_set) {
prefix_not_set = false;
RGWXMLDecoder::decode_xml("Value", prefix_rule, o, throw_if_missing);
} else if (name == "suffix" && suffix_not_set) {
suffix_not_set = false;
RGWXMLDecoder::decode_xml("Value", suffix_rule, o, throw_if_missing);
} else if (name == "regex" && regex_not_set) {
regex_not_set = false;
RGWXMLDecoder::decode_xml("Value", regex_rule, o, throw_if_missing);
} else {
throw RGWXMLDecoder::err("invalid/duplicate S3Key filter rule name: '" + name + "'");
}
}
return true;
}
void rgw_s3_key_filter::dump_xml(Formatter *f) const {
if (!prefix_rule.empty()) {
f->open_object_section("FilterRule");
::encode_xml("Name", "prefix", f);
::encode_xml("Value", prefix_rule, f);
f->close_section();
}
if (!suffix_rule.empty()) {
f->open_object_section("FilterRule");
::encode_xml("Name", "suffix", f);
::encode_xml("Value", suffix_rule, f);
f->close_section();
}
if (!regex_rule.empty()) {
f->open_object_section("FilterRule");
::encode_xml("Name", "regex", f);
::encode_xml("Value", regex_rule, f);
f->close_section();
}
}
bool rgw_s3_key_filter::has_content() const {
return !(prefix_rule.empty() && suffix_rule.empty() && regex_rule.empty());
}
void rgw_s3_key_value_filter::dump(Formatter *f) const {
if (!has_content()) {
return;
}
f->open_array_section("FilterRules");
for (const auto& key_value : kv) {
f->open_object_section("");
::encode_json("Name", key_value.first, f);
::encode_json("Value", key_value.second, f);
f->close_section();
}
f->close_section();
}
bool rgw_s3_key_value_filter::decode_xml(XMLObj* obj) {
kv.clear();
XMLObjIter iter = obj->find("FilterRule");
XMLObj *o;
const auto throw_if_missing = true;
std::string key;
std::string value;
while ((o = iter.get_next())) {
RGWXMLDecoder::decode_xml("Name", key, o, throw_if_missing);
RGWXMLDecoder::decode_xml("Value", value, o, throw_if_missing);
kv.emplace(key, value);
}
return true;
}
void rgw_s3_key_value_filter::dump_xml(Formatter *f) const {
for (const auto& key_value : kv) {
f->open_object_section("FilterRule");
::encode_xml("Name", key_value.first, f);
::encode_xml("Value", key_value.second, f);
f->close_section();
}
}
bool rgw_s3_key_value_filter::has_content() const {
return !kv.empty();
}
void rgw_s3_filter::dump(Formatter *f) const {
encode_json("S3Key", key_filter, f);
encode_json("S3Metadata", metadata_filter, f);
encode_json("S3Tags", tag_filter, f);
}
bool rgw_s3_filter::decode_xml(XMLObj* obj) {
RGWXMLDecoder::decode_xml("S3Key", key_filter, obj);
RGWXMLDecoder::decode_xml("S3Metadata", metadata_filter, obj);
RGWXMLDecoder::decode_xml("S3Tags", tag_filter, obj);
return true;
}
void rgw_s3_filter::dump_xml(Formatter *f) const {
if (key_filter.has_content()) {
::encode_xml("S3Key", key_filter, f);
}
if (metadata_filter.has_content()) {
::encode_xml("S3Metadata", metadata_filter, f);
}
if (tag_filter.has_content()) {
::encode_xml("S3Tags", tag_filter, f);
}
}
bool rgw_s3_filter::has_content() const {
return key_filter.has_content() ||
metadata_filter.has_content() ||
tag_filter.has_content();
}
bool match(const rgw_s3_key_filter& filter, const std::string& key) {
const auto key_size = key.size();
const auto prefix_size = filter.prefix_rule.size();
if (prefix_size != 0) {
// prefix rule exists
if (prefix_size > key_size) {
// if prefix is longer than key, we fail
return false;
}
if (!std::equal(filter.prefix_rule.begin(), filter.prefix_rule.end(), key.begin())) {
return false;
}
}
const auto suffix_size = filter.suffix_rule.size();
if (suffix_size != 0) {
// suffix rule exists
if (suffix_size > key_size) {
// if suffix is longer than key, we fail
return false;
}
if (!std::equal(filter.suffix_rule.begin(), filter.suffix_rule.end(), (key.end() - suffix_size))) {
return false;
}
}
if (!filter.regex_rule.empty()) {
// TODO add regex chaching in the filter
const std::regex base_regex(filter.regex_rule);
if (!std::regex_match(key, base_regex)) {
return false;
}
}
return true;
}
bool match(const rgw_s3_key_value_filter& filter, const KeyValueMap& kv) {
// all filter pairs must exist with the same value in the object's metadata/tags
// object metadata/tags may include items not in the filter
return std::includes(kv.begin(), kv.end(), filter.kv.begin(), filter.kv.end());
}
bool match(const rgw_s3_key_value_filter& filter, const KeyMultiValueMap& kv) {
// all filter pairs must exist with the same value in the object's metadata/tags
// object metadata/tags may include items not in the filter
for (auto& filter : filter.kv) {
auto result = kv.equal_range(filter.first);
if (std::any_of(result.first, result.second, [&filter](const std::pair<std::string, std::string>& p) { return p.second == filter.second;}))
continue;
else
return false;
}
return true;
}
bool match(const rgw::notify::EventTypeList& events, rgw::notify::EventType event) {
// if event list exists, and none of the events in the list matches the event type, filter the message
if (!events.empty() && std::find(events.begin(), events.end(), event) == events.end()) {

View File

@ -9,94 +9,10 @@
#include "rgw_zone.h"
#include "rgw_notify_event_type.h"
#include <boost/container/flat_map.hpp>
#include "rgw_s3_filter.h"
class XMLObj;
struct rgw_s3_key_filter {
std::string prefix_rule;
std::string suffix_rule;
std::string regex_rule;
bool has_content() const;
void dump(Formatter *f) const;
bool decode_xml(XMLObj *obj);
void dump_xml(Formatter *f) const;
void encode(bufferlist& bl) const {
ENCODE_START(1, 1, bl);
encode(prefix_rule, bl);
encode(suffix_rule, bl);
encode(regex_rule, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::const_iterator& bl) {
DECODE_START(1, bl);
decode(prefix_rule, bl);
decode(suffix_rule, bl);
decode(regex_rule, bl);
DECODE_FINISH(bl);
}
};
WRITE_CLASS_ENCODER(rgw_s3_key_filter)
using KeyValueMap = boost::container::flat_map<std::string, std::string>;
using KeyMultiValueMap = std::multimap<std::string, std::string>;
struct rgw_s3_key_value_filter {
KeyValueMap kv;
bool has_content() const;
void dump(Formatter *f) const;
bool decode_xml(XMLObj *obj);
void dump_xml(Formatter *f) const;
void encode(bufferlist& bl) const {
ENCODE_START(1, 1, bl);
encode(kv, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::const_iterator& bl) {
DECODE_START(1, bl);
decode(kv, bl);
DECODE_FINISH(bl);
}
};
WRITE_CLASS_ENCODER(rgw_s3_key_value_filter)
struct rgw_s3_filter {
rgw_s3_key_filter key_filter;
rgw_s3_key_value_filter metadata_filter;
rgw_s3_key_value_filter tag_filter;
bool has_content() const;
void dump(Formatter *f) const;
bool decode_xml(XMLObj *obj);
void dump_xml(Formatter *f) const;
void encode(bufferlist& bl) const {
ENCODE_START(2, 1, bl);
encode(key_filter, bl);
encode(metadata_filter, bl);
encode(tag_filter, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::const_iterator& bl) {
DECODE_START(2, bl);
decode(key_filter, bl);
decode(metadata_filter, bl);
if (struct_v >= 2) {
decode(tag_filter, bl);
}
DECODE_FINISH(bl);
}
};
WRITE_CLASS_ENCODER(rgw_s3_filter)
using OptionalFilter = std::optional<rgw_s3_filter>;
struct rgw_pubsub_topic_filter;

269
src/rgw/rgw_s3_filter.cc Normal file
View File

@ -0,0 +1,269 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab ft=cpp
#include "rgw_pubsub.h"
#include "rgw_tools.h"
#include "rgw_xml.h"
#include "rgw_s3_filter.h"
#include "common/errno.h"
#include "rgw_sal.h"
#include <regex>
#include <algorithm>
void rgw_s3_key_filter::dump(Formatter *f) const {
if (!has_content()) {
return;
}
f->open_array_section("FilterRules");
if (!prefix_rule.empty()) {
f->open_object_section("");
::encode_json("Name", "prefix", f);
::encode_json("Value", prefix_rule, f);
f->close_section();
}
if (!suffix_rule.empty()) {
f->open_object_section("");
::encode_json("Name", "suffix", f);
::encode_json("Value", suffix_rule, f);
f->close_section();
}
if (!regex_rule.empty()) {
f->open_object_section("");
::encode_json("Name", "regex", f);
::encode_json("Value", regex_rule, f);
f->close_section();
}
f->close_section();
}
bool rgw_s3_key_filter::decode_xml(XMLObj* obj) {
XMLObjIter iter = obj->find("FilterRule");
XMLObj *o;
const auto throw_if_missing = true;
auto prefix_not_set = true;
auto suffix_not_set = true;
auto regex_not_set = true;
std::string name;
while ((o = iter.get_next())) {
RGWXMLDecoder::decode_xml("Name", name, o, throw_if_missing);
if (name == "prefix" && prefix_not_set) {
prefix_not_set = false;
RGWXMLDecoder::decode_xml("Value", prefix_rule, o, throw_if_missing);
} else if (name == "suffix" && suffix_not_set) {
suffix_not_set = false;
RGWXMLDecoder::decode_xml("Value", suffix_rule, o, throw_if_missing);
} else if (name == "regex" && regex_not_set) {
regex_not_set = false;
RGWXMLDecoder::decode_xml("Value", regex_rule, o, throw_if_missing);
} else {
throw RGWXMLDecoder::err("invalid/duplicate S3Key filter rule name: '" + name + "'");
}
}
return true;
}
void rgw_s3_key_filter::dump_xml(Formatter *f) const {
if (!prefix_rule.empty()) {
f->open_object_section("FilterRule");
::encode_xml("Name", "prefix", f);
::encode_xml("Value", prefix_rule, f);
f->close_section();
}
if (!suffix_rule.empty()) {
f->open_object_section("FilterRule");
::encode_xml("Name", "suffix", f);
::encode_xml("Value", suffix_rule, f);
f->close_section();
}
if (!regex_rule.empty()) {
f->open_object_section("FilterRule");
::encode_xml("Name", "regex", f);
::encode_xml("Value", regex_rule, f);
f->close_section();
}
}
bool rgw_s3_key_filter::has_content() const {
return !(prefix_rule.empty() && suffix_rule.empty() && regex_rule.empty());
}
void rgw_s3_key_value_filter::dump(Formatter *f) const {
if (!has_content()) {
return;
}
f->open_array_section("FilterRules");
for (const auto& key_value : kv) {
f->open_object_section("");
::encode_json("Name", key_value.first, f);
::encode_json("Value", key_value.second, f);
f->close_section();
}
f->close_section();
}
bool rgw_s3_key_value_filter::decode_xml(XMLObj* obj) {
kv.clear();
XMLObjIter iter = obj->find("FilterRule");
XMLObj *o;
const auto throw_if_missing = true;
std::string key;
std::string value;
while ((o = iter.get_next())) {
RGWXMLDecoder::decode_xml("Name", key, o, throw_if_missing);
RGWXMLDecoder::decode_xml("Value", value, o, throw_if_missing);
kv.emplace(key, value);
}
return true;
}
void rgw_s3_key_value_filter::dump_xml(Formatter *f) const {
for (const auto& key_value : kv) {
f->open_object_section("FilterRule");
::encode_xml("Name", key_value.first, f);
::encode_xml("Value", key_value.second, f);
f->close_section();
}
}
bool rgw_s3_key_value_filter::has_content() const {
return !kv.empty();
}
void rgw_s3_filter::dump(Formatter *f) const {
encode_json("S3Key", key_filter, f);
encode_json("S3Metadata", metadata_filter, f);
encode_json("S3Tags", tag_filter, f);
}
bool rgw_s3_filter::decode_xml(XMLObj* obj) {
RGWXMLDecoder::decode_xml("S3Key", key_filter, obj);
RGWXMLDecoder::decode_xml("S3Metadata", metadata_filter, obj);
RGWXMLDecoder::decode_xml("S3Tags", tag_filter, obj);
return true;
}
void rgw_s3_filter::dump_xml(Formatter *f) const {
if (key_filter.has_content()) {
::encode_xml("S3Key", key_filter, f);
}
if (metadata_filter.has_content()) {
::encode_xml("S3Metadata", metadata_filter, f);
}
if (tag_filter.has_content()) {
::encode_xml("S3Tags", tag_filter, f);
}
}
bool rgw_s3_filter::has_content() const {
return key_filter.has_content() ||
metadata_filter.has_content() ||
tag_filter.has_content();
}
bool match(const rgw_s3_key_filter& filter, const std::string& key) {
const auto key_size = key.size();
const auto prefix_size = filter.prefix_rule.size();
if (prefix_size != 0) {
// prefix rule exists
if (prefix_size > key_size) {
// if prefix is longer than key, we fail
return false;
}
if (!std::equal(filter.prefix_rule.begin(), filter.prefix_rule.end(), key.begin())) {
return false;
}
}
const auto suffix_size = filter.suffix_rule.size();
if (suffix_size != 0) {
// suffix rule exists
if (suffix_size > key_size) {
// if suffix is longer than key, we fail
return false;
}
if (!std::equal(filter.suffix_rule.begin(), filter.suffix_rule.end(), (key.end() - suffix_size))) {
return false;
}
}
if (!filter.regex_rule.empty()) {
// TODO add regex caching in the filter
const std::regex base_regex(filter.regex_rule);
if (!std::regex_match(key, base_regex)) {
return false;
}
}
return true;
}
bool match(const rgw_s3_key_value_filter& filter, const KeyValueMap& kv) {
// all filter pairs must exist with the same value in the object's metadata/tags
// object metadata/tags may include items not in the filter
return std::includes(kv.begin(), kv.end(), filter.kv.begin(), filter.kv.end());
}
bool match(const rgw_s3_key_value_filter& filter, const KeyMultiValueMap& kv) {
// all filter pairs must exist with the same value in the object's metadata/tags
// object metadata/tags may include items not in the filter
for (auto& filter : filter.kv) {
auto result = kv.equal_range(filter.first);
if (std::any_of(result.first, result.second, [&filter](const std::pair<std::string, std::string>& p) { return p.second == filter.second;}))
continue;
else
return false;
}
return true;
}
bool match(const rgw_s3_filter& s3_filter, const rgw::sal::Object* obj) {
if (obj == nullptr) {
return false;
}
if (match(s3_filter.key_filter, obj->get_name())) {
return true;
}
const auto &attrs = obj->get_attrs();
if (!s3_filter.metadata_filter.kv.empty()) {
KeyValueMap attrs_map;
for (auto& attr : attrs) {
if (boost::algorithm::starts_with(attr.first, RGW_ATTR_META_PREFIX)) {
std::string_view key(attr.first);
key.remove_prefix(sizeof(RGW_ATTR_PREFIX)-1);
// we want to pass a null terminated version
// of the bufferlist, hence "to_str().c_str()"
attrs_map.emplace(key, attr.second.to_str().c_str());
}
}
if (match(s3_filter.metadata_filter, attrs_map)) {
return true;
}
}
if (!s3_filter.tag_filter.kv.empty()) {
// tag filter exists
// try to fetch tags from the attributes
KeyMultiValueMap tags;
const auto attr_iter = attrs.find(RGW_ATTR_TAGS);
if (attr_iter != attrs.end()) {
auto bliter = attr_iter->second.cbegin();
RGWObjTags obj_tags;
try {
::decode(obj_tags, bliter);
} catch (buffer::error &) {
// not able to decode tags
return false;
}
tags = std::move(obj_tags.get_tags());
}
if (match(s3_filter.tag_filter, tags)) {
return true;
}
}
return false;
}

102
src/rgw/rgw_s3_filter.h Normal file
View File

@ -0,0 +1,102 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab ft=cpp
#pragma once
#include "rgw_tools.h"
#include <boost/container/flat_map.hpp>
class XMLObj;
struct rgw_s3_key_filter {
std::string prefix_rule;
std::string suffix_rule;
std::string regex_rule;
bool has_content() const;
void dump(Formatter *f) const;
bool decode_xml(XMLObj *obj);
void dump_xml(Formatter *f) const;
void encode(bufferlist& bl) const {
ENCODE_START(1, 1, bl);
encode(prefix_rule, bl);
encode(suffix_rule, bl);
encode(regex_rule, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::const_iterator& bl) {
DECODE_START(1, bl);
decode(prefix_rule, bl);
decode(suffix_rule, bl);
decode(regex_rule, bl);
DECODE_FINISH(bl);
}
};
WRITE_CLASS_ENCODER(rgw_s3_key_filter)
using KeyValueMap = boost::container::flat_map<std::string, std::string>;
using KeyMultiValueMap = std::multimap<std::string, std::string>;
struct rgw_s3_key_value_filter {
KeyValueMap kv;
bool has_content() const;
void dump(Formatter *f) const;
bool decode_xml(XMLObj *obj);
void dump_xml(Formatter *f) const;
void encode(bufferlist& bl) const {
ENCODE_START(1, 1, bl);
encode(kv, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::const_iterator& bl) {
DECODE_START(1, bl);
decode(kv, bl);
DECODE_FINISH(bl);
}
};
WRITE_CLASS_ENCODER(rgw_s3_key_value_filter)
struct rgw_s3_filter {
rgw_s3_key_filter key_filter;
rgw_s3_key_value_filter metadata_filter;
rgw_s3_key_value_filter tag_filter;
bool has_content() const;
void dump(Formatter *f) const;
bool decode_xml(XMLObj *obj);
void dump_xml(Formatter *f) const;
void encode(bufferlist& bl) const {
ENCODE_START(2, 1, bl);
encode(key_filter, bl);
encode(metadata_filter, bl);
encode(tag_filter, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::const_iterator& bl) {
DECODE_START(2, bl);
decode(key_filter, bl);
decode(metadata_filter, bl);
if (struct_v >= 2) {
decode(tag_filter, bl);
}
DECODE_FINISH(bl);
}
};
WRITE_CLASS_ENCODER(rgw_s3_filter)
bool match(const rgw_s3_key_filter& filter, const std::string& key);
bool match(const rgw_s3_key_value_filter& filter, const KeyValueMap& kv);
bool match(const rgw_s3_key_value_filter& filter, const KeyMultiValueMap& kv);
bool match(const rgw_s3_filter& filter, const rgw::sal::Object* obj);