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