require 'spec_helper' class ClassMixedWithDSLInstallUtils include Beaker::DSL::InstallUtils include Beaker::DSL::Wrappers include Beaker::DSL::Helpers include Beaker::DSL::Structure include Beaker::DSL::Roles include Beaker::DSL::Patterns include Beaker::DSL::InstallUtils::ModuleUtils def logger @logger ||= RSpec::Mocks::Double.new('logger').as_null_object end end describe ClassMixedWithDSLInstallUtils do let(:presets) { Beaker::Options::Presets.new } let(:opts) { presets.presets.merge(presets.env_vars) } let(:basic_hosts) do make_hosts({ pe_ver: '3.0', platform: 'linux', roles: ['agent'], }, 4) end let(:hosts) do basic_hosts[0][:roles] = %w[master database dashboard] basic_hosts[1][:platform] = 'windows' basic_hosts[2][:platform] = 'osx-10.9-x86_64' basic_hosts[3][:platform] = 'eos' basic_hosts end let(:hosts_sorted) { [hosts[1], hosts[0], hosts[2], hosts[3]] } describe '#install_dev_puppet_module_on' do context 'having set allow( a ).to receive forge' do it 'stubs the forge on the host' do master = hosts.first allow(subject).to receive(:options).and_return({ forge_host: 'ahost.com' }) subject.install_dev_puppet_module_on(master, { source: '/module', module_name: 'test' }) end it 'installs via #install_puppet_module_via_pmt' do master = hosts.first allow(subject).to receive(:options).and_return({ forge_host: 'ahost.com' }) expect(subject).to receive(:install_puppet_module_via_pmt_on) subject.install_dev_puppet_module_on(master, { source: '/module', module_name: 'test' }) end end context 'without allow( a ).to receive forge (default)' do it 'calls copy_module_to to get the module on the SUT' do master = hosts.first allow(subject).to receive(:options).and_return({}) expect(subject).to receive(:copy_module_to) subject.install_dev_puppet_module_on(master, { source: '/module', module_name: 'test' }) end end end describe '#install_dev_puppet_module' do it 'delegates to #install_dev_puppet_module_on with the hosts list' do allow(subject).to receive(:hosts).and_return(hosts) allow(subject).to receive(:options).and_return({}) hosts.each do |host| expect(subject).to receive(:install_dev_puppet_module_on) .with(host, { source: '/module', module_name: 'test' }) end subject.install_dev_puppet_module({ source: '/module', module_name: 'test' }) end end describe '#install_puppet_module_via_pmt_on' do it 'installs module via puppet module tool' do allow(subject).to receive(:hosts).and_return(hosts) allow(subject).to receive(:options).and_return({}) master = hosts.first allow(subject).to receive(:on).once expect(subject).to receive(:puppet).with('module install test ', {}).once subject.install_puppet_module_via_pmt_on(master, { module_name: 'test' }) end it 'takes the trace option and passes it down correctly' do allow(subject).to receive(:hosts).and_return(hosts) allow(subject).to receive(:options).and_return({}) master = hosts.first trace_opts = { trace: nil } master['default_module_install_opts'] = trace_opts allow(subject).to receive(:on).once expect(subject).to receive(:puppet).with('module install test ', trace_opts).once subject.install_puppet_module_via_pmt_on(master, { module_name: 'test' }) end it 'takes the forge_host option as a hostname and passes it down correctly' do allow(subject).to receive(:hosts).and_return(hosts) allow(subject).to receive(:options).and_return({ forge_host: 'forge.example.com' }) master = hosts.first forge_opts = { module_repository: 'https://forge.example.com' } allow(subject).to receive(:on).once expect(subject).to receive(:puppet).with('module install test ', forge_opts).once subject.install_puppet_module_via_pmt_on(master, { module_name: 'test' }) end it 'takes the forge_host option as a url and passes it down correctly' do allow(subject).to receive(:hosts).and_return(hosts) allow(subject).to receive(:options).and_return({ forge_host: 'http://forge.example.com' }) master = hosts.first forge_opts = { module_repository: 'http://forge.example.com' } allow(subject).to receive(:on).once expect(subject).to receive(:puppet).with('module install test ', forge_opts).once subject.install_puppet_module_via_pmt_on(master, { module_name: 'test' }) end end describe '#install_puppet_module_via_pmt' do it 'delegates to #install_puppet_module_via_pmt with the hosts list' do allow(subject).to receive(:hosts).and_return(hosts) expect(subject).to receive(:install_puppet_module_via_pmt_on).with(hosts, { source: '/module', module_name: 'test', }).once subject.install_puppet_module_via_pmt({ source: '/module', module_name: 'test' }) end end describe 'copy_module_to' do let(:ignore_list) { Beaker::DSL::InstallUtils::ModuleUtils::PUPPET_MODULE_INSTALL_IGNORE } let(:source) { File.expand_path('./') } let(:target) { '/etc/puppetlabs/puppet/modules/testmodule' } let(:module_parse_name) { 'testmodule' } shared_examples 'copy_module_to' do |opts| it { host = double('host') allow(host).to receive(:[]).with('distmoduledir').and_return('/etc/puppetlabs/puppet/modules') allow(host).to receive(:is_powershell?).and_return(false) result = double stdout = target.split('/')[0..-2].join('/') + "\n" allow(result).to receive(:stdout).and_return(stdout) expect(subject).to receive(:on).with(host, "echo #{File.dirname(target)}").and_return(result) allow(Dir).to receive(:getpwd).and_return(source) allow(subject).to receive(:parse_for_moduleroot).and_return(source) if module_parse_name allow(subject).to receive(:parse_for_modulename).with(any_args).and_return(module_parse_name) else expect(subject).to_not receive(:parse_for_modulename) end allow(File).to receive(:exist?).with(any_args).and_return(false) allow(File).to receive(:directory?).with(any_args).and_return(false) expect(subject).to receive(:scp_to).with(host, source, File.dirname(target), { ignore: ignore_list }) expect(host).to receive(:mv).with(File.join(File.dirname(target), File.basename(source)), target) if opts.nil? subject.copy_module_to(host) else subject.copy_module_to(host, opts) end } end describe 'should call scp with the correct info, with only providing host' do let(:target) { '/etc/puppetlabs/puppet/modules/testmodule' } it_should_behave_like 'copy_module_to', module_name: 'testmodule' end describe 'should call scp with the correct info, when specifying the modulename' do let(:target) { '/etc/puppetlabs/puppet/modules/bogusmodule' } let(:module_parse_name) { false } it_should_behave_like 'copy_module_to', { module_name: 'bogusmodule' } end describe 'should call scp with the correct info, when specifying the target to a different path' do target = '/opt/shared/puppet/modules' let(:target) { "#{target}/testmodule" } it_should_behave_like 'copy_module_to', { target_module_path: target, module_name: 'testmodule' } end describe 'should accept multiple hosts when' do it 'used in a default manner' do allow(subject).to receive(:build_ignore_list).and_return([]) allow(subject).to receive(:parse_for_modulename).and_return([nil, 'modulename']) allow(subject).to receive(:on).and_return(double.as_null_object) expect(subject).to receive(:scp_to).exactly(4).times subject.copy_module_to(hosts) end end describe 'non-cygwin windows' do it 'should have different commands than cygwin' do host = double('host') allow(host).to receive(:[]).with('platform').and_return('windows') allow(host).to receive(:[]).with('distmoduledir').and_return('C:\\ProgramData\\PuppetLabs\\puppet\\etc\\modules') allow(host).to receive(:is_powershell?).and_return(true) result = double allow(result).to receive(:stdout).and_return('C:\\ProgramData\\PuppetLabs\\puppet\\etc\\modules') expect(subject).to receive(:on).with(host, 'echo C:\\ProgramData\\PuppetLabs\\puppet\\etc\\modules').and_return(result) expect(subject).to receive(:scp_to).with(host, '/opt/testmodule2', 'C:\\ProgramData\\PuppetLabs\\puppet\\etc\\modules', { ignore: ignore_list }) expect(host).to receive(:mv).with('C:\\ProgramData\\PuppetLabs\\puppet\\etc\\modules\\testmodule2', 'C:\\ProgramData\\PuppetLabs\\puppet\\etc\\modules\\testmodule') subject.copy_module_to(host, { module_name: 'testmodule', source: '/opt/testmodule2' }) end end end describe 'split_author_modulename' do it 'should return a correct modulename' do result = subject.split_author_modulename('myname-test_43_module') expect(result[:author]).to eq('myname') expect(result[:module]).to eq('test_43_module') end end describe 'get_module_name' do it 'should return an array of author and modulename' do expect(subject.get_module_name('myname-test_43_module')).to eq(%w[myname test_43_module]) end it 'should return nil for invalid names' do expect(subject.get_module_name('myname-')).to eq(nil) end end describe 'get_target_module_path' do let(:host) do host = make_host("echo '/explicit/modulepath'", { stdout: "/explicit/modulepath\n" }) allow(host).to receive(:puppet).and_return(puppet) host end let(:puppet) do puppet = instance_double('PuppetConfigReader') allow(puppet).to receive(:[]).with('basemodulepath').and_return(basemodulepath) puppet end context 'no modulepath' do let(:basemodulepath) { '' } it 'should prefer the explicit path' do expect(subject.get_target_module_path(host, '/explicit/modulepath')).to eq('/explicit/modulepath') expect(puppet).not_to have_received(:[]).with('basemodulepath') end it 'should raise an error if no directory is found' do expect do subject.get_target_module_path(host) end.to raise_error(ArgumentError, 'Unable to find target module path to copy to') expect(puppet).to have_received(:[]).with('basemodulepath') end end context 'single modulepath' do let(:basemodulepath) { '/path/to/modulepath' } it 'should prefer the explicit path' do expect(subject.get_target_module_path(host, '/explicit/modulepath')).to eq('/explicit/modulepath') expect(puppet).not_to have_received(:[]).with('basemodulepath') end it 'should fall back to autodetection' do expect(subject.get_target_module_path(host)).to eq('/path/to/modulepath') expect(puppet).to have_received(:[]).with('basemodulepath') end end context 'multiple directories' do let(:basemodulepath) { '/home/puppet/modules:/path/to/modulepath' } it 'should prefer the explicit path' do expect(subject.get_target_module_path(host, '/explicit/modulepath')).to eq('/explicit/modulepath') expect(puppet).not_to have_received(:[]).with('basemodulepath') end it 'should return the first directory' do expect(subject.get_target_module_path(host)).to eq('/home/puppet/modules') expect(puppet).to have_received(:[]).with('basemodulepath') end end end describe 'parse_for_modulename' do directory = '/testfilepath/myname-testmodule' it 'should return name from metadata.json' do allow(File).to receive(:exist?).with("#{directory}/metadata.json").and_return(true) allow(File).to receive(:read).with("#{directory}/metadata.json").and_return(' {"name":"myname-testmodule"} ') expect(subject.logger).to receive(:debug).with('Attempting to parse Modulename from metadata.json') expect(subject.logger).to_not receive(:debug).with('Unable to determine name, returning null') expect(subject.parse_for_modulename(directory)).to eq(%w[myname testmodule]) end it 'should return name from Modulefile' do allow(File).to receive(:exist?).with("#{directory}/metadata.json").and_return(false) allow(File).to receive(:exist?).with("#{directory}/Modulefile").and_return(true) allow(File).to receive(:read).with("#{directory}/Modulefile").and_return("name 'myname-testmodule' \nauthor 'myname'") expect(subject.logger).to receive(:debug).with('Attempting to parse Modulename from Modulefile') expect(subject.logger).to_not receive(:debug).with('Unable to determine name, returning null') expect(subject.parse_for_modulename(directory)).to eq(%w[myname testmodule]) end end describe 'parse_for_module_root' do directory = '/testfilepath/myname-testmodule' describe 'stops searching when either' do it 'finds a Modulefile' do allow(File).to receive(:exist?).and_return(false) allow(File).to receive(:exist?).with("#{directory}/Modulefile").and_return(true) expect(subject.logger).to_not receive(:debug).with("At root, can't parse for another directory") expect(subject.logger).to receive(:debug).with("No Modulefile or metadata.json found at #{directory}/acceptance, moving up") expect(subject.parse_for_moduleroot("#{directory}/acceptance")).to eq(directory) end it 'finds a metadata.json file' do allow(File).to receive(:exist?).and_return(false) allow(File).to receive(:exist?).with("#{directory}/metadata.json").and_return(true) expect(subject.logger).to_not receive(:debug).with("At root, can't parse for another directory") expect(subject.logger).to receive(:debug).with("No Modulefile or metadata.json found at #{directory}/acceptance, moving up") expect(subject.parse_for_moduleroot("#{directory}/acceptance")).to eq(directory) end end it 'should recersively go up the directory to find the module files' do allow(File).to receive(:exist?).and_return(false) expect(subject.logger).to receive(:debug).with("No Modulefile or metadata.json found at #{directory}, moving up") expect(subject.logger).to receive(:error).with("At root, can't parse for another directory") expect(subject.parse_for_moduleroot(directory)).to eq(nil) end end end