]>
git.proxmox.com Git - ceph.git/blob - ceph/src/test/confutils.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2011 New Dream Network
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
14 #include "common/ConfUtils.h"
15 #include "common/config_proxy.h"
16 #include "common/errno.h"
17 #include "gtest/gtest.h"
18 #include "include/buffer.h"
20 #if __has_include(<filesystem>)
22 namespace fs
= std::filesystem
;
23 #elif __has_include(<experimental/filesystem>)
24 #include <experimental/filesystem>
25 namespace fs
= std::experimental::filesystem
;
34 #include <sys/types.h>
36 using ceph::bufferlist
;
38 using std::ostringstream
;
40 #define MAX_FILES_TO_DELETE 1000UL
42 static size_t config_idx
= 0;
43 static size_t unlink_idx
= 0;
44 static char *to_unlink
[MAX_FILES_TO_DELETE
];
46 static std::string
get_temp_dir()
48 static std::string temp_dir
;
50 if (temp_dir
.empty()) {
51 const char *tmpdir
= getenv("TMPDIR");
56 oss
<< tmpdir
<< "/confutils_test_dir." << rand() << "." << getpid();
58 if (!fs::exists(oss
.str())) {
60 if (!fs::create_directory(oss
.str(), ec
)) {
61 cerr
<< "failed to create temp directory '" << temp_dir
<< "' "
62 << ec
.message() << std::endl
;
65 fs::permissions(oss
.str(), fs::perms::sticky_bit
| fs::perms::all
);
72 static void unlink_all(void)
74 for (size_t i
= 0; i
< unlink_idx
; ++i
) {
77 for (size_t i
= 0; i
< unlink_idx
; ++i
) {
80 rmdir(get_temp_dir().c_str());
83 static int create_tempfile(const std::string
&fname
, const char *text
)
85 FILE *fp
= fopen(fname
.c_str(), "w");
88 cerr
<< "Failed to write file '" << fname
<< "' to temp directory '"
89 << get_temp_dir() << "'. " << cpp_strerror(err
) << std::endl
;
92 std::shared_ptr
<FILE> fpp(fp
, fclose
);
93 if (unlink_idx
>= MAX_FILES_TO_DELETE
)
95 if (unlink_idx
== 0) {
96 memset(to_unlink
, 0, sizeof(to_unlink
));
99 to_unlink
[unlink_idx
++] = strdup(fname
.c_str());
100 size_t strlen_text
= strlen(text
);
101 size_t res
= fwrite(text
, 1, strlen_text
, fp
);
102 if (res
!= strlen_text
) {
104 cerr
<< "fwrite error while writing to " << fname
105 << ": " << cpp_strerror(err
) << std::endl
;
111 static std::string
next_tempfile(const char *text
)
114 std::string
temp_dir(get_temp_dir());
115 if (temp_dir
.empty())
117 oss
<< temp_dir
<< "/test_config." << config_idx
++ << ".config";
118 int ret
= create_tempfile(oss
.str(), text
);
124 const char * const trivial_conf_1
= "";
126 const char * const trivial_conf_2
= "log dir = foobar";
128 const char * const trivial_conf_3
= "log dir = barfoo\n";
130 const char * const trivial_conf_4
= "log dir = \"barbaz\"\n";
132 const char * const simple_conf_1
= "\
133 ; here's a comment\n\
135 keyring = .my_ceph_keyring\n\
139 log per instance = true\n\
140 log sym history = 100\n\
141 profiling logger = true\n\
142 profiling logger dir = wowsers\n\
144 pid file = out/$name.pid\n\
146 mds debug frag = true\n\
148 pid file = out/$name.pid\n\
149 osd scrub load threshold = 5.0\n\
153 osd data = dev/osd0\n\
154 osd journal size = 100\n\
160 // we can add whitespace at odd locations and it will get stripped out.
161 const char * const simple_conf_2
= "\
163 log dir = special_mds_a\n\
165 log sym history = 100\n\
166 log dir = out # after a comment, anything # can ### happen ;;; right?\n\
167 log per instance = true\n\
168 profiling logger = true\n\
169 profiling logger dir = log\n\
173 keyring = osd_keyring ; osd's keyring\n\
177 # I like pound signs as comment markers.\n\
178 ; Do you like pound signs as comment markers?\n\
179 keyring = shenanigans ; The keyring of a leprechaun\n\
181 # Let's just have a line with a lot of whitespace and nothing else.\n\
186 // test line-combining
187 const char * const conf3
= "\
189 log file = /quite/a/long/path\\\n\
194 [mon] #nothing here \n\
197 const char * const escaping_conf_1
= "\
199 log file = the \"scare quotes\"\n\
203 keyring = \"nested \\\"quotes\\\"\"\n\
206 const char * const escaping_conf_2
= "\
208 log file = floppy disk\n\
210 keyring = \"backslash\\\\\"\n\
213 // illegal because it contains an invalid utf8 sequence.
214 const char illegal_conf1
[] = "\
217 pid file = invalid-utf-\xe2\x28\xa1\n\
219 keyring = osd_keyring ; osd's keyring\n\
222 // illegal because it contains a malformed section header.
223 const char illegal_conf2
[] = "\
227 keyring = osd_keyring ; osd's keyring\n\
230 // illegal because it contains a line that doesn't parse
231 const char illegal_conf3
[] = "\
235 keyring = osd_keyring ; osd's keyring\n\
238 // illegal because it has unterminated quotes
239 const char illegal_conf4
[] = "\
241 keyring = \"unterminated quoted string\n\
243 keyring = osd_keyring ; osd's keyring\n\
246 const char override_config_1
[] = "\
248 log file = global_log\n\
250 log file = mds_log\n\
252 log file = osd_log\n\
254 log file = osd0_log\n\
257 const char dup_key_config_1
[] = "\
263 TEST(ConfUtils
, ParseFiles0
) {
267 std::ostringstream err
;
268 std::string
trivial_conf_1_f(next_tempfile(trivial_conf_1
));
270 ASSERT_EQ(cf1
.parse_file(trivial_conf_1_f
.c_str(), &err
), 0);
271 ASSERT_EQ(err
.tellp(), 0U);
274 std::ostringstream err
;
275 std::string
trivial_conf_2_f(next_tempfile(trivial_conf_2
));
277 ASSERT_EQ(cf2
.parse_file(trivial_conf_2_f
.c_str(), &err
), -EINVAL
);
278 ASSERT_GT(err
.tellp(), 0U);
281 std::ostringstream err
;
283 bl3
.append(trivial_conf_3
, strlen(trivial_conf_3
));
285 ASSERT_EQ(cf3
.parse_bufferlist(&bl3
, &err
), 0);
286 ASSERT_EQ(err
.tellp(), 0U);
287 ASSERT_EQ(cf3
.read("global", "log dir", val
), 0);
288 ASSERT_EQ(val
, "barfoo");
291 std::ostringstream err
;
292 std::string
trivial_conf_4_f(next_tempfile(trivial_conf_4
));
294 ASSERT_EQ(cf4
.parse_file(trivial_conf_4_f
.c_str(), &err
), 0);
295 ASSERT_EQ(err
.tellp(), 0U);
296 ASSERT_EQ(cf4
.read("global", "log dir", val
), 0);
297 ASSERT_EQ(val
, "barbaz");
301 TEST(ConfUtils
, ParseFiles1
) {
302 std::ostringstream err
;
303 std::string
simple_conf_1_f(next_tempfile(simple_conf_1
));
305 ASSERT_EQ(cf1
.parse_file(simple_conf_1_f
.c_str(), &err
), 0);
306 ASSERT_EQ(err
.tellp(), 0U);
308 std::string
simple_conf_2_f(next_tempfile(simple_conf_1
));
310 ASSERT_EQ(cf2
.parse_file(simple_conf_2_f
.c_str(), &err
), 0);
311 ASSERT_EQ(err
.tellp(), 0U);
314 bl3
.append(simple_conf_1
, strlen(simple_conf_1
));
316 ASSERT_EQ(cf3
.parse_bufferlist(&bl3
, &err
), 0);
317 ASSERT_EQ(err
.tellp(), 0U);
320 bl4
.append(simple_conf_2
, strlen(simple_conf_2
));
322 ASSERT_EQ(cf4
.parse_bufferlist(&bl4
, &err
), 0);
323 ASSERT_EQ(err
.tellp(), 0U);
326 TEST(ConfUtils
, ReadFiles1
) {
327 std::ostringstream err
;
328 std::string
simple_conf_1_f(next_tempfile(simple_conf_1
));
330 ASSERT_EQ(cf1
.parse_file(simple_conf_1_f
.c_str(), &err
), 0);
331 ASSERT_EQ(err
.tellp(), 0U);
334 ASSERT_EQ(cf1
.read("global", "keyring", val
), 0);
335 ASSERT_EQ(val
, ".my_ceph_keyring");
337 ASSERT_EQ(cf1
.read("mds", "profiling logger dir", val
), 0);
338 ASSERT_EQ(val
, "wowsers");
340 ASSERT_EQ(cf1
.read("mds", "something that does not exist", val
), -ENOENT
);
342 // exists in mds section, but not in global
343 ASSERT_EQ(cf1
.read("global", "profiling logger dir", val
), -ENOENT
);
346 bl2
.append(simple_conf_2
, strlen(simple_conf_2
));
348 ASSERT_EQ(cf2
.parse_bufferlist(&bl2
, &err
), 0);
349 ASSERT_EQ(err
.tellp(), 0U);
350 ASSERT_EQ(cf2
.read("osd0", "keyring", val
), 0);
351 ASSERT_EQ(val
, "osd_keyring");
353 ASSERT_EQ(cf2
.read("mds", "pid file", val
), 0);
354 ASSERT_EQ(val
, "foo2");
355 ASSERT_EQ(cf2
.read("nonesuch", "keyring", val
), -ENOENT
);
358 TEST(ConfUtils
, ReadFiles2
) {
359 std::ostringstream err
;
360 std::string
conf3_f(next_tempfile(conf3
));
363 ASSERT_EQ(cf1
.parse_file(conf3_f
.c_str(), &err
), 0);
364 ASSERT_EQ(err
.tellp(), 0U);
365 ASSERT_EQ(cf1
.read("global", "log file", val
), 0);
366 ASSERT_EQ(val
, "/quite/a/long/path/for/a/log/file");
367 ASSERT_EQ(cf1
.read("global", "pid file", val
), 0);
368 ASSERT_EQ(val
, "spork");
371 TEST(ConfUtils
, IllegalFiles
) {
373 std::ostringstream err
;
375 std::string
illegal_conf1_f(next_tempfile(illegal_conf1
));
376 ASSERT_EQ(cf1
.parse_file(illegal_conf1_f
.c_str(), &err
), -EINVAL
);
377 ASSERT_GT(err
.tellp(), 0U);
380 std::ostringstream err
;
382 bl2
.append(illegal_conf2
, strlen(illegal_conf2
));
384 ASSERT_EQ(cf2
.parse_bufferlist(&bl2
, &err
), -EINVAL
);
385 ASSERT_GT(err
.tellp(), 0U);
388 std::ostringstream err
;
389 std::string
illegal_conf3_f(next_tempfile(illegal_conf3
));
391 ASSERT_EQ(cf3
.parse_file(illegal_conf3_f
.c_str(), &err
), -EINVAL
);
392 ASSERT_GT(err
.tellp(), 0U);
395 std::ostringstream err
;
396 std::string
illegal_conf4_f(next_tempfile(illegal_conf4
));
398 ASSERT_EQ(cf4
.parse_file(illegal_conf4_f
.c_str(), &err
), -EINVAL
);
399 ASSERT_GT(err
.tellp(), 0U);
403 TEST(ConfUtils
, EscapingFiles
) {
404 std::ostringstream err
;
405 std::string
escaping_conf_1_f(next_tempfile(escaping_conf_1
));
408 ASSERT_EQ(cf1
.parse_file(escaping_conf_1_f
.c_str(), &err
), 0);
409 ASSERT_EQ(err
.tellp(), 0U);
411 ASSERT_EQ(cf1
.read("global", "log file", val
), 0);
412 ASSERT_EQ(val
, "the \"scare quotes\"");
413 ASSERT_EQ(cf1
.read("global", "pid file", val
), 0);
414 ASSERT_EQ(val
, "a pid file");
415 ASSERT_EQ(cf1
.read("mon", "keyring", val
), 0);
416 ASSERT_EQ(val
, "nested \"quotes\"");
418 std::string
escaping_conf_2_f(next_tempfile(escaping_conf_2
));
420 ASSERT_EQ(cf2
.parse_file(escaping_conf_2_f
.c_str(), &err
), 0);
421 ASSERT_EQ(err
.tellp(), 0U);
423 ASSERT_EQ(cf2
.read("apple ][", "log file", val
), 0);
424 ASSERT_EQ(val
, "floppy disk");
425 ASSERT_EQ(cf2
.read("mon", "keyring", val
), 0);
426 ASSERT_EQ(val
, "backslash\\");
429 TEST(ConfUtils
, Overrides
) {
430 ConfigProxy conf
{false};
431 std::ostringstream warn
;
432 std::string
override_conf_1_f(next_tempfile(override_config_1
));
434 conf
->name
.set(CEPH_ENTITY_TYPE_MON
, "0");
435 conf
.parse_config_files(override_conf_1_f
.c_str(), &warn
, 0);
436 ASSERT_FALSE(conf
.has_parse_error());
437 ASSERT_EQ(conf
->log_file
, "global_log");
439 conf
->name
.set(CEPH_ENTITY_TYPE_MDS
, "a");
440 conf
.parse_config_files(override_conf_1_f
.c_str(), &warn
, 0);
441 ASSERT_FALSE(conf
.has_parse_error());
442 ASSERT_EQ(conf
->log_file
, "mds_log");
444 conf
->name
.set(CEPH_ENTITY_TYPE_OSD
, "0");
445 conf
.parse_config_files(override_conf_1_f
.c_str(), &warn
, 0);
446 ASSERT_FALSE(conf
.has_parse_error());
447 ASSERT_EQ(conf
->log_file
, "osd0_log");
450 TEST(ConfUtils
, DupKey
) {
451 ConfigProxy conf
{false};
452 std::ostringstream warn
;
453 std::string
dup_key_config_f(next_tempfile(dup_key_config_1
));
455 conf
->name
.set(CEPH_ENTITY_TYPE_MDS
, "a");
456 conf
.parse_config_files(dup_key_config_f
.c_str(), &warn
, 0);
457 ASSERT_FALSE(conf
.has_parse_error());
458 ASSERT_EQ(conf
->log_file
, string("3"));