lib/beaker/dsl/install_utils.rb in beaker-1.18.0 vs lib/beaker/dsl/install_utils.rb in beaker-1.19.0

- old
+ new

@@ -23,10 +23,13 @@ GitURI = %r{^(git|https?|file)://|^git@|^gitmirror@} # Github's ssh signature for cloning via ssh GitHubSig = 'github.com,207.97.227.239 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' + # The directories in the module directory that will not be scp-ed to the test system when using `copy_module_to` + PUPPET_MODULE_INSTALL_IGNORE = ['.bundle', '.git', '.idea', '.vagrant', '.vendor', 'acceptance', 'spec', 'tests', 'log'] + # @param [String] uri A uri in the format of <git uri>#<revision> # the `git://`, `http://`, `https://`, and ssh # (if cloning as the remote git user) protocols # are valid for <git uri> # @@ -1076,8 +1079,221 @@ end #send in the global options hash do_higgs_install higgs_host, options end + # Install the desired module on all hosts using either the PMT or a + # staging forge + # + # @see install_dev_puppet_module + def install_dev_puppet_module_on( host, opts ) + if options[:forge_host] + with_forge_stubbed_on( host ) do + install_puppet_module_via_pmt_on( host, opts ) + end + else + copy_module_to( host, opts ) + end + end + alias :puppet_module_install_on :install_dev_puppet_module_on + + # Install the desired module on all hosts using either the PMT or a + # staging forge + # + # Passes options through to either `install_puppet_module_via_pmt_on` + # or `copy_module_to` + # + # @param opts [Hash] + # + # @example Installing a module from the local directory + # install_dev_puppet_module( :source => './', :module_name => 'concat' ) + # + # @example Installing a module from a staging forge + # options[:forge_host] = 'my-forge-api.example.com' + # install_dev_puppet_module( :source => './', :module_name => 'concat' ) + # + # @see install_puppet_module_via_pmt + # @see copy_module_to + def install_dev_puppet_module( opts ) + block_on( hosts ) {|h| install_dev_puppet_module_on( h, opts ) } + end + alias :puppet_module_install :install_dev_puppet_module + + # Install the desired module with the PMT on a given host + # + # @param opts [Hash] + # @option opts [String] :module_name The short name of the module to be installed + # @option opts [String] :version The version of the module to be installed + def install_puppet_module_via_pmt_on( host, opts = {} ) + block_on host do |h| + version_info = opts[:version] ? "-v #{opts[:version]}" : "" + if opts[:source] + author_name, module_name = parse_for_modulename( opts[:source] ) + modname = "#{author_name}-#{module_name}" + else + modname = opts[:module_name] + end + + on h, puppet("module install #{modname} #{version_info}") + end + end + + # Install the desired module with the PMT on all known hosts + # @see #install_puppet_module_via_pmt_on + def install_puppet_module_via_pmt( opts = {} ) + install_puppet_module_via_pmt_on(hosts, opts) + end + + # Install local module for acceptance testing + # should be used as a presuite to ensure local module is copied to the hosts you want, particularly masters + # @api dsl + # @param [Host, Array<Host>, String, Symbol] host + # One or more hosts to act upon, + # or a role (String or Symbol) that identifies one or more hosts. + # @option opts [String] :source ('./') + # The current directory where the module sits, otherwise will try + # and walk the tree to figure out + # @option opts [String] :module_name (nil) + # Name which the module should be installed under, please do not include author, + # if none is provided it will attempt to parse the metadata.json and then the Modulefile to determine + # the name of the module + # @option opts [String] :target_module_path (host['distmoduledir']/modules) + # Location where the module should be installed, will default + # to host['distmoduledir']/modules + # @option opts [Array] :ignore_list + # @raise [ArgumentError] if not host is provided or module_name is not provided and can not be found in Modulefile + # + def copy_module_to(host, opts = {}) + opts = {:source => './', + :target_module_path => host['distmoduledir'], + :ignore_list => PUPPET_MODULE_INSTALL_IGNORE}.merge(opts) + ignore_list = build_ignore_list(opts) + target_module_dir = on( host, "echo #{opts[:target_module_path]}" ).stdout.chomp + source = File.expand_path( opts[:source] ) + if opts.has_key?(:module_name) + module_name = opts[:module_name] + else + _, module_name = parse_for_modulename( source ) + end + scp_to host, source, File.join(target_module_dir, module_name), {:ignore => ignore_list} + end + alias :copy_root_module_to :copy_module_to + + # Install a package on a host + # + # @param [Host] host A host object + # @param [String] package_name Name of the package to install + # + # @return [Result] An object representing the outcome of *install command*. + def install_package host, package_name, package_version = nil + host.install_package package_name, '', package_version + end + + # Check to see if a package is installed on a remote host + # + # @param [Host] host A host object + # @param [String] package_name Name of the package to check for. + # + # @return [Boolean] true/false if the package is found + def check_for_package host, package_name + host.check_for_package package_name + end + + # Upgrade a package on a host. The package must already be installed + # + # @param [Host] host A host object + # @param [String] package_name Name of the package to install + # + # @return [Result] An object representing the outcome of *upgrade command*. + def upgrade_package host, package_name + host.upgrade_package package_name + end + + #Recursive method for finding the module root + # Assumes that a Modulefile exists + # @param [String] possible_module_directory + # will look for Modulefile and if none found go up one level and try again until root is reached + # + # @return [String,nil] + def parse_for_moduleroot(possible_module_directory) + if File.exists?("#{possible_module_directory}/Modulefile") + possible_module_directory + elsif possible_module_directory === '/' + logger.error "At root, can't parse for another directory" + nil + else + logger.debug "No Modulefile found at #{possible_module_directory}, moving up" + parse_for_moduleroot File.expand_path(File.join(possible_module_directory,'..')) + end + end + + + #Parse root directory of a module for module name + # Searches for metadata.json and then if none found, Modulefile and parses for the Name attribute + # @param [String] root_module_dir + # @return [String] module name + def parse_for_modulename(root_module_dir) + author_name, module_name = nil, nil + if File.exists?("#{root_module_dir}/metadata.json") + logger.debug "Attempting to parse Modulename from metadata.json" + module_json = JSON.parse(File.read "#{root_module_dir}/metadata.json") + if(module_json.has_key?('name')) + author_name, module_name = get_module_name(module_json['name']) + end + end + if !module_name && File.exists?("#{root_module_dir}/Modulefile") + logger.debug "Attempting to parse Modulename from Modulefile" + if /^name\s+'?(\w+-\w+)'?\s*$/i.match(File.read("#{root_module_dir}/Modulefile")) + author_name, module_name = get_module_name(Regexp.last_match[1]) + end + end + if !module_name && !author_name + logger.debug "Unable to determine name, returning null" + end + return author_name, module_name + end + + #Parse modulename from the pattern 'Auther-ModuleName' + # + # @param [String] author_module_name <Author>-<ModuleName> pattern + # + # @return [String,nil] + # + def get_module_name(author_module_name) + split_name = split_author_modulename(author_module_name) + if split_name + return split_name[:author], split_name[:module] + end + end + + #Split the Author-Name into a hash + # @param [String] author_module_attr + # + # @return [Hash<Symbol,String>,nil] :author and :module symbols will be returned + # + def split_author_modulename(author_module_attr) + result = /(\w+)-(\w+)/.match(author_module_attr) + if result + {:author => result[1], :module => result[2]} + else + nil + end + end + + # Build an array list of files/directories to ignore when pushing to remote host + # Automatically adds '..' and '.' to array. If not opts of :ignore list is provided + # it will use the static variable PUPPET_MODULE_INSTALL_IGNORE + # + # @param opts [Hash] + # @option opts [Array] :ignore_list A list of files/directories to ignore + def build_ignore_list(opts = {}) + ignore_list = opts[:ignore_list] || PUPPET_MODULE_INSTALL_IGNORE + if !ignore_list.kind_of?(Array) || ignore_list.nil? + raise ArgumentError "Ignore list must be an Array" + end + ignore_list << '.' unless ignore_list.include? '.' + ignore_list << '..' unless ignore_list.include? '..' + ignore_list + end end end end