#include "include/function2.hpp"
#include "common/Formatter.h"
#include "common/containers.h"
+#include "common/split.h"
#include <common/errno.h>
#include "include/random.h"
#include "cls/lock/cls_lock_client.h"
using namespace librados;
+static inline std::string_view sv_trim(std::string_view str) {
+ while (isspace(str.front())) {
+ str.remove_prefix(1);
+ }
+ while (isspace(str.back())) {
+ str.remove_suffix(1);
+ }
+ return str;
+}
+
+uint32_t LCFilter::recognize_flags(const std::string& flag_expr)
+{
+ uint32_t flags = 0;
+ ceph::split sp_flags(flag_expr); // default separators are ,;=\t\n
+ for (auto it = sp_flags.begin(); it != sp_flags.end(); ++it) {
+ auto token = sv_trim(string_view{*it});
+ for (auto& flag_tok : LCFilter::filter_flags) {
+ if (token == flag_tok.name) {
+ flags |= LCFilter::make_flag(flag_tok.bit);
+ }
+ }
+ }
+ return flags;
+}
+
bool LCRule::valid() const
{
if (id.length() > MAX_ID_LEN) {
#define CEPH_RGW_LC_H
#include <map>
+#include <array>
#include <string>
#include <iostream>
};
WRITE_CLASS_ENCODER(LCTransition)
+enum class LCFlagType : uint16_t
+{
+ none = 0,
+ ArchiveZone,
+};
+
+class LCFlag {
+public:
+ LCFlagType bit;
+ const char* name;
+
+ constexpr LCFlag(LCFlagType ord, const char* name) : bit(ord), name(name)
+ {}
+};
+
class LCFilter
{
- protected:
+ public:
+
+ static constexpr uint32_t make_flag(LCFlagType type) {
+ switch (type) {
+ case LCFlagType::none:
+ return 0;
+ break;
+ default:
+ return 1 << (uint32_t(type) - 1);
+ }
+ }
+
+ static constexpr std::array<LCFlag, 2> filter_flags =
+ {
+ LCFlag(LCFlagType::none, "none"),
+ LCFlag(LCFlagType::ArchiveZone, "ArchiveZone"),
+ };
+
+protected:
std::string prefix;
RGWObjTags obj_tags;
+ uint32_t flags;
- public:
+public:
+static uint32_t recognize_flags(const std::string& flag_expr);
+
+ LCFilter() : flags(make_flag(LCFlagType::none))
+ {}
const std::string& get_prefix() const {
return prefix;
return obj_tags;
}
+ const uint32_t get_flags() {
+ return flags;
+ }
+
bool empty() const {
return !(has_prefix() || has_tags());
}
}
void encode(bufferlist& bl) const {
- ENCODE_START(2, 1, bl);
+ ENCODE_START(3, 1, bl);
encode(prefix, bl);
encode(obj_tags, bl);
+ encode(flags, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::const_iterator& bl) {
- DECODE_START(2, bl);
+ DECODE_START(3, bl);
decode(prefix, bl);
if (struct_v >= 2) {
decode(obj_tags, bl);
+ if (struct_v >= 3) {
+ decode(flags, bl);
+ }
}
DECODE_FINISH(bl);
}
* Empty filters are allowed:
* https://docs.aws.amazon.com/AmazonS3/latest/dev/intro-lifecycle-rules.html
*/
- XMLObj *o = obj->find_first("And");
+ XMLObj* o = obj->find_first("And");
if (o == nullptr){
o = obj;
}
RGWXMLDecoder::decode_xml("Prefix", prefix, o);
+
+ /* parse optional flags (extension) */
+ auto flags_iter = o->find("Flag");
+ while (auto flag_xml = flags_iter.get_next()){
+ flags |= LCFilter::recognize_flags(flag_xml->get_data());
+ }
+
+ obj_tags.clear(); // why is this needed?
auto tags_iter = o->find("Tag");
- obj_tags.clear();
- while (auto tag_xml =tags_iter.get_next()){
+ while (auto tag_xml = tags_iter.get_next()){
std::string _key,_val;
RGWXMLDecoder::decode_xml("Key", _key, tag_xml);
RGWXMLDecoder::decode_xml("Value", _val, tag_xml);
#include "rgw_xml.h"
#include "rgw_tag_s3.h"
+
class LCFilter_S3 : public LCFilter
{
public:
target_link_libraries(unittest_rgw_xml ${rgw_libs} ${EXPAT_LIBRARIES})
+# unittest_rgw_lc
+add_executable(unittest_rgw_lc test_rgw_lc.cc)
+add_ceph_unittest(unittest_rgw_lc)
+target_include_directories(unittest_rgw_lc SYSTEM PRIVATE
+ "${CMAKE_SOURCE_DIR}/src/rgw")
+target_link_libraries(unittest_rgw_lc
+ rgw_common ${rgw_libs} ${EXPAT_LIBRARIES})
+
# unittest_rgw_arn
add_executable(unittest_rgw_arn test_rgw_arn.cc)
add_ceph_unittest(unittest_rgw_arn)
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "rgw_xml.h"
+#include "rgw_lc.h"
+#include "rgw_lc_s3.h"
+#include <gtest/gtest.h>
+//#include <spawn/spawn.hpp>
+#include <string>
+#include <vector>
+#include <stdexcept>
+
+static const char* xmldoc_1 =
+R"(<Filter>
+ <And>
+ <Prefix>tax/</Prefix>
+ <Tag>
+ <Key>key1</Key>
+ <Value>value1</Value>
+ </Tag>
+ <Tag>
+ <Key>key2</Key>
+ <Value>value2</Value>
+ </Tag>
+ </And>
+</Filter>
+)";
+
+TEST(TestLCFilterDecoder, XMLDoc1)
+{
+ RGWXMLDecoder::XMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_TRUE(parser.parse(xmldoc_1, strlen(xmldoc_1), 1));
+ LCFilter_S3 filter;
+ auto result = RGWXMLDecoder::decode_xml("Filter", filter, &parser, true);
+ ASSERT_TRUE(result);
+ /* check repeated Tag element */
+ auto tag_map = filter.get_tags().get_tags();
+ auto val1 = tag_map.find("key1");
+ ASSERT_EQ(val1->second, "value1");
+ auto val2 = tag_map.find("key2");
+ ASSERT_EQ(val2->second, "value2");
+ /* check our flags */
+ ASSERT_EQ(filter.get_flags(), 0);
+}
+
+static const char* xmldoc_2 =
+R"(<Filter>
+ <And>
+ <Flag>
+ ArchiveZone
+ </Flag>
+ <Flag>
+ ArchiveZone
+ </Flag>
+ <Tag>
+ <Key>spongebob</Key>
+ <Value>squarepants</Value>
+ </Tag>
+ </And>
+</Filter>
+)";
+
+TEST(TestLCFilterDecoder, XMLDoc2)
+{
+ RGWXMLDecoder::XMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_TRUE(parser.parse(xmldoc_2, strlen(xmldoc_2), 1));
+ LCFilter_S3 filter;
+ auto result = RGWXMLDecoder::decode_xml("Filter", filter, &parser, true);
+ ASSERT_TRUE(result);
+ /* check repeated Tag element */
+ auto tag_map = filter.get_tags().get_tags();
+ auto val1 = tag_map.find("spongebob");
+ ASSERT_EQ(val1->second, "squarepants");
+ /* check our flags */
+ ASSERT_EQ(filter.get_flags(), uint32_t(LCFlagType::ArchiveZone));
+}
+
+// invalid And element placement
+static const char* xmldoc_3 =
+R"(<Filter>
+ <And>
+ <Tag>
+ <Key>miles</Key>
+ <Value>davis</Value>
+ </Tag>
+ </And>
+ <Tag>
+ <Key>spongebob</Key>
+ <Value>squarepants</Value>
+ </Tag>
+</Filter>
+)";
+
+TEST(TestLCFilterInvalidAnd, XMLDoc3)
+{
+ RGWXMLDecoder::XMLParser parser;
+ ASSERT_TRUE(parser.init());
+ ASSERT_TRUE(parser.parse(xmldoc_3, strlen(xmldoc_3), 1));
+ LCFilter_S3 filter;
+ auto result = RGWXMLDecoder::decode_xml("Filter", filter, &parser, true);
+ ASSERT_TRUE(result);
+ /* check repeated Tag element */
+ auto tag_map = filter.get_tags().get_tags();
+ auto val1 = tag_map.find("spongebob");
+ ASSERT_TRUE(val1 == tag_map.end());
+ /* because the invalid 2nd tag element was not recognized,
+ * we cannot access it:
+ ASSERT_EQ(val1->second, "squarepants");
+ */
+ /* check our flags */
+ ASSERT_EQ(filter.get_flags(), uint32_t(LCFlagType::none));
+}