diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index d3e6a06a7..9b6e656bb 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -29,7 +29,7 @@ jobs: name: RuboCop TODO steps: - uses: actions/checkout@v1 - - uses: gimmyxd/rtc-action@0.2.0 + - uses: gimmyxd/rtc-action@0.2.1 env: RTC_TOKEN: ${{ secrets.RTC_TOKEN }} UPDATE_PR: false diff --git a/lib/facts/aix/aio_agent_version.rb b/lib/facts/aix/aio_agent_version.rb new file mode 100644 index 000000000..48a98a366 --- /dev/null +++ b/lib/facts/aix/aio_agent_version.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Facts + module Aix + class AioAgentVersion + FACT_NAME = 'aio_agent_version' + + def call_the_resolver + fact_value = Facter::Resolvers::AioAgentVersion.resolve(:aio_agent_version) + Facter::ResolvedFact.new(FACT_NAME, fact_value) + end + end + end +end diff --git a/lib/facts/linux/aio_agent_version.rb b/lib/facts/linux/aio_agent_version.rb new file mode 100644 index 000000000..cd9000a99 --- /dev/null +++ b/lib/facts/linux/aio_agent_version.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Facts + module Linux + class AioAgentVersion + FACT_NAME = 'aio_agent_version' + + def call_the_resolver + fact_value = Facter::Resolvers::AioAgentVersion.resolve(:aio_agent_version) + Facter::ResolvedFact.new(FACT_NAME, fact_value) + end + end + end +end diff --git a/lib/facts/macosx/aio_agent_version.rb b/lib/facts/macosx/aio_agent_version.rb new file mode 100644 index 000000000..2ca884ae8 --- /dev/null +++ b/lib/facts/macosx/aio_agent_version.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Facts + module Macosx + class AioAgentVersion + FACT_NAME = 'aio_agent_version' + + def call_the_resolver + fact_value = Facter::Resolvers::AioAgentVersion.resolve(:aio_agent_version) + Facter::ResolvedFact.new(FACT_NAME, fact_value) + end + end + end +end diff --git a/lib/facts/solaris/aio_agent_version.rb b/lib/facts/solaris/aio_agent_version.rb new file mode 100644 index 000000000..b595f792c --- /dev/null +++ b/lib/facts/solaris/aio_agent_version.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Facts + module Solaris + class AioAgentVersion + FACT_NAME = 'aio_agent_version' + + def call_the_resolver + fact_value = Facter::Resolvers::AioAgentVersion.resolve(:aio_agent_version) + Facter::ResolvedFact.new(FACT_NAME, fact_value) + end + end + end +end diff --git a/lib/facts/windows/aio_agent_version.rb b/lib/facts/windows/aio_agent_version.rb new file mode 100644 index 000000000..dbb954efb --- /dev/null +++ b/lib/facts/windows/aio_agent_version.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Facts + module Windows + class AioAgentVersion + FACT_NAME = 'aio_agent_version' + + def call_the_resolver + fact_value = Facter::Resolvers::Windows::AioAgentVersion.resolve(:aio_agent_version) + Facter::ResolvedFact.new(FACT_NAME, fact_value) + end + end + end +end diff --git a/lib/resolvers/agent_resolver.rb b/lib/resolvers/aio_agent_version.rb similarity index 53% rename from lib/resolvers/agent_resolver.rb rename to lib/resolvers/aio_agent_version.rb index 00166b22e..5cc1aedd1 100644 --- a/lib/resolvers/agent_resolver.rb +++ b/lib/resolvers/aio_agent_version.rb @@ -2,7 +2,7 @@ module Facter module Resolvers - class Agent < BaseResolver + class AioAgentVersion < BaseResolver @semaphore = Mutex.new @fact_list ||= {} @@ -14,8 +14,9 @@ def post_resolve(fact_name) end def read_agent_version - version_file = ::File.join(ROOT_DIR, 'lib/puppet/VERSION') - @fact_list[:aio_agent_version] = ::File.read(version_file) + aio_agent_version = Util::FileHelper.safe_read('/opt/puppetlabs/puppet/VERSION', nil).chomp + aio_agent_version = aio_agent_version&.match(/^\d+\.\d+\.\d+(\.\d+){0,2}/)&.to_s + @fact_list[:aio_agent_version] = aio_agent_version end end end diff --git a/lib/resolvers/windows/aio_agent_version.rb b/lib/resolvers/windows/aio_agent_version.rb new file mode 100644 index 000000000..d4ffe2474 --- /dev/null +++ b/lib/resolvers/windows/aio_agent_version.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module Facter + module Resolvers + module Windows + class AioAgentVersion < BaseResolver + REGISTRY_PATH = 'SOFTWARE\\Puppet Labs\\Puppet' + @semaphore = Mutex.new + @fact_list ||= {} + + class << self + private + + def post_resolve(fact_name) + @fact_list.fetch(fact_name) { read_version(fact_name) } + end + + def read_version(fact_name) + ::Win32::Registry::HKEY_LOCAL_MACHINE.open(REGISTRY_PATH) do |reg| + build_fact_list(reg) + end + + @fact_list[fact_name] + rescue Win32::Registry::Error + log.debug("The registry path #{REGISTRY_PATH} does not exist") + end + + def build_fact_list(reg) + puppet_aio_path = read_for_64_bit(reg) || read_for_32_bit(reg) + + return if puppet_aio_path.nil? || puppet_aio_path.empty? + + puppet_aio_version_path = File.join(puppet_aio_path, 'VERSION') + aio_agent_version = Util::FileHelper.safe_read(puppet_aio_version_path, nil)&.chomp + + @fact_list[:aio_agent_version] = aio_agent_version&.match(/^\d+\.\d+\.\d+(\.\d+){0,2}/)&.to_s + end + + def read_for_64_bit(reg) + reg.read('RememberedInstallDir64')[1] + rescue Win32::Registry::Error + log.debug('Could not read Puppet AIO path from 64 bit registry') + nil + end + + def read_for_32_bit(reg) + reg.read('RememberedInstallDir')[1] + rescue Win32::Registry::Error + log.debug('Could not read Puppet AIO path from 32 bit registry') + nil + end + end + end + end + end +end diff --git a/spec/facter/facts/aix/aio_agent_version_spec.rb b/spec/facter/facts/aix/aio_agent_version_spec.rb new file mode 100644 index 000000000..705185df4 --- /dev/null +++ b/spec/facter/facts/aix/aio_agent_version_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +describe Facts::Aix::AioAgentVersion do + describe '#call_the_resolver' do + subject(:fact) { Facts::Aix::AioAgentVersion.new } + + let(:value) { '1.2.3' } + + before do + allow(Facter::Resolvers::AioAgentVersion).to receive(:resolve).with(:aio_agent_version).and_return(value) + end + + it 'calls Facter::Resolvers::Agent' do + fact.call_the_resolver + expect(Facter::Resolvers::AioAgentVersion).to have_received(:resolve).with(:aio_agent_version) + end + + it 'returns aio_agent_version fact' do + expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \ + have_attributes(name: 'aio_agent_version', value: value) + end + end +end diff --git a/spec/facter/facts/linux/aio_agent_version_spec.rb b/spec/facter/facts/linux/aio_agent_version_spec.rb new file mode 100644 index 000000000..07ddbe58d --- /dev/null +++ b/spec/facter/facts/linux/aio_agent_version_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +describe Facts::Linux::AioAgentVersion do + describe '#call_the_resolver' do + subject(:fact) { Facts::Linux::AioAgentVersion.new } + + let(:value) { '1.2.3' } + + before do + allow(Facter::Resolvers::AioAgentVersion).to receive(:resolve).with(:aio_agent_version).and_return(value) + end + + it 'calls Facter::Resolvers::Agent' do + fact.call_the_resolver + expect(Facter::Resolvers::AioAgentVersion).to have_received(:resolve).with(:aio_agent_version) + end + + it 'returns aio_agent_version fact' do + expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \ + have_attributes(name: 'aio_agent_version', value: value) + end + end +end diff --git a/spec/facter/facts/macosx/aio_agent_version_spec.rb b/spec/facter/facts/macosx/aio_agent_version_spec.rb new file mode 100644 index 000000000..798fff8ad --- /dev/null +++ b/spec/facter/facts/macosx/aio_agent_version_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +describe Facts::Macosx::AioAgentVersion do + describe '#call_the_resolver' do + subject(:fact) { Facts::Macosx::AioAgentVersion.new } + + let(:value) { '1.2.3' } + + before do + allow(Facter::Resolvers::AioAgentVersion).to receive(:resolve).with(:aio_agent_version).and_return(value) + end + + it 'calls Facter::Resolvers::Agent' do + fact.call_the_resolver + expect(Facter::Resolvers::AioAgentVersion).to have_received(:resolve).with(:aio_agent_version) + end + + it 'returns aio_agent_version fact' do + expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \ + have_attributes(name: 'aio_agent_version', value: value) + end + end +end diff --git a/spec/facter/facts/solaris/aio_agent_version_spec.rb b/spec/facter/facts/solaris/aio_agent_version_spec.rb new file mode 100644 index 000000000..d5cb46c71 --- /dev/null +++ b/spec/facter/facts/solaris/aio_agent_version_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +describe Facts::Solaris::AioAgentVersion do + describe '#call_the_resolver' do + subject(:fact) { Facts::Solaris::AioAgentVersion.new } + + let(:value) { '1.2.3' } + + before do + allow(Facter::Resolvers::AioAgentVersion).to receive(:resolve).with(:aio_agent_version).and_return(value) + end + + it 'calls Facter::Resolvers::Agent' do + fact.call_the_resolver + expect(Facter::Resolvers::AioAgentVersion).to have_received(:resolve).with(:aio_agent_version) + end + + it 'returns aio_agent_version fact' do + expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \ + have_attributes(name: 'aio_agent_version', value: value) + end + end +end diff --git a/spec/facter/resolvers/aio_agent_version_spec.rb b/spec/facter/resolvers/aio_agent_version_spec.rb index 7e8c745e5..c382435d8 100644 --- a/spec/facter/resolvers/aio_agent_version_spec.rb +++ b/spec/facter/resolvers/aio_agent_version_spec.rb @@ -1,10 +1,39 @@ # frozen_string_literal: true -describe Facter::Resolvers::Agent do +describe Facter::Resolvers::AioAgentVersion do describe '#resolve' do + before do + allow(Facter::Util::FileHelper) + .to receive(:safe_read) + .with('/opt/puppetlabs/puppet/VERSION', nil) + .and_return('7.0.1') + end + + after do + Facter::Resolvers::AioAgentVersion.invalidate_cache + end + + it 'calls FileHelper.safe_read' do + Facter::Resolvers::AioAgentVersion.resolve(:aio_agent_version) + + expect(Facter::Util::FileHelper).to have_received(:safe_read).with('/opt/puppetlabs/puppet/VERSION', nil) + end + it 'detects puppet version' do - allow(File).to receive(:read).with("#{ROOT_DIR}/lib/puppet/VERSION").and_return('7.0.1') - expect(Facter::Resolvers::Agent.resolve(:aio_agent_version)).to eql('7.0.1') + expect(Facter::Resolvers::AioAgentVersion.resolve(:aio_agent_version)).to eql('7.0.1') + end + + context 'when AIO puppet agent is a dev build' do + before do + allow(Facter::Util::FileHelper) + .to receive(:safe_read) + .with('/opt/puppetlabs/puppet/VERSION', nil) + .and_return('7.0.1.8.g12345678') + end + + it 'only shows the first 4 groups of digits' do + expect(Facter::Resolvers::AioAgentVersion.resolve(:aio_agent_version)).to eql('7.0.1.8') + end end end end diff --git a/spec/facter/resolvers/windows/aio_agent_version_spec.rb b/spec/facter/resolvers/windows/aio_agent_version_spec.rb new file mode 100644 index 000000000..8d416c84b --- /dev/null +++ b/spec/facter/resolvers/windows/aio_agent_version_spec.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +describe Facter::Resolvers::Windows::AioAgentVersion do + describe '#resolve' do + subject(:aio_agent_resolver) { Facter::Resolvers::Windows::AioAgentVersion } + + let(:puppet_version) { '7.0.1' } + let(:reg) { instance_spy(Win32::Registry::HKEY_LOCAL_MACHINE) } + let(:log) { instance_spy(Facter::Log) } + + before do + allow(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).with('SOFTWARE\\Puppet Labs\\Puppet').and_yield(reg) + allow(Facter::Util::FileHelper) + .to receive(:safe_read) + .with('path_to_puppet/VERSION', nil) + .and_return(puppet_version) + allow(Facter::Resolvers::BaseResolver).to receive(:log).and_return(log) + end + + after do + Facter::Resolvers::Windows::AioAgentVersion.invalidate_cache + end + + context 'when the registry path is incorrect' do + before do + allow(Win32::Registry::HKEY_LOCAL_MACHINE) + .to receive(:open) + .with('SOFTWARE\\Puppet Labs\\Puppet') + .and_raise(Win32::Registry::Error) + end + + it 'logs debug message specific to none existent path' do + aio_agent_resolver.resolve(:aio_agent_version) + + expect(log).to have_received(:debug).with('The registry path SOFTWARE\Puppet Labs\Puppet does not exist') + end + end + + context 'when windows is 64 bit edition' do + before do + allow(reg).to receive(:read).with('RememberedInstallDir64').and_return([1, 'path_to_puppet']) + end + + it 'returns path from registry specific to 64 bit windows' do + expect(aio_agent_resolver.resolve(:aio_agent_version)).to eq(puppet_version) + end + + context 'when RememberedInstallDir64 does not contain a value' do + before do + allow(reg).to receive(:read).with('RememberedInstallDir64').and_return([1, '']) + end + + it 'calls file helper with empty path' do + expect(aio_agent_resolver.resolve(:aio_agent_version)).to be(nil) + end + end + + context 'when AIO puppet agent is a dev build' do + before do + allow(Facter::Util::FileHelper) + .to receive(:safe_read) + .with('path_to_puppet/VERSION', nil) + .and_return('7.0.1.8.g12345678') + end + + it 'only shows the first 4 groups of digits' do + expect(aio_agent_resolver.resolve(:aio_agent_version)).to eql('7.0.1.8') + end + end + end + + context 'when windows is 32 bit edition' do + before do + allow(reg).to receive(:read).with('RememberedInstallDir64').and_raise(Win32::Registry::Error) + allow(reg).to receive(:read).with('RememberedInstallDir').and_return([1, 'path_to_puppet']) + end + + it 'logs debug message for 64 bit register' do + aio_agent_resolver.resolve(:aio_agent_version) + + expect(log).to have_received(:debug).with('Could not read Puppet AIO path from 64 bit registry') + end + + it 'returns path from registry specific to 32 bit windows' do + expect(aio_agent_resolver.resolve(:aio_agent_version)).to eq(puppet_version) + end + + context 'when there is no registry entry for 32 bit version' do + before do + allow(reg).to receive(:read).with('RememberedInstallDir').and_raise(Win32::Registry::Error) + end + + it 'logs debug error for 32 bit registry' do + aio_agent_resolver.resolve(:aio_agent_version) + + expect(log).to have_received(:debug).with('Could not read Puppet AIO path from 32 bit registry') + end + end + end + end +end diff --git a/spec/mocks/win32_mock.rb b/spec/mocks/win32_mock.rb index cd003658c..b5df44d3d 100644 --- a/spec/mocks/win32_mock.rb +++ b/spec/mocks/win32_mock.rb @@ -14,6 +14,8 @@ def each; end def [](*); end def keys; end + + def read(*); end end end end diff --git a/spec/mocks/win32_registry_error_mock.rb b/spec/mocks/win32_registry_error_mock.rb new file mode 100644 index 000000000..a26caec87 --- /dev/null +++ b/spec/mocks/win32_registry_error_mock.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Win32 + module Registry + class Error < RuntimeError + end + end +end