diff --git a/.rubocop.yml b/.rubocop.yml index bd1f628..b38e768 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -6,6 +6,9 @@ AllCops: RSpec/DescribeClass: Enabled: false +RSpec/AnyInstance: + Enabled: false + Lint/AmbiguousRegexpLiteral: Enabled: false diff --git a/Gemfile b/Gemfile index 37f3e12..d9d6978 100644 --- a/Gemfile +++ b/Gemfile @@ -3,7 +3,7 @@ source 'https://rubygems.org' gemspec group :test do - gem 'beaker', '2.51.0' if RUBY_VERSION <= '2.1.6' + gem 'beaker', '2.52.0' if RUBY_VERSION <= '2.1.6' gem 'beaker' if RUBY_VERSION > '2.1.6' gem 'bundler', '~> 1.9' gem 'rake', '~> 10.0' diff --git a/README.md b/README.md index f2d9f1a..ded6c42 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,17 @@ This gem is simply an abstraction for the various functions that are performed within the `spec/spec_helper_acceptance.rb` files across the modules to standardise how these are implemented. -### `install_module_on` +### `install_module` +This will call `install_module_on` on the hosts with role 'master'. If there are none, the module will be install on all hosts with the role 'agent', again, if there are none, the module will be installed on all hosts. -This will install the module under test on the specified host using the local source +### `install_module_on` +This will install the module under test on the specified host using the local source. The module name will be derived from the name property of the module's metadata.json file, assuming it is in format author-modulename. ### `install_module_dependencies_on` - This will install a list of dependencies on the specified host either from forge or github, depending on the specified dependencies ### Support - No support is supplied or implied. Use at your own risk. ### TODO - - Implement `install_module_on` - Implement `install_module_dependencies_on` \ No newline at end of file diff --git a/lib/beaker/module_install_helper.rb b/lib/beaker/module_install_helper.rb index 650efb1..628d797 100644 --- a/lib/beaker/module_install_helper.rb +++ b/lib/beaker/module_install_helper.rb @@ -3,9 +3,20 @@ # Provides method for use in module test setup to install the module under # test and it's dependencies on the specified hosts module Beaker::ModuleInstallHelper - # This method will install the module under test on the specified host - def install_module_on(_host) - raise 'Not Implemented Yet' + include Beaker::DSL + + # This method calls the install_module_on method for each host which is a + # master, or if no master is present, on all agent nodes. + def install_module + install_module_on hosts_to_install_module_on + end + + # This method will install the module under test on the specified host(s) from + # the source on the local machine + def install_module_on(host) + copy_module_to(host, + source: @module_source_dir, + module_name: module_name_from_metadata) end # This method will install the module under tests dependencies on the @@ -13,6 +24,53 @@ def install_module_on(_host) def install_module_dependencies_on(_host, _dependencies) raise 'Not Implemented Yet' end + + # This method will return array of all masters. If no masters exist, it will + # return all agent nodes. If no nodes tagged master or agent exist, all nodes + # will be returned + def hosts_to_install_module_on + masters = hosts_with_role(hosts, :master) + return masters unless masters.empty? + + agents = hosts_with_role(hosts, :agent) + return agents unless agents.empty? + + hosts + end + + # This method will read the 'name' attribute from metadata.json file and + # remove the first segment. E.g. puppetlabs-vcsrepo -> vcsrepo + def module_name_from_metadata + res = get_module_name module_metadata['name'] + raise 'Error getting module name' unless res + res[1] + end + + # This method uses the module_source_directory path to read the metadata.json + # file into a json array + def module_metadata + metadata_path = "#{@module_source_dir}/metadata.json" + unless File.exist?(metadata_path) + raise "Error loading metadata.json file from #{@module_source_dir}" + end + JSON.parse(File.read(metadata_path)) + end + + # Use this property to store the module_source_dir, so we don't traverse + # the tree every time + def get_module_source_directory(search_in) + module_source_dir = nil + # here we go up the file tree and search the directories for a + # valid metadata.json + while module_source_dir.nil? && search_in.length > 1 + # remove last segment (file or folder, doesn't matter) + search_in = search_in.split('/')[0...-1].join('/') + module_source_dir = search_in if File.exist?("#{search_in}/metadata.json") + end + module_source_dir + end end include Beaker::ModuleInstallHelper +# Use the caller (requirer) of this file to begin search for module source dir +@module_source_dir = get_module_source_directory caller[0][/[^:]+/] diff --git a/spec/unit/beaker/module_install_helper_spec.rb b/spec/unit/beaker/module_install_helper_spec.rb index 5ddbe2a..22bf44d 100644 --- a/spec/unit/beaker/module_install_helper_spec.rb +++ b/spec/unit/beaker/module_install_helper_spec.rb @@ -1,12 +1,87 @@ require 'spec_helper' -describe 'ModuleInstallHelper' do +describe Beaker::ModuleInstallHelper do + context 'hosts_to_install_module_on with split master/agent setup' do + let(:hosts) do + [ + { 'roles' => %w(master database dashboard classifier) }, + { 'roles' => ['agent'] } + ] + end + + it 'returns a node with master role' do + expect(hosts_to_install_module_on.first['roles']).to include 'master' + end + end + + context 'hosts_to_install_module_on with split master/agent setup' do + let(:hosts) { [{ 'roles' => ['agent'] }] } + + it 'returns a node with master role' do + expect(hosts_to_install_module_on.first['roles']).to include 'agent' + end + end + + context 'module_name_from_metadata' do + let(:module_metadata) { { 'name' => 'puppetlabs-vcsrepo' } } + + it 'Removes author from name' do + res = module_name_from_metadata + expect(res).to eq('vcsrepo') + end + end + + context 'module_metadata' do + before do + @module_source_dir = '/a/b/c/d' + allow(File).to receive(:exist?).and_return(true) + allow(File).to receive(:read).and_return('{"name": "puppetlabs-vcsrepo"}') + end + + it 'Returns hash with correct data' do + expect(module_metadata['name']).to eq('puppetlabs-vcsrepo') + end + end + + context 'get_module_source_directory' do + let(:search_in) { '/a/b/c/d/e/f/g/h.rb' } + + before do + allow(File).to receive(:exist?).with(anything).and_return(false) + allow(File).to receive(:exist?).with('/a/metadata.json').and_return(true) + end + + it 'traverses file tree until it finds a folder containing metadata.json' do + expect(get_module_source_directory(search_in)).to eq('/a') + end + end + context 'install_module_on' do - it 'raises not implemented error' do - expect { install_module_on(nil) } - .to raise_error /Not Implemented Yet/ + let(:module_source_dir) { '/a/b/c/d' } + + before do + @module_source_dir = '/a/b/c/d' + allow(File).to receive(:exist?).and_return(true) + allow(File).to receive(:read).and_return('{"name": "puppetlabs-vcsrepo"}') + + allow_any_instance_of(Beaker::DSL::InstallUtils::ModuleUtils) + .to receive(:copy_module_to) + .with(anything) + .and_return(false) + + allow_any_instance_of(Beaker::DSL::InstallUtils::ModuleUtils) + .to receive(:copy_module_to) + .with(host, source: module_source_dir, module_name: 'vcsrepo') + .and_return(true) + end + + let(:host) { { 'roles' => %w(master database dashboard classifier) } } + + it 'copy module to given host' do + expect(install_module_on(host)).to be true end end + context 'install_module_dependencies_on' do it 'raises not implemented error' do expect { install_module_dependencies_on(nil, nil) }