]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/confutils.cc
bump version to 12.2.12-pve1
[ceph.git] / ceph / src / test / confutils.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2011 New Dream Network
7 *
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.
12 *
13 */
14#include "common/ConfUtils.h"
15#include "common/config.h"
16#include "common/errno.h"
17#include "gtest/gtest.h"
18#include "include/buffer.h"
19
20#include <errno.h>
21#include <iostream>
22#include <stdlib.h>
23#include <sstream>
24#include <stdint.h>
25#include <sys/stat.h>
26#include <sys/types.h>
27#include "include/memory.h"
28
29using ceph::bufferlist;
30using std::cerr;
31using std::ostringstream;
32
33#define MAX_FILES_TO_DELETE 1000UL
34
35static size_t config_idx = 0;
36static size_t unlink_idx = 0;
37static char *to_unlink[MAX_FILES_TO_DELETE];
38
39static std::string get_temp_dir()
40{
41 static std::string temp_dir;
42
43 if (temp_dir.empty()) {
44 const char *tmpdir = getenv("TMPDIR");
45 if (!tmpdir)
46 tmpdir = "/tmp";
47 srand(time(NULL));
48 ostringstream oss;
49 oss << tmpdir << "/confutils_test_dir." << rand() << "." << getpid();
50 umask(022);
51 int res = mkdir(oss.str().c_str(), 01777);
52 if (res) {
53 cerr << "failed to create temp directory '" << temp_dir << "'" << std::endl;
54 return "";
55 }
56 temp_dir = oss.str();
57 }
58 return temp_dir;
59}
60
61static void unlink_all(void)
62{
63 for (size_t i = 0; i < unlink_idx; ++i) {
64 unlink(to_unlink[i]);
65 }
66 for (size_t i = 0; i < unlink_idx; ++i) {
67 free(to_unlink[i]);
68 }
69 rmdir(get_temp_dir().c_str());
70}
71
72static int create_tempfile(const std::string &fname, const char *text)
73{
74 FILE *fp = fopen(fname.c_str(), "w");
75 if (!fp) {
76 int err = errno;
77 cerr << "Failed to write file '" << fname << "' to temp directory '"
78 << get_temp_dir() << "'. " << cpp_strerror(err) << std::endl;
79 return err;
80 }
81 ceph::shared_ptr<FILE> fpp(fp, fclose);
82 if (unlink_idx >= MAX_FILES_TO_DELETE)
83 return -ENOBUFS;
84 if (unlink_idx == 0) {
85 memset(to_unlink, 0, sizeof(to_unlink));
86 atexit(unlink_all);
87 }
88 to_unlink[unlink_idx++] = strdup(fname.c_str());
89 size_t strlen_text = strlen(text);
90 size_t res = fwrite(text, 1, strlen_text, fp);
91 if (res != strlen_text) {
92 int err = errno;
93 cerr << "fwrite error while writing to " << fname
94 << ": " << cpp_strerror(err) << std::endl;
95 return err;
96 }
97 return 0;
98}
99
100static std::string next_tempfile(const char *text)
101{
102 ostringstream oss;
103 std::string temp_dir(get_temp_dir());
104 if (temp_dir.empty())
105 return "";
106 oss << temp_dir << "/test_config." << config_idx++ << ".config";
107 int ret = create_tempfile(oss.str(), text);
108 if (ret)
109 return "";
110 return oss.str();
111}
112
113const char * const trivial_conf_1 = "";
114
115const char * const trivial_conf_2 = "log dir = foobar";
116
117const char * const trivial_conf_3 = "log dir = barfoo\n";
118
119const char * const trivial_conf_4 = "log dir = \"barbaz\"\n";
120
121const char * const simple_conf_1 = "\
122; here's a comment\n\
123[global]\n\
124 keyring = .my_ceph_keyring\n\
125\n\
126[mds]\n\
127 log dir = out\n\
128 log per instance = true\n\
129 log sym history = 100\n\
130 profiling logger = true\n\
131 profiling logger dir = wowsers\n\
132 chdir = ""\n\
133 pid file = out/$name.pid\n\
134\n\
135 mds debug frag = true\n\
136[osd]\n\
137 pid file = out/$name.pid\n\
138 osd scrub load threshold = 5.0\n\
139\n\
140 lockdep = 1\n\
141[osd0]\n\
142 osd data = dev/osd0\n\
143 osd journal size = 100\n\
144[mds.a]\n\
145[mds.b]\n\
146[mds.c]\n\
147";
148
149// we can add whitespace at odd locations and it will get stripped out.
150const char * const simple_conf_2 = "\
151[mds.a]\n\
152 log dir = special_mds_a\n\
153[mds]\n\
154 log sym history = 100\n\
155 log dir = out # after a comment, anything # can ### happen ;;; right?\n\
156 log per instance = true\n\
157 profiling logger = true\n\
158 profiling logger dir = log\n\
159 chdir = ""\n\
160 pid file\t=\tfoo2\n\
161[osd0]\n\
162 keyring = osd_keyring ; osd's keyring\n\
163\n\
164 \n\
165[global]\n\
166 # I like pound signs as comment markers.\n\
167 ; Do you like pound signs as comment markers?\n\
168 keyring = shenanigans ; The keyring of a leprechaun\n\
169\n\
170 # Let's just have a line with a lot of whitespace and nothing else.\n\
171 \n\
172 lockdep = 1\n\
173";
174
175// test line-combining
176const char * const conf3 = "\
177[global]\n\
178 log file = /quite/a/long/path\\\n\
179/for/a/log/file\n\
180 pid file = \\\n\
181 spork\\\n\
182\n\
183[mon] #nothing here \n\
184";
185
186const char * const escaping_conf_1 = "\
187[global]\n\
188 log file = the \"scare quotes\"\n\
189 pid file = a \\\n\
190pid file\n\
191[mon]\n\
192 keyring = \"nested \\\"quotes\\\"\"\n\
193";
194
195const char * const escaping_conf_2 = "\
196[apple \\]\\[]\n\
197 log file = floppy disk\n\
198[mon]\n\
199 keyring = \"backslash\\\\\"\n\
200";
201
202// illegal because it contains an invalid utf8 sequence.
203const char illegal_conf1[] = "\
204[global]\n\
205 log file = foo\n\
206 pid file = invalid-utf-\xe2\x28\xa1\n\
207[osd0]\n\
208 keyring = osd_keyring ; osd's keyring\n\
209";
210
211// illegal because it contains a malformed section header.
212const char illegal_conf2[] = "\
213[global\n\
214 log file = foo\n\
215[osd0]\n\
216 keyring = osd_keyring ; osd's keyring\n\
217";
218
219// illegal because it contains a line that doesn't parse
220const char illegal_conf3[] = "\
221[global]\n\
222 who_what_where\n\
223[osd0]\n\
224 keyring = osd_keyring ; osd's keyring\n\
225";
226
227// illegal because it has unterminated quotes
228const char illegal_conf4[] = "\
229[global]\n\
230 keyring = \"unterminated quoted string\n\
231[osd0]\n\
232 keyring = osd_keyring ; osd's keyring\n\
233";
234
235// illegal because it has a backslash at the very end
236const char illegal_conf5[] = "\
237[global]\n\
238 keyring = something awful\\\\\n\
239";
240
241// unicode config file
242const char unicode_config_1[] = "\
243[global]\n\
244 log file = \x66\xd1\x86\xd1\x9d\xd3\xad\xd3\xae \n\
245 pid file = foo-bar\n\
246[osd0]\n\
247";
248
249const char override_config_1[] = "\
250[global]\n\
251 log file = global_log\n\
252[mds]\n\
253 log file = mds_log\n\
254[osd]\n\
255 log file = osd_log\n\
256[osd.0]\n\
257 log file = osd0_log\n\
258";
259
260const char dup_key_config_1[] = "\
261[mds.a]\n\
262 log_file = 1\n\
263 log_file = 3\n\
264";
265
266TEST(ConfUtils, Whitespace) {
267 std::string test0("");
268 ConfFile::trim_whitespace(test0, false);
269 ASSERT_EQ(test0, "");
270
271 std::string test0a("");
272 ConfFile::trim_whitespace(test0a, true);
273 ASSERT_EQ(test0a, "");
274
275 std::string test0b(" ");
276 ConfFile::trim_whitespace(test0b, false);
277 ASSERT_EQ(test0b, "");
278
279 std::string test0c(" ");
280 ConfFile::trim_whitespace(test0c, true);
281 ASSERT_EQ(test0c, "");
282
283 std::string test1(" abc ");
284 ConfFile::trim_whitespace(test1, false);
285 ASSERT_EQ(test1, "abc");
286
287 std::string test2(" abc d ");
288 ConfFile::trim_whitespace(test2, true);
289 ASSERT_EQ(test2, "abc d");
290
291 std::string test3(" abc d ");
292 ConfFile::trim_whitespace(test3, false);
293 ASSERT_EQ(test3, "abc d");
294
295 std::string test4("abcd");
296 ConfFile::trim_whitespace(test4, false);
297 ASSERT_EQ(test4, "abcd");
298
299 std::string test5("abcd");
300 ConfFile::trim_whitespace(test5, true);
301 ASSERT_EQ(test5, "abcd");
302}
303
304TEST(ConfUtils, ParseFiles0) {
305 std::deque<std::string> err;
306 std::string val;
307 std::ostringstream warn;
308
309 std::string trivial_conf_1_f(next_tempfile(trivial_conf_1));
310 ConfFile cf1;
311 ASSERT_EQ(cf1.parse_file(trivial_conf_1_f.c_str(), &err, &warn), 0);
312 ASSERT_EQ(err.size(), 0U);
313
314 std::string trivial_conf_2_f(next_tempfile(trivial_conf_2));
315 ConfFile cf2;
316 ASSERT_EQ(cf2.parse_file(trivial_conf_2_f.c_str(), &err, &warn), 0);
317 ASSERT_EQ(err.size(), 1U);
318
319 bufferlist bl3;
320 bl3.append(trivial_conf_3, strlen(trivial_conf_3));
321 ConfFile cf3;
322 ASSERT_EQ(cf3.parse_bufferlist(&bl3, &err, &warn), 0);
323 ASSERT_EQ(err.size(), 0U);
324 ASSERT_EQ(cf3.read("global", "log dir", val), 0);
325 ASSERT_EQ(val, "barfoo");
326
327 std::string trivial_conf_4_f(next_tempfile(trivial_conf_4));
328 ConfFile cf4;
329 ASSERT_EQ(cf4.parse_file(trivial_conf_4_f.c_str(), &err, &warn), 0);
330 ASSERT_EQ(err.size(), 0U);
331 ASSERT_EQ(cf4.read("global", "log dir", val), 0);
332 ASSERT_EQ(val, "barbaz");
333}
334
335TEST(ConfUtils, ParseFiles1) {
336 std::deque<std::string> err;
337 std::ostringstream warn;
338 std::string simple_conf_1_f(next_tempfile(simple_conf_1));
339 ConfFile cf1;
340 ASSERT_EQ(cf1.parse_file(simple_conf_1_f.c_str(), &err, &warn), 0);
341 ASSERT_EQ(err.size(), 0U);
342
343 std::string simple_conf_2_f(next_tempfile(simple_conf_1));
344 ConfFile cf2;
345 ASSERT_EQ(cf2.parse_file(simple_conf_2_f.c_str(), &err, &warn), 0);
346 ASSERT_EQ(err.size(), 0U);
347
348 bufferlist bl3;
349 bl3.append(simple_conf_1, strlen(simple_conf_1));
350 ConfFile cf3;
351 ASSERT_EQ(cf3.parse_bufferlist(&bl3, &err, &warn), 0);
352 ASSERT_EQ(err.size(), 0U);
353
354 bufferlist bl4;
355 bl4.append(simple_conf_2, strlen(simple_conf_2));
356 ConfFile cf4;
357 ASSERT_EQ(cf4.parse_bufferlist(&bl4, &err, &warn), 0);
358 ASSERT_EQ(err.size(), 0U);
359}
360
361TEST(ConfUtils, ReadFiles1) {
362 std::deque<std::string> err;
363 std::ostringstream warn;
364 std::string simple_conf_1_f(next_tempfile(simple_conf_1));
365 ConfFile cf1;
366 ASSERT_EQ(cf1.parse_file(simple_conf_1_f.c_str(), &err, &warn), 0);
367 ASSERT_EQ(err.size(), 0U);
368
369 std::string val;
370 ASSERT_EQ(cf1.read("global", "keyring", val), 0);
371 ASSERT_EQ(val, ".my_ceph_keyring");
372
373 ASSERT_EQ(cf1.read("mds", "profiling logger dir", val), 0);
374 ASSERT_EQ(val, "wowsers");
375
376 ASSERT_EQ(cf1.read("mds", "something that does not exist", val), -ENOENT);
377
378 // exists in mds section, but not in global
379 ASSERT_EQ(cf1.read("global", "profiling logger dir", val), -ENOENT);
380
381 bufferlist bl2;
382 bl2.append(simple_conf_2, strlen(simple_conf_2));
383 ConfFile cf2;
384 ASSERT_EQ(cf2.parse_bufferlist(&bl2, &err, &warn), 0);
385 ASSERT_EQ(err.size(), 0U);
386 ASSERT_EQ(cf2.read("osd0", "keyring", val), 0);
387 ASSERT_EQ(val, "osd_keyring");
388
389 ASSERT_EQ(cf2.read("mds", "pid file", val), 0);
390 ASSERT_EQ(val, "foo2");
391 ASSERT_EQ(cf2.read("nonesuch", "keyring", val), -ENOENT);
392}
393
394TEST(ConfUtils, ReadFiles2) {
395 std::deque<std::string> err;
396 std::ostringstream warn;
397 std::string conf3_f(next_tempfile(conf3));
398 ConfFile cf1;
399 std::string val;
400 ASSERT_EQ(cf1.parse_file(conf3_f.c_str(), &err, &warn), 0);
401 ASSERT_EQ(err.size(), 0U);
402 ASSERT_EQ(cf1.read("global", "log file", val), 0);
403 ASSERT_EQ(val, "/quite/a/long/path/for/a/log/file");
404 ASSERT_EQ(cf1.read("global", "pid file", val), 0);
405 ASSERT_EQ(val, "spork");
406
407 std::string unicode_config_1f(next_tempfile(unicode_config_1));
408 ConfFile cf2;
409 ASSERT_EQ(cf2.parse_file(unicode_config_1f.c_str(), &err, &warn), 0);
410 ASSERT_EQ(err.size(), 0U);
411 ASSERT_EQ(cf2.read("global", "log file", val), 0);
412 ASSERT_EQ(val, "\x66\xd1\x86\xd1\x9d\xd3\xad\xd3\xae");
413}
414
415TEST(ConfUtils, IllegalFiles) {
416 std::deque<std::string> err;
417 std::ostringstream warn;
418 std::string illegal_conf1_f(next_tempfile(illegal_conf1));
419 ConfFile cf1;
420 ASSERT_EQ(cf1.parse_file(illegal_conf1_f.c_str(), &err, &warn), 0);
421 ASSERT_EQ(err.size(), 1U);
422
423 bufferlist bl2;
424 bl2.append(illegal_conf2, strlen(illegal_conf2));
425 ConfFile cf2;
426 ASSERT_EQ(cf2.parse_bufferlist(&bl2, &err, &warn), 0);
427 ASSERT_EQ(err.size(), 1U);
428
429 std::string illegal_conf3_f(next_tempfile(illegal_conf3));
430 ConfFile cf3;
431 ASSERT_EQ(cf3.parse_file(illegal_conf3_f.c_str(), &err, &warn), 0);
432 ASSERT_EQ(err.size(), 1U);
433
434 std::string illegal_conf4_f(next_tempfile(illegal_conf4));
435 ConfFile cf4;
436 ASSERT_EQ(cf4.parse_file(illegal_conf4_f.c_str(), &err, &warn), 0);
437 ASSERT_EQ(err.size(), 1U);
438
439 std::string illegal_conf5_f(next_tempfile(illegal_conf5));
440 ConfFile cf5;
441 ASSERT_EQ(cf5.parse_file(illegal_conf5_f.c_str(), &err, &warn), 0);
442 ASSERT_EQ(err.size(), 1U);
443}
444
445TEST(ConfUtils, EscapingFiles) {
446 std::deque<std::string> err;
447 std::ostringstream warn;
448 std::string escaping_conf_1_f(next_tempfile(escaping_conf_1));
449 ConfFile cf1;
450 std::string val;
451 ASSERT_EQ(cf1.parse_file(escaping_conf_1_f.c_str(), &err, &warn), 0);
452 ASSERT_EQ(err.size(), 0U);
453
454 ASSERT_EQ(cf1.read("global", "log file", val), 0);
455 ASSERT_EQ(val, "the \"scare quotes\"");
456 ASSERT_EQ(cf1.read("global", "pid file", val), 0);
457 ASSERT_EQ(val, "a pid file");
458 ASSERT_EQ(cf1.read("mon", "keyring", val), 0);
459 ASSERT_EQ(val, "nested \"quotes\"");
460
461 std::string escaping_conf_2_f(next_tempfile(escaping_conf_2));
462 ConfFile cf2;
463 ASSERT_EQ(cf2.parse_file(escaping_conf_2_f.c_str(), &err, &warn), 0);
464 ASSERT_EQ(err.size(), 0U);
465
466 ASSERT_EQ(cf2.read("apple ][", "log file", val), 0);
467 ASSERT_EQ(val, "floppy disk");
468 ASSERT_EQ(cf2.read("mon", "keyring", val), 0);
469 ASSERT_EQ(val, "backslash\\");
470}
471
472TEST(ConfUtils, Overrides) {
473 md_config_t conf;
474 std::ostringstream warn;
475 std::string override_conf_1_f(next_tempfile(override_config_1));
476
477 conf.name.set(CEPH_ENTITY_TYPE_MON, "0");
478 conf.parse_config_files(override_conf_1_f.c_str(), &warn, 0);
479 ASSERT_EQ(conf.parse_errors.size(), 0U);
480 ASSERT_EQ(conf.log_file, "global_log");
481
482 conf.name.set(CEPH_ENTITY_TYPE_MDS, "a");
483 conf.parse_config_files(override_conf_1_f.c_str(), &warn, 0);
484 ASSERT_EQ(conf.parse_errors.size(), 0U);
485 ASSERT_EQ(conf.log_file, "mds_log");
486
487 conf.name.set(CEPH_ENTITY_TYPE_OSD, "0");
488 conf.parse_config_files(override_conf_1_f.c_str(), &warn, 0);
489 ASSERT_EQ(conf.parse_errors.size(), 0U);
490 ASSERT_EQ(conf.log_file, "osd0_log");
491}
492
493TEST(ConfUtils, DupKey) {
494 md_config_t conf;
495 std::ostringstream warn;
496 std::string dup_key_config_f(next_tempfile(dup_key_config_1));
497
498 conf.name.set(CEPH_ENTITY_TYPE_MDS, "a");
499 conf.parse_config_files(dup_key_config_f.c_str(), &warn, 0);
500 ASSERT_EQ(conf.parse_errors.size(), 0U);
501 ASSERT_EQ(conf.log_file, string("3"));
502}
503
504