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