]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/rgw/test_rgw_lua.cc
bdfe37ef2db25680ab8f6ddcc0acad89bbefcb68
[ceph.git] / ceph / src / test / rgw / test_rgw_lua.cc
1 #include <gtest/gtest.h>
2 #include "common/ceph_context.h"
3 #include "rgw/rgw_common.h"
4 #include "rgw/rgw_process.h"
5 #include "rgw/rgw_sal_rados.h"
6 #include "rgw/rgw_lua_request.h"
7
8 using namespace rgw;
9
10 class CctCleaner {
11 CephContext* cct;
12 public:
13 CctCleaner(CephContext* _cct) : cct(_cct) {}
14 ~CctCleaner() {
15 #ifdef WITH_SEASTAR
16 delete cct;
17 #else
18 cct->put();
19 #endif
20 }
21 };
22
23 class TestRGWUser : public sal::RGWUser {
24 public:
25 virtual int list_buckets(const string&, const string&, uint64_t, bool, sal::RGWBucketList&, optional_yield y) override {
26 return 0;
27 }
28
29 virtual sal::RGWBucket* create_bucket(rgw_bucket& bucket, ceph::real_time creation_time) override {
30 return nullptr;
31 }
32
33 virtual int load_by_id(optional_yield y) override {
34 return 0;
35 }
36
37 virtual ~TestRGWUser() = default;
38 };
39
40 class TestAccounter : public io::Accounter, public io::BasicClient {
41 RGWEnv env;
42
43 protected:
44 virtual int init_env(CephContext *cct) override {
45 return 0;
46 }
47
48 public:
49 ~TestAccounter() = default;
50
51 virtual void set_account(bool enabled) override {
52 }
53
54 virtual uint64_t get_bytes_sent() const override {
55 return 0;
56 }
57
58 virtual uint64_t get_bytes_received() const override {
59 return 0;
60 }
61
62 virtual RGWEnv& get_env() noexcept override {
63 return env;
64 }
65
66 virtual size_t complete_request() override {
67 return 0;
68 }
69 };
70
71 auto cct = new CephContext(CEPH_ENTITY_TYPE_CLIENT);
72
73 CctCleaner cleaner(cct);
74
75 TEST(TestRGWLua, EmptyScript)
76 {
77 const std::string script;
78
79 RGWEnv e;
80 uint64_t id = 0;
81 req_state s(cct, &e, id);
82
83 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", script);
84 ASSERT_EQ(rc, 0);
85 }
86
87 #define DEFINE_REQ_STATE RGWEnv e; req_state s(cct, &e, 0);
88
89 TEST(TestRGWLua, SyntaxError)
90 {
91 const std::string script = R"(
92 if 3 < 5 then
93 RGWDebugLog("missing 'end'")
94 )";
95
96 DEFINE_REQ_STATE;
97
98 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", script);
99 ASSERT_EQ(rc, -1);
100 }
101
102 TEST(TestRGWLua, Hello)
103 {
104 const std::string script = R"(
105 RGWDebugLog("hello from lua")
106 )";
107
108 DEFINE_REQ_STATE;
109
110 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", script);
111 ASSERT_EQ(rc, 0);
112 }
113
114 TEST(TestRGWLua, RGWDebugLogNumber)
115 {
116 const std::string script = R"(
117 RGWDebugLog(1234567890)
118 )";
119
120 DEFINE_REQ_STATE;
121
122 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", script);
123 ASSERT_EQ(rc, 0);
124 }
125
126 TEST(TestRGWLua, RGWDebugNil)
127 {
128 const std::string script = R"(
129 RGWDebugLog(nil)
130 )";
131
132 DEFINE_REQ_STATE;
133
134 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", script);
135 ASSERT_EQ(rc, -1);
136 }
137
138 TEST(TestRGWLua, URI)
139 {
140 const std::string script = R"(
141 RGWDebugLog(Request.DecodedURI)
142 assert(Request.DecodedURI == "http://hello.world/")
143 )";
144
145 DEFINE_REQ_STATE;
146 s.decoded_uri = "http://hello.world/";
147
148 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", script);
149 ASSERT_EQ(rc, 0);
150 }
151
152 TEST(TestRGWLua, Response)
153 {
154 const std::string script = R"(
155 assert(Request.Response.Message == "This is a bad request")
156 assert(Request.Response.HTTPStatus == "Bad Request")
157 assert(Request.Response.RGWCode == 4000)
158 assert(Request.Response.HTTPStatusCode == 400)
159 )";
160
161 DEFINE_REQ_STATE;
162 s.err.http_ret = 400;
163 s.err.ret = 4000;
164 s.err.err_code = "Bad Request";
165 s.err.message = "This is a bad request";
166
167 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
168 ASSERT_EQ(rc, 0);
169 }
170
171 TEST(TestRGWLua, SetResponse)
172 {
173 const std::string script = R"(
174 assert(Request.Response.Message == "this is a bad request")
175 Request.Response.Message = "this is a good request"
176 assert(Request.Response.Message == "this is a good request")
177 )";
178
179 DEFINE_REQ_STATE;
180 s.err.message = "this is a bad request";
181
182 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
183 ASSERT_EQ(rc, 0);
184 }
185
186 TEST(TestRGWLua, SetRGWId)
187 {
188 const std::string script = R"(
189 assert(Request.RGWId == "foo")
190 Request.RGWId = "bar"
191 )";
192
193 DEFINE_REQ_STATE;
194 s.host_id = "foo";
195
196 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
197 ASSERT_NE(rc, 0);
198 }
199
200 TEST(TestRGWLua, InvalidField)
201 {
202 const std::string script = R"(
203 RGWDebugLog(Request.Kaboom)
204 )";
205
206 DEFINE_REQ_STATE;
207 s.host_id = "foo";
208
209 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "kaboom", script);
210 ASSERT_EQ(rc, -1);
211 }
212
213 TEST(TestRGWLua, InvalidSubField)
214 {
215 const std::string script = R"(
216 RGWDebugLog(Request.Error.Kaboom)
217 )";
218
219 DEFINE_REQ_STATE;
220
221 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "kaboom", script);
222 ASSERT_EQ(rc, -1);
223 }
224
225 TEST(TestRGWLua, Bucket)
226 {
227 const std::string script = R"(
228 assert(Request.Bucket)
229 RGWDebugLog("Bucket Id: " .. Request.Bucket.Id)
230 assert(Request.Bucket.Marker == "mymarker")
231 assert(Request.Bucket.Name == "myname")
232 assert(Request.Bucket.Tenant == "mytenant")
233 assert(Request.Bucket.Count == 0)
234 assert(Request.Bucket.Size == 0)
235 assert(Request.Bucket.ZoneGroupId)
236 assert(Request.Bucket.CreationTime)
237 assert(Request.Bucket.MTime)
238 assert(Request.Bucket.Quota.MaxSize == -1)
239 assert(Request.Bucket.Quota.MaxObjects == -1)
240 assert(tostring(Request.Bucket.Quota.Enabled))
241 assert(tostring(Request.Bucket.Quota.Rounded))
242 assert(Request.Bucket.User.Id)
243 assert(Request.Bucket.User.Tenant)
244 )";
245
246 DEFINE_REQ_STATE;
247
248 rgw_bucket b;
249 b.tenant = "mytenant";
250 b.name = "myname";
251 b.marker = "mymarker";
252 b.bucket_id = "myid";
253 s.bucket.reset(new sal::RGWRadosBucket(nullptr, b));
254
255 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
256 ASSERT_EQ(rc, 0);
257 }
258
259 TEST(TestRGWLua, GenericAttributes)
260 {
261 const std::string script = R"(
262 assert(Request.GenericAttributes["hello"] == "world")
263 assert(Request.GenericAttributes["foo"] == "bar")
264 assert(Request.GenericAttributes["kaboom"] == nil)
265 assert(#Request.GenericAttributes == 4)
266 for k, v in pairs(Request.GenericAttributes) do
267 assert(k)
268 assert(v)
269 end
270 )";
271
272 DEFINE_REQ_STATE;
273 s.generic_attrs["hello"] = "world";
274 s.generic_attrs["foo"] = "bar";
275 s.generic_attrs["goodbye"] = "cruel world";
276 s.generic_attrs["ka"] = "boom";
277
278 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
279 ASSERT_EQ(rc, 0);
280 }
281
282 TEST(TestRGWLua, Environment)
283 {
284 const std::string script = R"(
285 assert(Request.Environment[""] == "bar")
286 assert(Request.Environment["goodbye"] == "cruel world")
287 assert(Request.Environment["ka"] == "boom")
288 assert(#Request.Environment == 3, #Request.Environment)
289 for k, v in pairs(Request.Environment) do
290 assert(k)
291 assert(v)
292 end
293 )";
294
295 DEFINE_REQ_STATE;
296 s.env[""] = "world";
297 s.env[""] = "bar";
298 s.env["goodbye"] = "cruel world";
299 s.env["ka"] = "boom";
300
301 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
302 ASSERT_EQ(rc, 0);
303 }
304
305 TEST(TestRGWLua, Tags)
306 {
307 const std::string script = R"(
308 assert(#Request.Tags == 4)
309 assert(Request.Tags["foo"] == "bar")
310 for k, v in pairs(Request.Tags) do
311 assert(k)
312 assert(v)
313 end
314 )";
315
316 DEFINE_REQ_STATE;
317 s.tagset.add_tag("hello", "world");
318 s.tagset.add_tag("foo", "bar");
319 s.tagset.add_tag("goodbye", "cruel world");
320 s.tagset.add_tag("ka", "boom");
321
322 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
323 ASSERT_EQ(rc, 0);
324 }
325
326 TEST(TestRGWLua, TagsNotWriteable)
327 {
328 const std::string script = R"(
329 Request.Tags["hello"] = "goodbye"
330 )";
331
332 DEFINE_REQ_STATE;
333 s.tagset.add_tag("hello", "world");
334
335 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
336 ASSERT_NE(rc, 0);
337 }
338
339 TEST(TestRGWLua, Metadata)
340 {
341 const std::string script = R"(
342 assert(#Request.HTTP.Metadata == 3)
343 for k, v in pairs(Request.HTTP.Metadata) do
344 assert(k)
345 assert(v)
346 end
347 assert(Request.HTTP.Metadata["hello"] == "world")
348 assert(Request.HTTP.Metadata["kaboom"] == nil)
349 Request.HTTP.Metadata["hello"] = "goodbye"
350 Request.HTTP.Metadata["kaboom"] = "boom"
351 assert(#Request.HTTP.Metadata == 4)
352 assert(Request.HTTP.Metadata["hello"] == "goodbye")
353 assert(Request.HTTP.Metadata["kaboom"] == "boom")
354 )";
355
356 DEFINE_REQ_STATE;
357 s.info.x_meta_map["hello"] = "world";
358 s.info.x_meta_map["foo"] = "bar";
359 s.info.x_meta_map["ka"] = "boom";
360
361 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
362 ASSERT_EQ(rc, 0);
363 }
364
365 TEST(TestRGWLua, Acl)
366 {
367 const std::string script = R"(
368 function print_grant(g)
369 print("Grant Type: " .. g.Type)
370 print("Grant Group Type: " .. g.GroupType)
371 print("Grant Referer: " .. g.Referer)
372 if (g.User) then
373 print("Grant User.Tenant: " .. g.User.Tenant)
374 print("Grant User.Id: " .. g.User.Id)
375 end
376 end
377
378 assert(Request.UserAcl.Owner.DisplayName == "jack black", Request.UserAcl.Owner.DisplayName)
379 assert(Request.UserAcl.Owner.User.Id == "black", Request.UserAcl.Owner.User.Id)
380 assert(Request.UserAcl.Owner.User.Tenant == "jack", Request.UserAcl.Owner.User.Tenant)
381 assert(#Request.UserAcl.Grants == 5)
382 print_grant(Request.UserAcl.Grants[""])
383 for k, v in pairs(Request.UserAcl.Grants) do
384 print_grant(v)
385 if k == "john$doe" then
386 assert(v.Permission == 4)
387 elseif k == "jane$doe" then
388 assert(v.Permission == 1)
389 else
390 assert(false)
391 end
392 end
393 )";
394
395 DEFINE_REQ_STATE;
396 ACLOwner owner;
397 owner.set_id(rgw_user("jack", "black"));
398 owner.set_name("jack black");
399 s.user_acl.reset(new RGWAccessControlPolicy(cct));
400 s.user_acl->set_owner(owner);
401 ACLGrant grant1, grant2, grant3, grant4, grant5;
402 grant1.set_canon(rgw_user("jane", "doe"), "her grant", 1);
403 grant2.set_group(ACL_GROUP_ALL_USERS ,2);
404 grant3.set_referer("http://localhost/ref2", 3);
405 grant4.set_canon(rgw_user("john", "doe"), "his grant", 4);
406 grant5.set_group(ACL_GROUP_AUTHENTICATED_USERS, 5);
407 s.user_acl->get_acl().add_grant(&grant1);
408 s.user_acl->get_acl().add_grant(&grant2);
409 s.user_acl->get_acl().add_grant(&grant3);
410 s.user_acl->get_acl().add_grant(&grant4);
411 s.user_acl->get_acl().add_grant(&grant5);
412 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
413 ASSERT_EQ(rc, 0);
414 }
415
416 TEST(TestRGWLua, UseFunction)
417 {
418 const std::string script = R"(
419 function print_owner(owner)
420 print("Owner Dispaly Name: " .. owner.DisplayName)
421 print("Owner Id: " .. owner.User.Id)
422 print("Owner Tenanet: " .. owner.User.Tenant)
423 end
424
425 print_owner(Request.ObjectOwner)
426
427 function print_acl(acl_type)
428 index = acl_type .. "ACL"
429 acl = Request[index]
430 if acl then
431 print(acl_type .. "ACL Owner")
432 print_owner(acl.Owner)
433 else
434 print("no " .. acl_type .. " ACL in request: " .. Request.Id)
435 end
436 end
437
438 print_acl("User")
439 print_acl("Bucket")
440 print_acl("Object")
441 )";
442
443 DEFINE_REQ_STATE;
444 s.owner.set_name("user two");
445 s.owner.set_id(rgw_user("tenant2", "user2"));
446 s.user_acl.reset(new RGWAccessControlPolicy());
447 s.user_acl->get_owner().set_name("user three");
448 s.user_acl->get_owner().set_id(rgw_user("tenant3", "user3"));
449 s.bucket_acl.reset(new RGWAccessControlPolicy());
450 s.bucket_acl->get_owner().set_name("user four");
451 s.bucket_acl->get_owner().set_id(rgw_user("tenant4", "user4"));
452 s.object_acl.reset(new RGWAccessControlPolicy());
453 s.object_acl->get_owner().set_name("user five");
454 s.object_acl->get_owner().set_id(rgw_user("tenant5", "user5"));
455
456 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
457 ASSERT_EQ(rc, 0);
458 }
459
460 TEST(TestRGWLua, WithLib)
461 {
462 const std::string script = R"(
463 expected_result = {"my", "bucket", "name", "is", "fish"}
464 i = 1
465 for p in string.gmatch(Request.Bucket.Name, "%a+") do
466 assert(p == expected_result[i])
467 i = i + 1
468 end
469 )";
470
471 DEFINE_REQ_STATE;
472
473 rgw_bucket b;
474 b.name = "my-bucket-name-is-fish";
475 s.bucket.reset(new sal::RGWRadosBucket(nullptr, b));
476
477 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
478 ASSERT_EQ(rc, 0);
479 }
480
481 TEST(TestRGWLua, NotAllowedInLib)
482 {
483 const std::string script = R"(
484 os.clock() -- this should be ok
485 os.exit() -- this should fail (os.exit() is removed)
486 )";
487
488 DEFINE_REQ_STATE;
489
490 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
491 ASSERT_NE(rc, 0);
492 }
493 #include <sys/socket.h>
494 #include <stdlib.h>
495
496 bool unix_socket_client_ended_ok = false;
497
498 void unix_socket_client(const std::string& path) {
499 int fd;
500 // create the socket
501 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
502 std::cout << "unix socket error: " << errno << std::endl;
503 return;
504 }
505 // set the path
506 struct sockaddr_un addr;
507 memset(&addr, 0, sizeof(addr));
508 addr.sun_family = AF_UNIX;
509 strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path)-1);
510
511 // let the socket be created by the "rgw" side
512 std::this_thread::sleep_for(std::chrono::seconds(2));
513 if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
514 std::cout << "unix socket connect error: " << errno << std::endl;
515 return;
516 }
517
518 char buff[256];
519 int rc;
520 while((rc=read(fd, buff, sizeof(buff))) > 0) {
521 std::cout << std::string(buff, rc);
522 unix_socket_client_ended_ok = true;
523 }
524 }
525
526 TEST(TestRGWLua, OpsLog)
527 {
528 const std::string unix_socket_path = "./aSocket.sock";
529 unlink(unix_socket_path.c_str());
530
531 std::thread unix_socket_thread(unix_socket_client, unix_socket_path);
532
533 const std::string script = R"(
534 if Request.Response.HTTPStatusCode == 200 then
535 assert(Request.Response.Message == "Life is great")
536 else
537 assert(Request.Bucket)
538 assert(Request.Log() == 0)
539 end
540 )";
541
542 auto store = std::unique_ptr<sal::RGWRadosStore>(new sal::RGWRadosStore);
543 store->setRados(new RGWRados);
544 auto olog = std::unique_ptr<OpsLogSocket>(new OpsLogSocket(cct, 1024));
545 ASSERT_TRUE(olog->init(unix_socket_path));
546
547 DEFINE_REQ_STATE;
548 s.err.http_ret = 200;
549 s.err.ret = 0;
550 s.err.err_code = "200OK";
551 s.err.message = "Life is great";
552 rgw_bucket b;
553 b.tenant = "tenant";
554 b.name = "name";
555 b.marker = "marker";
556 b.bucket_id = "id";
557 s.bucket.reset(new sal::RGWRadosBucket(nullptr, b));
558 s.bucket_name = "name";
559 s.enable_ops_log = true;
560 s.enable_usage_log = false;
561 s.user.reset(new TestRGWUser());
562 TestAccounter ac;
563 s.cio = &ac;
564 s.cct->_conf->rgw_ops_log_rados = false;
565
566 auto rc = lua::request::execute(store.get(), nullptr, olog.get(), &s, "put_obj", script);
567 EXPECT_EQ(rc, 0);
568
569 s.err.http_ret = 400;
570 rc = lua::request::execute(store.get(), nullptr, olog.get(), &s, "put_obj", script);
571 EXPECT_EQ(rc, 0);
572
573 // give the socket client time to read
574 std::this_thread::sleep_for(std::chrono::seconds(5));
575 unix_socket_thread.detach(); // read is stuck there, so we cannot join
576 EXPECT_TRUE(unix_socket_client_ended_ok);
577 }
578