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