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* 77 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 86 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 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* 107 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 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 137 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 144 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 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* 167 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 180 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* 198 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 213 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 228 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 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 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& 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& 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 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. 371 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 382 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. 393 model::metadata::metadata(const utils::config::tree& props) : 394 _pimpl(new impl(props)) 395 { 396 } 397 398 399 /// Destructor. 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 412 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& 422 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& 438 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 454 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& 464 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& 478 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& 492 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 508 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 522 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 534 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& 548 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& 564 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& 578 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& 592 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& 606 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& 622 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& 636 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& 651 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 665 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 678 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 690 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& 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. 732 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. 741 impl(const model::metadata& base) : 742 props(base._pimpl->props.deep_copy()), 743 built(false) 744 { 745 } 746 }; 747 748 749 /// Constructor. 750 model::metadata_builder::metadata_builder(void) : 751 _pimpl(new impl()) 752 { 753 } 754 755 756 /// Constructor. 757 model::metadata_builder::metadata_builder(const model::metadata& base) : 758 _pimpl(new impl(base)) 759 { 760 } 761 762 763 /// Destructor. 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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 1156 model::metadata_builder::build(void) const 1157 { 1158 PRE(!_pimpl->built); 1159 _pimpl->built = true; 1160 1161 return metadata(_pimpl->props); 1162 } 1163