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::auto_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::auto_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::auto_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::auto_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< paths_set_node >("required_programs"); 260 tree.define< user_node >("required_user"); 261 tree.define< delta_node >("timeout"); 262 } 263 264 265 /// Sets default values on a tree object. 266 /// 267 /// \param [in,out] tree The tree to configure. 268 static void 269 set_defaults(config::tree& tree) 270 { 271 tree.set< config::strings_set_node >("allowed_architectures", 272 model::strings_set()); 273 tree.set< config::strings_set_node >("allowed_platforms", 274 model::strings_set()); 275 tree.set< config::string_node >("description", ""); 276 tree.set< config::string_node >("execenv", ""); 277 tree.set< config::string_node >("execenv_jail_params", ""); 278 tree.set< config::bool_node >("has_cleanup", false); 279 tree.set< config::bool_node >("is_exclusive", false); 280 tree.set< config::strings_set_node >("required_configs", 281 model::strings_set()); 282 tree.set< bytes_node >("required_disk_space", units::bytes(0)); 283 tree.set< paths_set_node >("required_files", model::paths_set()); 284 tree.set< bytes_node >("required_memory", units::bytes(0)); 285 tree.set< paths_set_node >("required_programs", model::paths_set()); 286 tree.set< user_node >("required_user", ""); 287 // TODO(jmmv): We shouldn't be setting a default timeout like this. See 288 // Issue 5 for details. 289 tree.set< delta_node >("timeout", datetime::delta(300, 0)); 290 } 291 292 293 /// Queries the global defaults tree object with lazy initialization. 294 /// 295 /// \return A metadata tree. This object is statically allocated so it is 296 /// acceptable to obtain references to it and its members. 297 const config::tree& 298 get_defaults(void) 299 { 300 if (!defaults) { 301 config::tree props; 302 init_tree(props); 303 set_defaults(props); 304 defaults = props; 305 } 306 return defaults.get(); 307 } 308 309 310 /// Looks up a value in a tree with error rewriting. 311 /// 312 /// \tparam NodeType The type of the node. 313 /// \param tree The tree in which to insert the value. 314 /// \param key The key to set. 315 /// 316 /// \return A read-write reference to the value in the node. 317 /// 318 /// \throw model::error If the key is not known or if the value is not valid. 319 template< class NodeType > 320 typename NodeType::value_type& 321 lookup_rw(config::tree& tree, const std::string& key) 322 { 323 try { 324 return tree.lookup_rw< NodeType >(key); 325 } catch (const config::unknown_key_error& e) { 326 throw model::error(F("Unknown metadata property %s") % key); 327 } catch (const config::value_error& e) { 328 throw model::error(F("Invalid value for metadata property %s: %s") % 329 key % e.what()); 330 } 331 } 332 333 334 /// Sets a value in a tree with error rewriting. 335 /// 336 /// \tparam NodeType The type of the node. 337 /// \param tree The tree in which to insert the value. 338 /// \param key The key to set. 339 /// \param value The value to set the node to. 340 /// 341 /// \throw model::error If the key is not known or if the value is not valid. 342 template< class NodeType > 343 void 344 set(config::tree& tree, const std::string& key, 345 const typename NodeType::value_type& value) 346 { 347 try { 348 tree.set< NodeType >(key, value); 349 } catch (const config::unknown_key_error& e) { 350 throw model::error(F("Unknown metadata property %s") % key); 351 } catch (const config::value_error& e) { 352 throw model::error(F("Invalid value for metadata property %s: %s") % 353 key % e.what()); 354 } 355 } 356 357 358 } // anonymous namespace 359 360 361 /// Internal implementation of the metadata class. 362 struct model::metadata::impl : utils::noncopyable { 363 /// Metadata properties. 364 config::tree props; 365 366 /// Constructor. 367 /// 368 /// \param props_ Metadata properties of the test. 369 impl(const utils::config::tree& props_) : 370 props(props_) 371 { 372 } 373 374 /// Equality comparator. 375 /// 376 /// \param other The other object to compare this one to. 377 /// 378 /// \return True if this object and other are equal; false otherwise. 379 bool 380 operator==(const impl& other) const 381 { 382 return (get_defaults().combine(props) == 383 get_defaults().combine(other.props)); 384 } 385 }; 386 387 388 /// Constructor. 389 /// 390 /// \param props Metadata properties of the test. 391 model::metadata::metadata(const utils::config::tree& props) : 392 _pimpl(new impl(props)) 393 { 394 } 395 396 397 /// Destructor. 398 model::metadata::~metadata(void) 399 { 400 } 401 402 403 /// Applies a set of overrides to this metadata object. 404 /// 405 /// \param overrides The overrides to apply. Any values explicitly set in this 406 /// other object will override any possible values set in this object. 407 /// 408 /// \return A new metadata object with the combination. 409 model::metadata 410 model::metadata::apply_overrides(const metadata& overrides) const 411 { 412 return metadata(_pimpl->props.combine(overrides._pimpl->props)); 413 } 414 415 416 /// Returns the architectures allowed by the test. 417 /// 418 /// \return Set of architectures, or empty if this does not apply. 419 const model::strings_set& 420 model::metadata::allowed_architectures(void) const 421 { 422 if (_pimpl->props.is_set("allowed_architectures")) { 423 return _pimpl->props.lookup< config::strings_set_node >( 424 "allowed_architectures"); 425 } else { 426 return get_defaults().lookup< config::strings_set_node >( 427 "allowed_architectures"); 428 } 429 } 430 431 432 /// Returns the platforms allowed by the test. 433 /// 434 /// \return Set of platforms, or empty if this does not apply. 435 const model::strings_set& 436 model::metadata::allowed_platforms(void) const 437 { 438 if (_pimpl->props.is_set("allowed_platforms")) { 439 return _pimpl->props.lookup< config::strings_set_node >( 440 "allowed_platforms"); 441 } else { 442 return get_defaults().lookup< config::strings_set_node >( 443 "allowed_platforms"); 444 } 445 } 446 447 448 /// Returns all the user-defined metadata properties. 449 /// 450 /// \return A key/value map of properties. 451 model::properties_map 452 model::metadata::custom(void) const 453 { 454 return _pimpl->props.all_properties("custom", true); 455 } 456 457 458 /// Returns the description of the test. 459 /// 460 /// \return Textual description; may be empty. 461 const std::string& 462 model::metadata::description(void) const 463 { 464 if (_pimpl->props.is_set("description")) { 465 return _pimpl->props.lookup< config::string_node >("description"); 466 } else { 467 return get_defaults().lookup< config::string_node >("description"); 468 } 469 } 470 471 472 /// Returns execution environment name. 473 /// 474 /// \return Name of configured execution environment. 475 const std::string& 476 model::metadata::execenv(void) const 477 { 478 if (_pimpl->props.is_set("execenv")) { 479 return _pimpl->props.lookup< config::string_node >("execenv"); 480 } else { 481 return get_defaults().lookup< config::string_node >("execenv"); 482 } 483 } 484 485 486 /// Returns execenv jail(8) parameters string to run a test with. 487 /// 488 /// \return String of jail parameters. 489 const std::string& 490 model::metadata::execenv_jail_params(void) const 491 { 492 if (_pimpl->props.is_set("execenv_jail_params")) { 493 return _pimpl->props.lookup< config::string_node >( 494 "execenv_jail_params"); 495 } else { 496 return get_defaults().lookup< config::string_node >( 497 "execenv_jail_params"); 498 } 499 } 500 501 502 /// Returns whether the test has a cleanup part or not. 503 /// 504 /// \return True if there is a cleanup part; false otherwise. 505 bool 506 model::metadata::has_cleanup(void) const 507 { 508 if (_pimpl->props.is_set("has_cleanup")) { 509 return _pimpl->props.lookup< config::bool_node >("has_cleanup"); 510 } else { 511 return get_defaults().lookup< config::bool_node >("has_cleanup"); 512 } 513 } 514 515 516 /// Returns whether the test has a specific execenv apart from default one. 517 /// 518 /// \return True if there is a non-host execenv configured; false otherwise. 519 bool 520 model::metadata::has_execenv(void) const 521 { 522 const std::string& name = execenv(); 523 return !name.empty() && name != engine::execenv::default_execenv_name; 524 } 525 526 527 /// Returns whether the test is exclusive or not. 528 /// 529 /// \return True if the test has to be run on its own, not concurrently with any 530 /// other tests; false otherwise. 531 bool 532 model::metadata::is_exclusive(void) const 533 { 534 if (_pimpl->props.is_set("is_exclusive")) { 535 return _pimpl->props.lookup< config::bool_node >("is_exclusive"); 536 } else { 537 return get_defaults().lookup< config::bool_node >("is_exclusive"); 538 } 539 } 540 541 542 /// Returns the list of configuration variables needed by the test. 543 /// 544 /// \return Set of configuration variables. 545 const model::strings_set& 546 model::metadata::required_configs(void) const 547 { 548 if (_pimpl->props.is_set("required_configs")) { 549 return _pimpl->props.lookup< config::strings_set_node >( 550 "required_configs"); 551 } else { 552 return get_defaults().lookup< config::strings_set_node >( 553 "required_configs"); 554 } 555 } 556 557 558 /// Returns the amount of free disk space required by the test. 559 /// 560 /// \return Number of bytes, or 0 if this does not apply. 561 const units::bytes& 562 model::metadata::required_disk_space(void) const 563 { 564 if (_pimpl->props.is_set("required_disk_space")) { 565 return _pimpl->props.lookup< bytes_node >("required_disk_space"); 566 } else { 567 return get_defaults().lookup< bytes_node >("required_disk_space"); 568 } 569 } 570 571 572 /// Returns the list of files needed by the test. 573 /// 574 /// \return Set of paths. 575 const model::paths_set& 576 model::metadata::required_files(void) const 577 { 578 if (_pimpl->props.is_set("required_files")) { 579 return _pimpl->props.lookup< paths_set_node >("required_files"); 580 } else { 581 return get_defaults().lookup< paths_set_node >("required_files"); 582 } 583 } 584 585 586 /// Returns the amount of memory required by the test. 587 /// 588 /// \return Number of bytes, or 0 if this does not apply. 589 const units::bytes& 590 model::metadata::required_memory(void) const 591 { 592 if (_pimpl->props.is_set("required_memory")) { 593 return _pimpl->props.lookup< bytes_node >("required_memory"); 594 } else { 595 return get_defaults().lookup< bytes_node >("required_memory"); 596 } 597 } 598 599 600 /// Returns the list of programs needed by the test. 601 /// 602 /// \return Set of paths. 603 const model::paths_set& 604 model::metadata::required_programs(void) const 605 { 606 if (_pimpl->props.is_set("required_programs")) { 607 return _pimpl->props.lookup< paths_set_node >("required_programs"); 608 } else { 609 return get_defaults().lookup< paths_set_node >("required_programs"); 610 } 611 } 612 613 614 /// Returns the user required by the test. 615 /// 616 /// \return One of unprivileged, root or empty. 617 const std::string& 618 model::metadata::required_user(void) const 619 { 620 if (_pimpl->props.is_set("required_user")) { 621 return _pimpl->props.lookup< user_node >("required_user"); 622 } else { 623 return get_defaults().lookup< user_node >("required_user"); 624 } 625 } 626 627 628 /// Returns the timeout of the test. 629 /// 630 /// \return A time delta; should be compared to default_timeout to see if it has 631 /// been overriden. 632 const datetime::delta& 633 model::metadata::timeout(void) const 634 { 635 if (_pimpl->props.is_set("timeout")) { 636 return _pimpl->props.lookup< delta_node >("timeout"); 637 } else { 638 return get_defaults().lookup< delta_node >("timeout"); 639 } 640 } 641 642 643 /// Externalizes the metadata to a set of key/value textual pairs. 644 /// 645 /// \return A key/value representation of the metadata. 646 model::properties_map 647 model::metadata::to_properties(void) const 648 { 649 const config::tree fully_specified = get_defaults().combine(_pimpl->props); 650 return fully_specified.all_properties(); 651 } 652 653 654 /// Equality comparator. 655 /// 656 /// \param other The other object to compare this one to. 657 /// 658 /// \return True if this object and other are equal; false otherwise. 659 bool 660 model::metadata::operator==(const metadata& other) const 661 { 662 return _pimpl == other._pimpl || *_pimpl == *other._pimpl; 663 } 664 665 666 /// Inequality comparator. 667 /// 668 /// \param other The other object to compare this one to. 669 /// 670 /// \return True if this object and other are different; false otherwise. 671 bool 672 model::metadata::operator!=(const metadata& other) const 673 { 674 return !(*this == other); 675 } 676 677 678 /// Injects the object into a stream. 679 /// 680 /// \param output The stream into which to inject the object. 681 /// \param object The object to format. 682 /// 683 /// \return The output stream. 684 std::ostream& 685 model::operator<<(std::ostream& output, const metadata& object) 686 { 687 output << "metadata{"; 688 689 bool first = true; 690 const model::properties_map props = object.to_properties(); 691 for (model::properties_map::const_iterator iter = props.begin(); 692 iter != props.end(); ++iter) { 693 if (!first) 694 output << ", "; 695 output << F("%s=%s") % (*iter).first % 696 text::quote((*iter).second, '\''); 697 first = false; 698 } 699 700 output << "}"; 701 return output; 702 } 703 704 705 /// Internal implementation of the metadata_builder class. 706 struct model::metadata_builder::impl : utils::noncopyable { 707 /// Collection of requirements. 708 config::tree props; 709 710 /// Whether we have created a metadata object or not. 711 bool built; 712 713 /// Constructor. 714 impl(void) : 715 built(false) 716 { 717 init_tree(props); 718 } 719 720 /// Constructor. 721 /// 722 /// \param base The base model to construct a copy from. 723 impl(const model::metadata& base) : 724 props(base._pimpl->props.deep_copy()), 725 built(false) 726 { 727 } 728 }; 729 730 731 /// Constructor. 732 model::metadata_builder::metadata_builder(void) : 733 _pimpl(new impl()) 734 { 735 } 736 737 738 /// Constructor. 739 model::metadata_builder::metadata_builder(const model::metadata& base) : 740 _pimpl(new impl(base)) 741 { 742 } 743 744 745 /// Destructor. 746 model::metadata_builder::~metadata_builder(void) 747 { 748 } 749 750 751 /// Accumulates an additional allowed architecture. 752 /// 753 /// \param arch The architecture. 754 /// 755 /// \return A reference to this builder. 756 /// 757 /// \throw model::error If the value is invalid. 758 model::metadata_builder& 759 model::metadata_builder::add_allowed_architecture(const std::string& arch) 760 { 761 if (!_pimpl->props.is_set("allowed_architectures")) { 762 _pimpl->props.set< config::strings_set_node >( 763 "allowed_architectures", 764 get_defaults().lookup< config::strings_set_node >( 765 "allowed_architectures")); 766 } 767 lookup_rw< config::strings_set_node >( 768 _pimpl->props, "allowed_architectures").insert(arch); 769 return *this; 770 } 771 772 773 /// Accumulates an additional allowed platform. 774 /// 775 /// \param platform The platform. 776 /// 777 /// \return A reference to this builder. 778 /// 779 /// \throw model::error If the value is invalid. 780 model::metadata_builder& 781 model::metadata_builder::add_allowed_platform(const std::string& platform) 782 { 783 if (!_pimpl->props.is_set("allowed_platforms")) { 784 _pimpl->props.set< config::strings_set_node >( 785 "allowed_platforms", 786 get_defaults().lookup< config::strings_set_node >( 787 "allowed_platforms")); 788 } 789 lookup_rw< config::strings_set_node >( 790 _pimpl->props, "allowed_platforms").insert(platform); 791 return *this; 792 } 793 794 795 /// Accumulates a single user-defined property. 796 /// 797 /// \param key Name of the property to define. 798 /// \param value Value of the property. 799 /// 800 /// \return A reference to this builder. 801 /// 802 /// \throw model::error If the value is invalid. 803 model::metadata_builder& 804 model::metadata_builder::add_custom(const std::string& key, 805 const std::string& value) 806 { 807 _pimpl->props.set_string(F("custom.%s") % key, value); 808 return *this; 809 } 810 811 812 /// Accumulates an additional required configuration variable. 813 /// 814 /// \param var The name of the configuration variable. 815 /// 816 /// \return A reference to this builder. 817 /// 818 /// \throw model::error If the value is invalid. 819 model::metadata_builder& 820 model::metadata_builder::add_required_config(const std::string& var) 821 { 822 if (!_pimpl->props.is_set("required_configs")) { 823 _pimpl->props.set< config::strings_set_node >( 824 "required_configs", 825 get_defaults().lookup< config::strings_set_node >( 826 "required_configs")); 827 } 828 lookup_rw< config::strings_set_node >( 829 _pimpl->props, "required_configs").insert(var); 830 return *this; 831 } 832 833 834 /// Accumulates an additional required file. 835 /// 836 /// \param path The path to the file. 837 /// 838 /// \return A reference to this builder. 839 /// 840 /// \throw model::error If the value is invalid. 841 model::metadata_builder& 842 model::metadata_builder::add_required_file(const fs::path& path) 843 { 844 if (!_pimpl->props.is_set("required_files")) { 845 _pimpl->props.set< paths_set_node >( 846 "required_files", 847 get_defaults().lookup< paths_set_node >("required_files")); 848 } 849 lookup_rw< paths_set_node >(_pimpl->props, "required_files").insert(path); 850 return *this; 851 } 852 853 854 /// Accumulates an additional required program. 855 /// 856 /// \param path The path to the program. 857 /// 858 /// \return A reference to this builder. 859 /// 860 /// \throw model::error If the value is invalid. 861 model::metadata_builder& 862 model::metadata_builder::add_required_program(const fs::path& path) 863 { 864 if (!_pimpl->props.is_set("required_programs")) { 865 _pimpl->props.set< paths_set_node >( 866 "required_programs", 867 get_defaults().lookup< paths_set_node >("required_programs")); 868 } 869 lookup_rw< paths_set_node >(_pimpl->props, 870 "required_programs").insert(path); 871 return *this; 872 } 873 874 875 /// Sets the architectures allowed by the test. 876 /// 877 /// \param as Set of architectures. 878 /// 879 /// \return A reference to this builder. 880 /// 881 /// \throw model::error If the value is invalid. 882 model::metadata_builder& 883 model::metadata_builder::set_allowed_architectures( 884 const model::strings_set& as) 885 { 886 set< config::strings_set_node >(_pimpl->props, "allowed_architectures", as); 887 return *this; 888 } 889 890 891 /// Sets the platforms allowed by the test. 892 /// 893 /// \return ps Set of platforms. 894 /// 895 /// \return A reference to this builder. 896 /// 897 /// \throw model::error If the value is invalid. 898 model::metadata_builder& 899 model::metadata_builder::set_allowed_platforms(const model::strings_set& ps) 900 { 901 set< config::strings_set_node >(_pimpl->props, "allowed_platforms", ps); 902 return *this; 903 } 904 905 906 /// Sets the user-defined properties. 907 /// 908 /// \param props The custom properties to set. 909 /// 910 /// \return A reference to this builder. 911 /// 912 /// \throw model::error If the value is invalid. 913 model::metadata_builder& 914 model::metadata_builder::set_custom(const model::properties_map& props) 915 { 916 for (model::properties_map::const_iterator iter = props.begin(); 917 iter != props.end(); ++iter) 918 _pimpl->props.set_string(F("custom.%s") % (*iter).first, 919 (*iter).second); 920 return *this; 921 } 922 923 924 /// Sets the description of the test. 925 /// 926 /// \param description Textual description of the test. 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_description(const std::string& description) 933 { 934 set< config::string_node >(_pimpl->props, "description", description); 935 return *this; 936 } 937 938 939 /// Sets execution environment name. 940 /// 941 /// \param name Execution environment name. 942 /// 943 /// \return A reference to this builder. 944 /// 945 /// \throw model::error If the value is invalid. 946 model::metadata_builder& 947 model::metadata_builder::set_execenv(const std::string& name) 948 { 949 set< config::string_node >(_pimpl->props, "execenv", name); 950 return *this; 951 } 952 953 954 /// Sets execenv jail(8) parameters string to run the test with. 955 /// 956 /// \param params String of jail parameters. 957 /// 958 /// \return A reference to this builder. 959 /// 960 /// \throw model::error If the value is invalid. 961 model::metadata_builder& 962 model::metadata_builder::set_execenv_jail_params(const std::string& params) 963 { 964 set< config::string_node >(_pimpl->props, "execenv_jail_params", params); 965 return *this; 966 } 967 968 969 /// Sets whether the test has a cleanup part or not. 970 /// 971 /// \param cleanup True if the test has a cleanup part; false otherwise. 972 /// 973 /// \return A reference to this builder. 974 /// 975 /// \throw model::error If the value is invalid. 976 model::metadata_builder& 977 model::metadata_builder::set_has_cleanup(const bool cleanup) 978 { 979 set< config::bool_node >(_pimpl->props, "has_cleanup", cleanup); 980 return *this; 981 } 982 983 984 /// Sets whether the test is exclusive or not. 985 /// 986 /// \param exclusive True if the test is exclusive; false otherwise. 987 /// 988 /// \return A reference to this builder. 989 /// 990 /// \throw model::error If the value is invalid. 991 model::metadata_builder& 992 model::metadata_builder::set_is_exclusive(const bool exclusive) 993 { 994 set< config::bool_node >(_pimpl->props, "is_exclusive", exclusive); 995 return *this; 996 } 997 998 999 /// Sets the list of configuration variables needed by the test. 1000 /// 1001 /// \param vars Set of configuration variables. 1002 /// 1003 /// \return A reference to this builder. 1004 /// 1005 /// \throw model::error If the value is invalid. 1006 model::metadata_builder& 1007 model::metadata_builder::set_required_configs(const model::strings_set& vars) 1008 { 1009 set< config::strings_set_node >(_pimpl->props, "required_configs", vars); 1010 return *this; 1011 } 1012 1013 1014 /// Sets the amount of free disk space required by the test. 1015 /// 1016 /// \param bytes Number of bytes. 1017 /// 1018 /// \return A reference to this builder. 1019 /// 1020 /// \throw model::error If the value is invalid. 1021 model::metadata_builder& 1022 model::metadata_builder::set_required_disk_space(const units::bytes& bytes) 1023 { 1024 set< bytes_node >(_pimpl->props, "required_disk_space", bytes); 1025 return *this; 1026 } 1027 1028 1029 /// Sets the list of files needed by the test. 1030 /// 1031 /// \param files Set of paths. 1032 /// 1033 /// \return A reference to this builder. 1034 /// 1035 /// \throw model::error If the value is invalid. 1036 model::metadata_builder& 1037 model::metadata_builder::set_required_files(const model::paths_set& files) 1038 { 1039 set< paths_set_node >(_pimpl->props, "required_files", files); 1040 return *this; 1041 } 1042 1043 1044 /// Sets the amount of memory required by the test. 1045 /// 1046 /// \param bytes Number of bytes. 1047 /// 1048 /// \return A reference to this builder. 1049 /// 1050 /// \throw model::error If the value is invalid. 1051 model::metadata_builder& 1052 model::metadata_builder::set_required_memory(const units::bytes& bytes) 1053 { 1054 set< bytes_node >(_pimpl->props, "required_memory", bytes); 1055 return *this; 1056 } 1057 1058 1059 /// Sets the list of programs needed by the test. 1060 /// 1061 /// \param progs Set of paths. 1062 /// 1063 /// \return A reference to this builder. 1064 /// 1065 /// \throw model::error If the value is invalid. 1066 model::metadata_builder& 1067 model::metadata_builder::set_required_programs(const model::paths_set& progs) 1068 { 1069 set< paths_set_node >(_pimpl->props, "required_programs", progs); 1070 return *this; 1071 } 1072 1073 1074 /// Sets the user required by the test. 1075 /// 1076 /// \param user One of unprivileged, root or empty. 1077 /// 1078 /// \return A reference to this builder. 1079 /// 1080 /// \throw model::error If the value is invalid. 1081 model::metadata_builder& 1082 model::metadata_builder::set_required_user(const std::string& user) 1083 { 1084 set< user_node >(_pimpl->props, "required_user", user); 1085 return *this; 1086 } 1087 1088 1089 /// Sets a metadata property by name from its textual representation. 1090 /// 1091 /// \param key The property to set. 1092 /// \param value The value to set the property to. 1093 /// 1094 /// \return A reference to this builder. 1095 /// 1096 /// \throw model::error If the value is invalid or the key does not exist. 1097 model::metadata_builder& 1098 model::metadata_builder::set_string(const std::string& key, 1099 const std::string& value) 1100 { 1101 try { 1102 _pimpl->props.set_string(key, value); 1103 } catch (const config::unknown_key_error& e) { 1104 throw model::format_error(F("Unknown metadata property %s") % key); 1105 } catch (const config::value_error& e) { 1106 throw model::format_error( 1107 F("Invalid value for metadata property %s: %s") % key % e.what()); 1108 } 1109 return *this; 1110 } 1111 1112 1113 /// Sets the timeout of the test. 1114 /// 1115 /// \param timeout The timeout to set. 1116 /// 1117 /// \return A reference to this builder. 1118 /// 1119 /// \throw model::error If the value is invalid. 1120 model::metadata_builder& 1121 model::metadata_builder::set_timeout(const datetime::delta& timeout) 1122 { 1123 set< delta_node >(_pimpl->props, "timeout", timeout); 1124 return *this; 1125 } 1126 1127 1128 /// Creates a new metadata object. 1129 /// 1130 /// \pre This has not yet been called. We only support calling this function 1131 /// once due to the way the internal tree works: we pass around references, not 1132 /// deep copies, so if we allowed a second build, we'd encourage reusing the 1133 /// same builder to construct different metadata objects, and this could have 1134 /// unintended consequences. 1135 /// 1136 /// \return The constructed metadata object. 1137 model::metadata 1138 model::metadata_builder::build(void) const 1139 { 1140 PRE(!_pimpl->built); 1141 _pimpl->built = true; 1142 1143 return metadata(_pimpl->props); 1144 } 1145