1 // Copyright 2012 The Kyua Authors.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 #include "model/metadata.hpp"
30
31 #include <memory>
32
33 #include "engine/execenv/execenv.hpp"
34 #include "model/exceptions.hpp"
35 #include "model/types.hpp"
36 #include "utils/config/exceptions.hpp"
37 #include "utils/config/nodes.ipp"
38 #include "utils/config/tree.ipp"
39 #include "utils/datetime.hpp"
40 #include "utils/defs.hpp"
41 #include "utils/format/macros.hpp"
42 #include "utils/fs/exceptions.hpp"
43 #include "utils/fs/path.hpp"
44 #include "utils/noncopyable.hpp"
45 #include "utils/optional.ipp"
46 #include "utils/sanity.hpp"
47 #include "utils/text/exceptions.hpp"
48 #include "utils/text/operations.hpp"
49 #include "utils/units.hpp"
50
51 namespace config = utils::config;
52 namespace datetime = utils::datetime;
53 namespace fs = utils::fs;
54 namespace text = utils::text;
55 namespace units = utils::units;
56
57 using utils::optional;
58
59
60 namespace {
61
62
63 /// Global instance of defaults.
64 ///
65 /// This exists so that the getters in metadata can return references instead
66 /// of object copies. Use get_defaults() to query.
67 static optional< config::tree > defaults;
68
69
70 /// A leaf node that holds a bytes quantity.
71 class bytes_node : public config::native_leaf_node< units::bytes > {
72 public:
73 /// Copies the node.
74 ///
75 /// \return A dynamically-allocated node.
76 virtual base_node*
deep_copy(void) const77 deep_copy(void) const
78 {
79 std::unique_ptr< bytes_node > new_node(new bytes_node());
80 new_node->_value = _value;
81 return new_node.release();
82 }
83
84 /// Pushes the node's value onto the Lua stack.
85 void
push_lua(lutok::state &) const86 push_lua(lutok::state& /* state */) const
87 {
88 UNREACHABLE;
89 }
90
91 /// Sets the value of the node from an entry in the Lua stack.
92 void
set_lua(lutok::state &,const int)93 set_lua(lutok::state& /* state */, const int /* index */)
94 {
95 UNREACHABLE;
96 }
97 };
98
99
100 /// A leaf node that holds a time delta.
101 class delta_node : public config::typed_leaf_node< datetime::delta > {
102 public:
103 /// Copies the node.
104 ///
105 /// \return A dynamically-allocated node.
106 virtual base_node*
deep_copy(void) const107 deep_copy(void) const
108 {
109 std::unique_ptr< delta_node > new_node(new delta_node());
110 new_node->_value = _value;
111 return new_node.release();
112 }
113
114 /// Sets the value of the node from a raw string representation.
115 ///
116 /// \param raw_value The value to set the node to.
117 ///
118 /// \throw value_error If the value is invalid.
119 void
set_string(const std::string & raw_value)120 set_string(const std::string& raw_value)
121 {
122 unsigned int seconds;
123 try {
124 seconds = text::to_type< unsigned int >(raw_value);
125 } catch (const text::error& e) {
126 throw config::value_error(F("Invalid time delta %s") % raw_value);
127 }
128 set(datetime::delta(seconds, 0));
129 }
130
131 /// Converts the contents of the node to a string.
132 ///
133 /// \pre The node must have a value.
134 ///
135 /// \return A string representation of the value held by the node.
136 std::string
to_string(void) const137 to_string(void) const
138 {
139 return F("%s") % value().seconds;
140 }
141
142 /// Pushes the node's value onto the Lua stack.
143 void
push_lua(lutok::state &) const144 push_lua(lutok::state& /* state */) const
145 {
146 UNREACHABLE;
147 }
148
149 /// Sets the value of the node from an entry in the Lua stack.
150 void
set_lua(lutok::state &,const int)151 set_lua(lutok::state& /* state */, const int /* index */)
152 {
153 UNREACHABLE;
154 }
155 };
156
157
158 /// A leaf node that holds a "required user" property.
159 ///
160 /// This node is just a string, but it provides validation of the only allowed
161 /// values.
162 class user_node : public config::string_node {
163 /// Copies the node.
164 ///
165 /// \return A dynamically-allocated node.
166 virtual base_node*
deep_copy(void) const167 deep_copy(void) const
168 {
169 std::unique_ptr< user_node > new_node(new user_node());
170 new_node->_value = _value;
171 return new_node.release();
172 }
173
174 /// Checks a given user textual representation for validity.
175 ///
176 /// \param user The value to validate.
177 ///
178 /// \throw config::value_error If the value is not valid.
179 void
validate(const value_type & user) const180 validate(const value_type& user) const
181 {
182 if (!user.empty() && user != "root" && user != "unprivileged")
183 throw config::value_error("Invalid required user value");
184 }
185 };
186
187
188 /// A leaf node that holds a set of paths.
189 ///
190 /// This node type is used to represent the value of the required files and
191 /// required programs, for example, and these do not allow relative paths. We
192 /// check this here.
193 class paths_set_node : public config::base_set_node< fs::path > {
194 /// Copies the node.
195 ///
196 /// \return A dynamically-allocated node.
197 virtual base_node*
deep_copy(void) const198 deep_copy(void) const
199 {
200 std::unique_ptr< paths_set_node > new_node(new paths_set_node());
201 new_node->_value = _value;
202 return new_node.release();
203 }
204
205 /// Converts a single path to the native type.
206 ///
207 /// \param raw_value The value to parse.
208 ///
209 /// \return The parsed value.
210 ///
211 /// \throw config::value_error If the value is invalid.
212 fs::path
parse_one(const std::string & raw_value) const213 parse_one(const std::string& raw_value) const
214 {
215 try {
216 return fs::path(raw_value);
217 } catch (const fs::error& e) {
218 throw config::value_error(e.what());
219 }
220 }
221
222 /// Checks a collection of paths for validity.
223 ///
224 /// \param paths The value to validate.
225 ///
226 /// \throw config::value_error If the value is not valid.
227 void
validate(const value_type & paths) const228 validate(const value_type& paths) const
229 {
230 for (value_type::const_iterator iter = paths.begin();
231 iter != paths.end(); ++iter) {
232 const fs::path& path = *iter;
233 if (!path.is_absolute() && path.ncomponents() > 1)
234 throw config::value_error(F("Relative path '%s' not allowed") %
235 *iter);
236 }
237 }
238 };
239
240
241 /// Initializes a tree to hold test case requirements.
242 ///
243 /// \param [in,out] tree The tree to initialize.
244 static void
init_tree(config::tree & tree)245 init_tree(config::tree& tree)
246 {
247 tree.define< config::strings_set_node >("allowed_architectures");
248 tree.define< config::strings_set_node >("allowed_platforms");
249 tree.define_dynamic("custom");
250 tree.define< config::string_node >("description");
251 tree.define< config::string_node >("execenv");
252 tree.define< config::string_node >("execenv_jail_params");
253 tree.define< config::bool_node >("has_cleanup");
254 tree.define< config::bool_node >("is_exclusive");
255 tree.define< config::strings_set_node >("required_configs");
256 tree.define< bytes_node >("required_disk_space");
257 tree.define< paths_set_node >("required_files");
258 tree.define< bytes_node >("required_memory");
259 tree.define< config::strings_set_node >("required_kmods");
260 tree.define< paths_set_node >("required_programs");
261 tree.define< user_node >("required_user");
262 tree.define< delta_node >("timeout");
263 }
264
265
266 /// Sets default values on a tree object.
267 ///
268 /// \param [in,out] tree The tree to configure.
269 static void
set_defaults(config::tree & tree)270 set_defaults(config::tree& tree)
271 {
272 tree.set< config::strings_set_node >("allowed_architectures",
273 model::strings_set());
274 tree.set< config::strings_set_node >("allowed_platforms",
275 model::strings_set());
276 tree.set< config::string_node >("description", "");
277 tree.set< config::string_node >("execenv", "");
278 tree.set< config::string_node >("execenv_jail_params", "");
279 tree.set< config::bool_node >("has_cleanup", false);
280 tree.set< config::bool_node >("is_exclusive", false);
281 tree.set< config::strings_set_node >("required_configs",
282 model::strings_set());
283 tree.set< bytes_node >("required_disk_space", units::bytes(0));
284 tree.set< paths_set_node >("required_files", model::paths_set());
285 tree.set< bytes_node >("required_memory", units::bytes(0));
286 tree.set< config::strings_set_node >("required_kmods", model::strings_set());
287 tree.set< paths_set_node >("required_programs", model::paths_set());
288 tree.set< user_node >("required_user", "");
289 // TODO(jmmv): We shouldn't be setting a default timeout like this. See
290 // Issue 5 for details.
291 tree.set< delta_node >("timeout", datetime::delta(300, 0));
292 }
293
294
295 /// Queries the global defaults tree object with lazy initialization.
296 ///
297 /// \return A metadata tree. This object is statically allocated so it is
298 /// acceptable to obtain references to it and its members.
299 const config::tree&
get_defaults(void)300 get_defaults(void)
301 {
302 if (!defaults) {
303 config::tree props;
304 init_tree(props);
305 set_defaults(props);
306 defaults = props;
307 }
308 return defaults.get();
309 }
310
311
312 /// Looks up a value in a tree with error rewriting.
313 ///
314 /// \tparam NodeType The type of the node.
315 /// \param tree The tree in which to insert the value.
316 /// \param key The key to set.
317 ///
318 /// \return A read-write reference to the value in the node.
319 ///
320 /// \throw model::error If the key is not known or if the value is not valid.
321 template< class NodeType >
322 typename NodeType::value_type&
lookup_rw(config::tree & tree,const std::string & key)323 lookup_rw(config::tree& tree, const std::string& key)
324 {
325 try {
326 return tree.lookup_rw< NodeType >(key);
327 } catch (const config::unknown_key_error& e) {
328 throw model::error(F("Unknown metadata property %s") % key);
329 } catch (const config::value_error& e) {
330 throw model::error(F("Invalid value for metadata property %s: %s") %
331 key % e.what());
332 }
333 }
334
335
336 /// Sets a value in a tree with error rewriting.
337 ///
338 /// \tparam NodeType The type of the node.
339 /// \param tree The tree in which to insert the value.
340 /// \param key The key to set.
341 /// \param value The value to set the node to.
342 ///
343 /// \throw model::error If the key is not known or if the value is not valid.
344 template< class NodeType >
345 void
set(config::tree & tree,const std::string & key,const typename NodeType::value_type & value)346 set(config::tree& tree, const std::string& key,
347 const typename NodeType::value_type& value)
348 {
349 try {
350 tree.set< NodeType >(key, value);
351 } catch (const config::unknown_key_error& e) {
352 throw model::error(F("Unknown metadata property %s") % key);
353 } catch (const config::value_error& e) {
354 throw model::error(F("Invalid value for metadata property %s: %s") %
355 key % e.what());
356 }
357 }
358
359
360 } // anonymous namespace
361
362
363 /// Internal implementation of the metadata class.
364 struct model::metadata::impl : utils::noncopyable {
365 /// Metadata properties.
366 config::tree props;
367
368 /// Constructor.
369 ///
370 /// \param props_ Metadata properties of the test.
implmodel::metadata::impl371 impl(const utils::config::tree& props_) :
372 props(props_)
373 {
374 }
375
376 /// Equality comparator.
377 ///
378 /// \param other The other object to compare this one to.
379 ///
380 /// \return True if this object and other are equal; false otherwise.
381 bool
operator ==model::metadata::impl382 operator==(const impl& other) const
383 {
384 return (get_defaults().combine(props) ==
385 get_defaults().combine(other.props));
386 }
387 };
388
389
390 /// Constructor.
391 ///
392 /// \param props Metadata properties of the test.
metadata(const utils::config::tree & props)393 model::metadata::metadata(const utils::config::tree& props) :
394 _pimpl(new impl(props))
395 {
396 }
397
398
399 /// Destructor.
~metadata(void)400 model::metadata::~metadata(void)
401 {
402 }
403
404
405 /// Applies a set of overrides to this metadata object.
406 ///
407 /// \param overrides The overrides to apply. Any values explicitly set in this
408 /// other object will override any possible values set in this object.
409 ///
410 /// \return A new metadata object with the combination.
411 model::metadata
apply_overrides(const metadata & overrides) const412 model::metadata::apply_overrides(const metadata& overrides) const
413 {
414 return metadata(_pimpl->props.combine(overrides._pimpl->props));
415 }
416
417
418 /// Returns the architectures allowed by the test.
419 ///
420 /// \return Set of architectures, or empty if this does not apply.
421 const model::strings_set&
allowed_architectures(void) const422 model::metadata::allowed_architectures(void) const
423 {
424 if (_pimpl->props.is_set("allowed_architectures")) {
425 return _pimpl->props.lookup< config::strings_set_node >(
426 "allowed_architectures");
427 } else {
428 return get_defaults().lookup< config::strings_set_node >(
429 "allowed_architectures");
430 }
431 }
432
433
434 /// Returns the platforms allowed by the test.
435 ///
436 /// \return Set of platforms, or empty if this does not apply.
437 const model::strings_set&
allowed_platforms(void) const438 model::metadata::allowed_platforms(void) const
439 {
440 if (_pimpl->props.is_set("allowed_platforms")) {
441 return _pimpl->props.lookup< config::strings_set_node >(
442 "allowed_platforms");
443 } else {
444 return get_defaults().lookup< config::strings_set_node >(
445 "allowed_platforms");
446 }
447 }
448
449
450 /// Returns all the user-defined metadata properties.
451 ///
452 /// \return A key/value map of properties.
453 model::properties_map
custom(void) const454 model::metadata::custom(void) const
455 {
456 return _pimpl->props.all_properties("custom", true);
457 }
458
459
460 /// Returns the description of the test.
461 ///
462 /// \return Textual description; may be empty.
463 const std::string&
description(void) const464 model::metadata::description(void) const
465 {
466 if (_pimpl->props.is_set("description")) {
467 return _pimpl->props.lookup< config::string_node >("description");
468 } else {
469 return get_defaults().lookup< config::string_node >("description");
470 }
471 }
472
473
474 /// Returns execution environment name.
475 ///
476 /// \return Name of configured execution environment.
477 const std::string&
execenv(void) const478 model::metadata::execenv(void) const
479 {
480 if (_pimpl->props.is_set("execenv")) {
481 return _pimpl->props.lookup< config::string_node >("execenv");
482 } else {
483 return get_defaults().lookup< config::string_node >("execenv");
484 }
485 }
486
487
488 /// Returns execenv jail(8) parameters string to run a test with.
489 ///
490 /// \return String of jail parameters.
491 const std::string&
execenv_jail_params(void) const492 model::metadata::execenv_jail_params(void) const
493 {
494 if (_pimpl->props.is_set("execenv_jail_params")) {
495 return _pimpl->props.lookup< config::string_node >(
496 "execenv_jail_params");
497 } else {
498 return get_defaults().lookup< config::string_node >(
499 "execenv_jail_params");
500 }
501 }
502
503
504 /// Returns whether the test has a cleanup part or not.
505 ///
506 /// \return True if there is a cleanup part; false otherwise.
507 bool
has_cleanup(void) const508 model::metadata::has_cleanup(void) const
509 {
510 if (_pimpl->props.is_set("has_cleanup")) {
511 return _pimpl->props.lookup< config::bool_node >("has_cleanup");
512 } else {
513 return get_defaults().lookup< config::bool_node >("has_cleanup");
514 }
515 }
516
517
518 /// Returns whether the test has a specific execenv apart from default one.
519 ///
520 /// \return True if there is a non-host execenv configured; false otherwise.
521 bool
has_execenv(void) const522 model::metadata::has_execenv(void) const
523 {
524 const std::string& name = execenv();
525 return !name.empty() && name != engine::execenv::default_execenv_name;
526 }
527
528
529 /// Returns whether the test is exclusive or not.
530 ///
531 /// \return True if the test has to be run on its own, not concurrently with any
532 /// other tests; false otherwise.
533 bool
is_exclusive(void) const534 model::metadata::is_exclusive(void) const
535 {
536 if (_pimpl->props.is_set("is_exclusive")) {
537 return _pimpl->props.lookup< config::bool_node >("is_exclusive");
538 } else {
539 return get_defaults().lookup< config::bool_node >("is_exclusive");
540 }
541 }
542
543
544 /// Returns the list of configuration variables needed by the test.
545 ///
546 /// \return Set of configuration variables.
547 const model::strings_set&
required_configs(void) const548 model::metadata::required_configs(void) const
549 {
550 if (_pimpl->props.is_set("required_configs")) {
551 return _pimpl->props.lookup< config::strings_set_node >(
552 "required_configs");
553 } else {
554 return get_defaults().lookup< config::strings_set_node >(
555 "required_configs");
556 }
557 }
558
559
560 /// Returns the amount of free disk space required by the test.
561 ///
562 /// \return Number of bytes, or 0 if this does not apply.
563 const units::bytes&
required_disk_space(void) const564 model::metadata::required_disk_space(void) const
565 {
566 if (_pimpl->props.is_set("required_disk_space")) {
567 return _pimpl->props.lookup< bytes_node >("required_disk_space");
568 } else {
569 return get_defaults().lookup< bytes_node >("required_disk_space");
570 }
571 }
572
573
574 /// Returns the list of files needed by the test.
575 ///
576 /// \return Set of paths.
577 const model::paths_set&
required_files(void) const578 model::metadata::required_files(void) const
579 {
580 if (_pimpl->props.is_set("required_files")) {
581 return _pimpl->props.lookup< paths_set_node >("required_files");
582 } else {
583 return get_defaults().lookup< paths_set_node >("required_files");
584 }
585 }
586
587
588 /// Returns the amount of memory required by the test.
589 ///
590 /// \return Number of bytes, or 0 if this does not apply.
591 const units::bytes&
required_memory(void) const592 model::metadata::required_memory(void) const
593 {
594 if (_pimpl->props.is_set("required_memory")) {
595 return _pimpl->props.lookup< bytes_node >("required_memory");
596 } else {
597 return get_defaults().lookup< bytes_node >("required_memory");
598 }
599 }
600
601
602 /// Returns the list of kernel modules needed by the test.
603 ///
604 /// \return Set of kernel module names.
605 const model::strings_set&
required_kmods(void) const606 model::metadata::required_kmods(void) const
607 {
608 if (_pimpl->props.is_set("required_kmods")) {
609 return _pimpl->props.lookup< config::strings_set_node >(
610 "required_kmods");
611 } else {
612 return get_defaults().lookup< config::strings_set_node >(
613 "required_kmods");
614 }
615 }
616
617
618 /// Returns the list of programs needed by the test.
619 ///
620 /// \return Set of paths.
621 const model::paths_set&
required_programs(void) const622 model::metadata::required_programs(void) const
623 {
624 if (_pimpl->props.is_set("required_programs")) {
625 return _pimpl->props.lookup< paths_set_node >("required_programs");
626 } else {
627 return get_defaults().lookup< paths_set_node >("required_programs");
628 }
629 }
630
631
632 /// Returns the user required by the test.
633 ///
634 /// \return One of unprivileged, root or empty.
635 const std::string&
required_user(void) const636 model::metadata::required_user(void) const
637 {
638 if (_pimpl->props.is_set("required_user")) {
639 return _pimpl->props.lookup< user_node >("required_user");
640 } else {
641 return get_defaults().lookup< user_node >("required_user");
642 }
643 }
644
645
646 /// Returns the timeout of the test.
647 ///
648 /// \return A time delta; should be compared to default_timeout to see if it has
649 /// been overriden.
650 const datetime::delta&
timeout(void) const651 model::metadata::timeout(void) const
652 {
653 if (_pimpl->props.is_set("timeout")) {
654 return _pimpl->props.lookup< delta_node >("timeout");
655 } else {
656 return get_defaults().lookup< delta_node >("timeout");
657 }
658 }
659
660
661 /// Externalizes the metadata to a set of key/value textual pairs.
662 ///
663 /// \return A key/value representation of the metadata.
664 model::properties_map
to_properties(void) const665 model::metadata::to_properties(void) const
666 {
667 const config::tree fully_specified = get_defaults().combine(_pimpl->props);
668 return fully_specified.all_properties();
669 }
670
671
672 /// Equality comparator.
673 ///
674 /// \param other The other object to compare this one to.
675 ///
676 /// \return True if this object and other are equal; false otherwise.
677 bool
operator ==(const metadata & other) const678 model::metadata::operator==(const metadata& other) const
679 {
680 return _pimpl == other._pimpl || *_pimpl == *other._pimpl;
681 }
682
683
684 /// Inequality comparator.
685 ///
686 /// \param other The other object to compare this one to.
687 ///
688 /// \return True if this object and other are different; false otherwise.
689 bool
operator !=(const metadata & other) const690 model::metadata::operator!=(const metadata& other) const
691 {
692 return !(*this == other);
693 }
694
695
696 /// Injects the object into a stream.
697 ///
698 /// \param output The stream into which to inject the object.
699 /// \param object The object to format.
700 ///
701 /// \return The output stream.
702 std::ostream&
operator <<(std::ostream & output,const metadata & object)703 model::operator<<(std::ostream& output, const metadata& object)
704 {
705 output << "metadata{";
706
707 bool first = true;
708 const model::properties_map props = object.to_properties();
709 for (model::properties_map::const_iterator iter = props.begin();
710 iter != props.end(); ++iter) {
711 if (!first)
712 output << ", ";
713 output << F("%s=%s") % (*iter).first %
714 text::quote((*iter).second, '\'');
715 first = false;
716 }
717
718 output << "}";
719 return output;
720 }
721
722
723 /// Internal implementation of the metadata_builder class.
724 struct model::metadata_builder::impl : utils::noncopyable {
725 /// Collection of requirements.
726 config::tree props;
727
728 /// Whether we have created a metadata object or not.
729 bool built;
730
731 /// Constructor.
implmodel::metadata_builder::impl732 impl(void) :
733 built(false)
734 {
735 init_tree(props);
736 }
737
738 /// Constructor.
739 ///
740 /// \param base The base model to construct a copy from.
implmodel::metadata_builder::impl741 impl(const model::metadata& base) :
742 props(base._pimpl->props.deep_copy()),
743 built(false)
744 {
745 }
746 };
747
748
749 /// Constructor.
metadata_builder(void)750 model::metadata_builder::metadata_builder(void) :
751 _pimpl(new impl())
752 {
753 }
754
755
756 /// Constructor.
metadata_builder(const model::metadata & base)757 model::metadata_builder::metadata_builder(const model::metadata& base) :
758 _pimpl(new impl(base))
759 {
760 }
761
762
763 /// Destructor.
~metadata_builder(void)764 model::metadata_builder::~metadata_builder(void)
765 {
766 }
767
768
769 /// Accumulates an additional allowed architecture.
770 ///
771 /// \param arch The architecture.
772 ///
773 /// \return A reference to this builder.
774 ///
775 /// \throw model::error If the value is invalid.
776 model::metadata_builder&
add_allowed_architecture(const std::string & arch)777 model::metadata_builder::add_allowed_architecture(const std::string& arch)
778 {
779 if (!_pimpl->props.is_set("allowed_architectures")) {
780 _pimpl->props.set< config::strings_set_node >(
781 "allowed_architectures",
782 get_defaults().lookup< config::strings_set_node >(
783 "allowed_architectures"));
784 }
785 lookup_rw< config::strings_set_node >(
786 _pimpl->props, "allowed_architectures").insert(arch);
787 return *this;
788 }
789
790
791 /// Accumulates an additional allowed platform.
792 ///
793 /// \param platform The platform.
794 ///
795 /// \return A reference to this builder.
796 ///
797 /// \throw model::error If the value is invalid.
798 model::metadata_builder&
add_allowed_platform(const std::string & platform)799 model::metadata_builder::add_allowed_platform(const std::string& platform)
800 {
801 if (!_pimpl->props.is_set("allowed_platforms")) {
802 _pimpl->props.set< config::strings_set_node >(
803 "allowed_platforms",
804 get_defaults().lookup< config::strings_set_node >(
805 "allowed_platforms"));
806 }
807 lookup_rw< config::strings_set_node >(
808 _pimpl->props, "allowed_platforms").insert(platform);
809 return *this;
810 }
811
812
813 /// Accumulates a single user-defined property.
814 ///
815 /// \param key Name of the property to define.
816 /// \param value Value of the property.
817 ///
818 /// \return A reference to this builder.
819 ///
820 /// \throw model::error If the value is invalid.
821 model::metadata_builder&
add_custom(const std::string & key,const std::string & value)822 model::metadata_builder::add_custom(const std::string& key,
823 const std::string& value)
824 {
825 _pimpl->props.set_string(F("custom.%s") % key, value);
826 return *this;
827 }
828
829
830 /// Accumulates an additional required configuration variable.
831 ///
832 /// \param var The name of the configuration variable.
833 ///
834 /// \return A reference to this builder.
835 ///
836 /// \throw model::error If the value is invalid.
837 model::metadata_builder&
add_required_config(const std::string & var)838 model::metadata_builder::add_required_config(const std::string& var)
839 {
840 if (!_pimpl->props.is_set("required_configs")) {
841 _pimpl->props.set< config::strings_set_node >(
842 "required_configs",
843 get_defaults().lookup< config::strings_set_node >(
844 "required_configs"));
845 }
846 lookup_rw< config::strings_set_node >(
847 _pimpl->props, "required_configs").insert(var);
848 return *this;
849 }
850
851
852 /// Accumulates an additional required file.
853 ///
854 /// \param path The path to the file.
855 ///
856 /// \return A reference to this builder.
857 ///
858 /// \throw model::error If the value is invalid.
859 model::metadata_builder&
add_required_file(const fs::path & path)860 model::metadata_builder::add_required_file(const fs::path& path)
861 {
862 if (!_pimpl->props.is_set("required_files")) {
863 _pimpl->props.set< paths_set_node >(
864 "required_files",
865 get_defaults().lookup< paths_set_node >("required_files"));
866 }
867 lookup_rw< paths_set_node >(_pimpl->props, "required_files").insert(path);
868 return *this;
869 }
870
871
872 /// Accumulates an additional required program.
873 ///
874 /// \param path The path to the program.
875 ///
876 /// \return A reference to this builder.
877 ///
878 /// \throw model::error If the value is invalid.
879 model::metadata_builder&
add_required_program(const fs::path & path)880 model::metadata_builder::add_required_program(const fs::path& path)
881 {
882 if (!_pimpl->props.is_set("required_programs")) {
883 _pimpl->props.set< paths_set_node >(
884 "required_programs",
885 get_defaults().lookup< paths_set_node >("required_programs"));
886 }
887 lookup_rw< paths_set_node >(_pimpl->props,
888 "required_programs").insert(path);
889 return *this;
890 }
891
892
893 /// Sets the architectures allowed by the test.
894 ///
895 /// \param as Set of architectures.
896 ///
897 /// \return A reference to this builder.
898 ///
899 /// \throw model::error If the value is invalid.
900 model::metadata_builder&
set_allowed_architectures(const model::strings_set & as)901 model::metadata_builder::set_allowed_architectures(
902 const model::strings_set& as)
903 {
904 set< config::strings_set_node >(_pimpl->props, "allowed_architectures", as);
905 return *this;
906 }
907
908
909 /// Sets the platforms allowed by the test.
910 ///
911 /// \return ps Set of platforms.
912 ///
913 /// \return A reference to this builder.
914 ///
915 /// \throw model::error If the value is invalid.
916 model::metadata_builder&
set_allowed_platforms(const model::strings_set & ps)917 model::metadata_builder::set_allowed_platforms(const model::strings_set& ps)
918 {
919 set< config::strings_set_node >(_pimpl->props, "allowed_platforms", ps);
920 return *this;
921 }
922
923
924 /// Sets the user-defined properties.
925 ///
926 /// \param props The custom properties to set.
927 ///
928 /// \return A reference to this builder.
929 ///
930 /// \throw model::error If the value is invalid.
931 model::metadata_builder&
set_custom(const model::properties_map & props)932 model::metadata_builder::set_custom(const model::properties_map& props)
933 {
934 for (model::properties_map::const_iterator iter = props.begin();
935 iter != props.end(); ++iter)
936 _pimpl->props.set_string(F("custom.%s") % (*iter).first,
937 (*iter).second);
938 return *this;
939 }
940
941
942 /// Sets the description of the test.
943 ///
944 /// \param description Textual description of the test.
945 ///
946 /// \return A reference to this builder.
947 ///
948 /// \throw model::error If the value is invalid.
949 model::metadata_builder&
set_description(const std::string & description)950 model::metadata_builder::set_description(const std::string& description)
951 {
952 set< config::string_node >(_pimpl->props, "description", description);
953 return *this;
954 }
955
956
957 /// Sets execution environment name.
958 ///
959 /// \param name Execution environment name.
960 ///
961 /// \return A reference to this builder.
962 ///
963 /// \throw model::error If the value is invalid.
964 model::metadata_builder&
set_execenv(const std::string & name)965 model::metadata_builder::set_execenv(const std::string& name)
966 {
967 set< config::string_node >(_pimpl->props, "execenv", name);
968 return *this;
969 }
970
971
972 /// Sets execenv jail(8) parameters string to run the test with.
973 ///
974 /// \param params String of jail parameters.
975 ///
976 /// \return A reference to this builder.
977 ///
978 /// \throw model::error If the value is invalid.
979 model::metadata_builder&
set_execenv_jail_params(const std::string & params)980 model::metadata_builder::set_execenv_jail_params(const std::string& params)
981 {
982 set< config::string_node >(_pimpl->props, "execenv_jail_params", params);
983 return *this;
984 }
985
986
987 /// Sets whether the test has a cleanup part or not.
988 ///
989 /// \param cleanup True if the test has a cleanup part; false otherwise.
990 ///
991 /// \return A reference to this builder.
992 ///
993 /// \throw model::error If the value is invalid.
994 model::metadata_builder&
set_has_cleanup(const bool cleanup)995 model::metadata_builder::set_has_cleanup(const bool cleanup)
996 {
997 set< config::bool_node >(_pimpl->props, "has_cleanup", cleanup);
998 return *this;
999 }
1000
1001
1002 /// Sets whether the test is exclusive or not.
1003 ///
1004 /// \param exclusive True if the test is exclusive; false otherwise.
1005 ///
1006 /// \return A reference to this builder.
1007 ///
1008 /// \throw model::error If the value is invalid.
1009 model::metadata_builder&
set_is_exclusive(const bool exclusive)1010 model::metadata_builder::set_is_exclusive(const bool exclusive)
1011 {
1012 set< config::bool_node >(_pimpl->props, "is_exclusive", exclusive);
1013 return *this;
1014 }
1015
1016
1017 /// Sets the list of configuration variables needed by the test.
1018 ///
1019 /// \param vars Set of configuration variables.
1020 ///
1021 /// \return A reference to this builder.
1022 ///
1023 /// \throw model::error If the value is invalid.
1024 model::metadata_builder&
set_required_configs(const model::strings_set & vars)1025 model::metadata_builder::set_required_configs(const model::strings_set& vars)
1026 {
1027 set< config::strings_set_node >(_pimpl->props, "required_configs", vars);
1028 return *this;
1029 }
1030
1031
1032 /// Sets the amount of free disk space required by the test.
1033 ///
1034 /// \param bytes Number of bytes.
1035 ///
1036 /// \return A reference to this builder.
1037 ///
1038 /// \throw model::error If the value is invalid.
1039 model::metadata_builder&
set_required_disk_space(const units::bytes & bytes)1040 model::metadata_builder::set_required_disk_space(const units::bytes& bytes)
1041 {
1042 set< bytes_node >(_pimpl->props, "required_disk_space", bytes);
1043 return *this;
1044 }
1045
1046
1047 /// Sets the list of files needed by the test.
1048 ///
1049 /// \param files Set of paths.
1050 ///
1051 /// \return A reference to this builder.
1052 ///
1053 /// \throw model::error If the value is invalid.
1054 model::metadata_builder&
set_required_files(const model::paths_set & files)1055 model::metadata_builder::set_required_files(const model::paths_set& files)
1056 {
1057 set< paths_set_node >(_pimpl->props, "required_files", files);
1058 return *this;
1059 }
1060
1061
1062 /// Sets the amount of memory required by the test.
1063 ///
1064 /// \param bytes Number of bytes.
1065 ///
1066 /// \return A reference to this builder.
1067 ///
1068 /// \throw model::error If the value is invalid.
1069 model::metadata_builder&
set_required_memory(const units::bytes & bytes)1070 model::metadata_builder::set_required_memory(const units::bytes& bytes)
1071 {
1072 set< bytes_node >(_pimpl->props, "required_memory", bytes);
1073 return *this;
1074 }
1075
1076
1077 /// Sets the list of programs needed by the test.
1078 ///
1079 /// \param progs Set of paths.
1080 ///
1081 /// \return A reference to this builder.
1082 ///
1083 /// \throw model::error If the value is invalid.
1084 model::metadata_builder&
set_required_programs(const model::paths_set & progs)1085 model::metadata_builder::set_required_programs(const model::paths_set& progs)
1086 {
1087 set< paths_set_node >(_pimpl->props, "required_programs", progs);
1088 return *this;
1089 }
1090
1091
1092 /// Sets the user required by the test.
1093 ///
1094 /// \param user One of unprivileged, root or empty.
1095 ///
1096 /// \return A reference to this builder.
1097 ///
1098 /// \throw model::error If the value is invalid.
1099 model::metadata_builder&
set_required_user(const std::string & user)1100 model::metadata_builder::set_required_user(const std::string& user)
1101 {
1102 set< user_node >(_pimpl->props, "required_user", user);
1103 return *this;
1104 }
1105
1106
1107 /// Sets a metadata property by name from its textual representation.
1108 ///
1109 /// \param key The property to set.
1110 /// \param value The value to set the property to.
1111 ///
1112 /// \return A reference to this builder.
1113 ///
1114 /// \throw model::error If the value is invalid or the key does not exist.
1115 model::metadata_builder&
set_string(const std::string & key,const std::string & value)1116 model::metadata_builder::set_string(const std::string& key,
1117 const std::string& value)
1118 {
1119 try {
1120 _pimpl->props.set_string(key, value);
1121 } catch (const config::unknown_key_error& e) {
1122 throw model::format_error(F("Unknown metadata property %s") % key);
1123 } catch (const config::value_error& e) {
1124 throw model::format_error(
1125 F("Invalid value for metadata property %s: %s") % key % e.what());
1126 }
1127 return *this;
1128 }
1129
1130
1131 /// Sets the timeout of the test.
1132 ///
1133 /// \param timeout The timeout to set.
1134 ///
1135 /// \return A reference to this builder.
1136 ///
1137 /// \throw model::error If the value is invalid.
1138 model::metadata_builder&
set_timeout(const datetime::delta & timeout)1139 model::metadata_builder::set_timeout(const datetime::delta& timeout)
1140 {
1141 set< delta_node >(_pimpl->props, "timeout", timeout);
1142 return *this;
1143 }
1144
1145
1146 /// Creates a new metadata object.
1147 ///
1148 /// \pre This has not yet been called. We only support calling this function
1149 /// once due to the way the internal tree works: we pass around references, not
1150 /// deep copies, so if we allowed a second build, we'd encourage reusing the
1151 /// same builder to construct different metadata objects, and this could have
1152 /// unintended consequences.
1153 ///
1154 /// \return The constructed metadata object.
1155 model::metadata
build(void) const1156 model::metadata_builder::build(void) const
1157 {
1158 PRE(!_pimpl->built);
1159 _pimpl->built = true;
1160
1161 return metadata(_pimpl->props);
1162 }
1163