facter 4.0.38 → 4.0.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/bin/facter +3 -4
  3. data/lib/facter.rb +138 -27
  4. data/lib/facter/custom_facts/core/aggregate.rb +51 -17
  5. data/lib/facter/custom_facts/core/execution.rb +27 -35
  6. data/lib/facter/custom_facts/core/execution/base.rb +42 -6
  7. data/lib/facter/custom_facts/util/directory_loader.rb +1 -1
  8. data/lib/facter/custom_facts/util/resolution.rb +40 -11
  9. data/lib/facter/facts/aix/disks.rb +1 -1
  10. data/lib/facter/facts/aix/kernel.rb +1 -1
  11. data/lib/facter/facts/aix/kernelmajversion.rb +1 -1
  12. data/lib/facter/facts/aix/kernelrelease.rb +1 -1
  13. data/lib/facter/facts/aix/kernelversion.rb +1 -1
  14. data/lib/facter/facts/aix/os/release.rb +1 -1
  15. data/lib/facter/facts/linux/cloud.rb +15 -0
  16. data/lib/facter/facts/linux/disks.rb +1 -1
  17. data/lib/facter/facts/linux/ec2_metadata.rb +5 -27
  18. data/lib/facter/facts/linux/ec2_userdata.rb +5 -27
  19. data/lib/facter/facts/linux/is_virtual.rb +7 -46
  20. data/lib/facter/facts/linux/virtual.rb +3 -58
  21. data/lib/facter/facts/solaris/disks.rb +1 -1
  22. data/lib/facter/facts/solaris/zones.rb +1 -1
  23. data/lib/facter/facts_utils/virtual_detector.rb +78 -0
  24. data/lib/facter/framework/cli/cli.rb +83 -36
  25. data/lib/facter/framework/cli/cli_launcher.rb +34 -38
  26. data/lib/facter/framework/config/fact_groups.rb +36 -2
  27. data/lib/facter/framework/core/cache_manager.rb +40 -23
  28. data/lib/facter/framework/core/fact_loaders/fact_loader.rb +14 -11
  29. data/lib/facter/framework/core/options/config_file_options.rb +5 -3
  30. data/lib/facter/framework/core/options/option_store.rb +27 -10
  31. data/lib/facter/framework/logging/logger.rb +1 -5
  32. data/lib/facter/patches/sysfilesystem/sys/statvfs.rb +92 -0
  33. data/lib/facter/resolvers/aix/os_level.rb +27 -0
  34. data/lib/facter/resolvers/cloud.rb +39 -0
  35. data/lib/facter/resolvers/dmi_decode.rb +1 -0
  36. data/lib/facter/resolvers/dmi_resolver.rb +2 -2
  37. data/lib/facter/resolvers/memory_resolver.rb +12 -14
  38. data/lib/facter/resolvers/mountpoints_resolver.rb +5 -2
  39. data/lib/facter/resolvers/networking_linux_resolver.rb +10 -5
  40. data/lib/facter/resolvers/processors_resolver.rb +5 -1
  41. data/lib/facter/resolvers/solaris/mountpoints.rb +60 -0
  42. data/lib/facter/resolvers/solaris/networking.rb +1 -1
  43. data/lib/facter/resolvers/utils/filesystem_helper.rb +3 -1
  44. data/lib/facter/version.rb +1 -1
  45. metadata +8 -4
  46. data/lib/facter/resolvers/aix/os_level_resolver.rb +0 -25
  47. data/lib/facter/resolvers/os_level_resolver.rb +0 -28
@@ -3,10 +3,6 @@
3
3
  module Facter
4
4
  module Core
5
5
  module Execution
6
- # require_relative 'execution/base'
7
- # require_relative 'execution/windows'
8
- # require_relative 'execution/posix'
9
-
10
6
  @@impl = if LegacyFacter::Util::Config.windows?
11
7
  Facter::Core::Execution::Windows.new
12
8
  else
@@ -20,24 +16,22 @@ module Facter
20
16
  module_function
21
17
 
22
18
  # Returns the locations to be searched when looking for a binary. This
23
- # is currently determined by the +PATH+ environment variable plus
24
- # `/sbin` and `/usr/sbin` when run on unix
19
+ # is currently determined by the +PATH+ environment variable plus
20
+ # `/sbin` and `/usr/sbin` when run on unix
21
+ #
22
+ # @return [Array<String>] The paths to be searched for binaries
25
23
  #
26
- # @return [Array<String>] the paths to be searched for binaries
27
24
  # @api private
28
25
  def search_paths
29
26
  @@impl.search_paths
30
27
  end
31
28
 
32
29
  # Determines the full path to a binary. If the supplied filename does not
33
- # already describe an absolute path then different locations (determined
34
- # by {search_paths}) will be searched for a match.
30
+ # already describe an absolute path then different locations (determined
31
+ # by {search_paths}) will be searched for a match.
32
+ # @param bin [String] The executable to locate
35
33
  #
36
- # Returns nil if no matching executable can be found otherwise returns
37
- # the expanded pathname.
38
- #
39
- # @param bin [String] the executable to locate
40
- # @return [String,nil] the full path to the executable or nil if not
34
+ # @return [String/nil] The full path to the executable or nil if not
41
35
  # found
42
36
  #
43
37
  # @api public
@@ -46,10 +40,12 @@ module Facter
46
40
  end
47
41
 
48
42
  # Determine in a platform-specific way whether a path is absolute. This
49
- # defaults to the local platform if none is specified.
43
+ # defaults to the local platform if none is specified.
44
+ # @param path [String] The path to check
45
+
46
+ # @param platform [:posix/:windows/nil] The platform logic to use
50
47
  #
51
- # @param path [String] the path to check
52
- # @param platform [:posix,:windows,nil] the platform logic to use
48
+ # @api private
53
49
  def absolute_path?(path, platform = nil)
54
50
  case platform
55
51
  when :posix
@@ -62,38 +58,35 @@ module Facter
62
58
  end
63
59
 
64
60
  # Given a command line, this returns the command line with the
65
- # executable written as an absolute path. If the executable contains
66
- # spaces, it has be put in double quotes to be properly recognized.
67
- #
61
+ # executable written as an absolute path. If the executable contains
62
+ # spaces, it has to be put in double quotes to be properly recognized.
68
63
  # @param command [String] the command line
69
64
  #
70
- # @return [String, nil] the command line with the executable's path
71
- # expanded, or nil if the executable cannot be found.
65
+ # @return [String/nil] The command line with the executable's path
66
+ # expanded, or nil if the executable cannot be found.
67
+ #
68
+ # @api private
72
69
  def expand_command(command)
73
70
  @@impl.expand_command(command)
74
71
  end
75
72
 
76
73
  # Overrides environment variables within a block of code. The
77
- # specified values will be set for the duration of the block, after
78
- # which the original values (if any) will be restored.
79
- #
80
- # @overload with_env(values, { || ... })
81
- #
74
+ # specified values will be set for the duration of the block, after
75
+ # which the original values (if any) will be restored.
82
76
  # @param values [Hash<String=>String>] A hash of the environment
83
77
  # variables to override
84
78
  #
85
- # @return [void]
79
+ # @return [String] The block's return string
86
80
  #
87
- # @api public
81
+ # @api private
88
82
  def with_env(values, &block)
89
83
  @@impl.with_env(values, &block)
90
84
  end
91
85
 
92
86
  # Try to execute a command and return the output.
87
+ # @param command [String] Command to run
93
88
  #
94
- # @param code [String] the program to run
95
- #
96
- # @return [String] the output of the program, or nil if the command does
89
+ # @return [String/nil] Output of the program, or nil if the command does
97
90
  # not exist or could not be executed.
98
91
  #
99
92
  # @deprecated Use #{execute} instead
@@ -103,9 +96,9 @@ module Facter
103
96
  end
104
97
 
105
98
  # Execute a command and return the output of that program.
99
+ # @param command [String] Command to run
106
100
  #
107
- # @param code [String] the program to run
108
- # @param options [Hash]
101
+ # @param options [Hash] Hash with options for the aggregate fact
109
102
  #
110
103
  # @option options [Object] :on_fail How to behave when the command could
111
104
  # not be run. Specifying :raise will raise an error, anything else will
@@ -118,7 +111,6 @@ module Facter
118
111
  # command execution failed and :on_fail was specified.
119
112
  #
120
113
  # @api public
121
- # @since 2.0.1
122
114
  def execute(command, options = {})
123
115
  @@impl.execute(command, options)
124
116
  end
@@ -6,6 +6,10 @@ module Facter
6
6
  class Base
7
7
  STDERR_MESSAGE = 'Command %s resulted with the following stderr message: %s'
8
8
 
9
+ def initialize
10
+ @log = Log.new(self)
11
+ end
12
+
9
13
  # This is part of the public API. No race condition can happen
10
14
  # here because custom facts are executed sequentially
11
15
  def with_env(values)
@@ -36,9 +40,7 @@ module Facter
36
40
  end
37
41
 
38
42
  def execute(command, options = {})
39
- on_fail = options.fetch(:on_fail, :raise)
40
- expand = options.fetch(:expand, true)
41
- logger = options[:logger]
43
+ on_fail, expand, logger, time_limit = extract_options(options)
42
44
 
43
45
  expanded_command = if !expand && builtin_command?(command) || logger
44
46
  command
@@ -55,11 +57,21 @@ module Facter
55
57
  return on_fail
56
58
  end
57
59
 
58
- execute_command(expanded_command, on_fail, logger)
60
+ execute_command(expanded_command, on_fail, logger, time_limit)
59
61
  end
60
62
 
61
63
  private
62
64
 
65
+ def extract_options(options)
66
+ on_fail = options.fetch(:on_fail, :raise)
67
+ expand = options.fetch(:expand, true)
68
+ logger = options[:logger]
69
+ time_limit = options[:limit].to_i
70
+ time_limit = time_limit.positive? ? time_limit : nil
71
+
72
+ [on_fail, expand, logger, time_limit]
73
+ end
74
+
63
75
  def log_stderr(msg, command, logger)
64
76
  return if !msg || msg.empty?
65
77
 
@@ -77,12 +89,36 @@ module Facter
77
89
  output.chomp =~ /builtin/ ? true : false
78
90
  end
79
91
 
80
- def execute_command(command, on_fail, logger = nil)
92
+ def execute_command(command, on_fail, logger = nil, time_limit = nil)
93
+ time_limit ||= 300
81
94
  begin
82
95
  # Set LC_ALL and LANG to force i18n to C for the duration of this exec;
83
96
  # this ensures that any code that parses the
84
97
  # output of the command can expect it to be in a consistent / predictable format / locale
85
- out, stderr, _status_ = Open3.capture3({ 'LC_ALL' => 'C', 'LANG' => 'C' }, command.to_s)
98
+ opts = { 'LC_ALL' => 'C', 'LANG' => 'C' }
99
+ require 'timeout'
100
+ @log.debug("Executing command: #{command}")
101
+ out, stderr = Open3.popen3(opts, command.to_s) do |_, stdout, stderr, wait_thr|
102
+ pid = wait_thr.pid
103
+ stdout_messages = +''
104
+ stderr_messages = +''
105
+ out_reader = Thread.new { stdout.read }
106
+ err_reader = Thread.new { stderr.read }
107
+ begin
108
+ Timeout.timeout(time_limit) do
109
+ stdout_messages << out_reader.value
110
+ stderr_messages << err_reader.value
111
+ end
112
+ rescue Timeout::Error
113
+ message = "Timeout encounter after #{time_limit}s, killing process with pid: #{pid}"
114
+ Process.kill('KILL', pid)
115
+ on_fail == :raise ? (raise StandardError, message) : @log.debug(message)
116
+ ensure
117
+ out_reader.kill
118
+ err_reader.kill
119
+ end
120
+ [stdout_messages, stderr_messages]
121
+ end
86
122
  log_stderr(stderr, command, logger)
87
123
  rescue StandardError => e
88
124
  return '' if logger
@@ -59,7 +59,7 @@ module LegacyFacter
59
59
  basename = File.basename(file)
60
60
  next if file_blocked?(basename)
61
61
 
62
- if facts.find { |f| f.name == basename } && cm.group_cached?(basename)
62
+ if facts.find { |f| f.name == basename } && cm.fact_cache_enabled?(basename)
63
63
  Facter.log_exception(Exception.new("Caching is enabled for group \"#{basename}\" while "\
64
64
  'there are at least two external facts files with the same filename'))
65
65
  else
@@ -14,6 +14,8 @@ module Facter
14
14
  class Resolution
15
15
  # @api private
16
16
  attr_accessor :code, :fact_type
17
+
18
+ # @api private
17
19
  attr_writer :value
18
20
 
19
21
  extend Facter::Core::Execution
@@ -24,7 +26,12 @@ module Facter
24
26
  # compatibility.
25
27
  #
26
28
  # @deprecated
27
- public :search_paths, :which, :absolute_path?, :expand_command, :with_env, :exec
29
+ #
30
+ # @api public
31
+ public :which, :exec
32
+
33
+ # @api private
34
+ public :with_env
28
35
  end
29
36
 
30
37
  include LegacyFacter::Core::Resolvable
@@ -32,22 +39,27 @@ module Facter
32
39
 
33
40
  # @!attribute [rw] name
34
41
  # The name of this resolution. The resolution name should be unique with
35
- # respect to the given fact.
42
+ # respect to the given fact.
43
+ #
36
44
  # @return [String]
45
+ #
37
46
  # @api public
38
47
  attr_accessor :name
39
48
 
40
49
  # @!attribute [r] fact
41
- # @return [Facter::Util::Fact]
50
+ #
51
+ # @return [Facter::Util::Fact] Associated fact with this resolution.
52
+ #
42
53
  # @api private
43
54
  attr_reader :fact
44
55
 
45
56
  # Create a new resolution mechanism.
46
57
  #
47
58
  # @param name [String] The name of the resolution.
48
- # @return [void]
49
59
  #
50
- # @api private
60
+ # @return [Facter::Util::Resolution] The created resolution
61
+ #
62
+ # @api public
51
63
  def initialize(name, fact)
52
64
  @name = name
53
65
  @fact = fact
@@ -57,6 +69,11 @@ module Facter
57
69
  @weight = nil
58
70
  end
59
71
 
72
+ # Returns the fact's resolution type
73
+ #
74
+ # @return [Symbol] The fact's type
75
+ #
76
+ # @api private
60
77
  def resolution_type
61
78
  :simple
62
79
  end
@@ -64,7 +81,9 @@ module Facter
64
81
  # Evaluate the given block in the context of this resolution. If a block has
65
82
  # already been evaluated emit a warning to that effect.
66
83
  #
67
- # @return [void]
84
+ # @return [String] Result of the block's evaluation
85
+ #
86
+ # @api private
68
87
  def evaluate(&block)
69
88
  if @last_evaluated
70
89
  msg = "Already evaluated #{@name}"
@@ -85,6 +104,11 @@ module Facter
85
104
  end
86
105
  end
87
106
 
107
+ # Sets options for the aggregate fact
108
+ #
109
+ # @return [nil]
110
+ #
111
+ # @api private
88
112
  def options(options)
89
113
  accepted_options = %i[name value timeout weight fact_type file]
90
114
 
@@ -98,8 +122,6 @@ module Facter
98
122
  # Sets the code block or external program that will be evaluated to
99
123
  # get the value of the fact.
100
124
  #
101
- # @return [void]
102
- #
103
125
  # @overload setcode(string)
104
126
  # Sets an external program to call to get the value of the resolution
105
127
  # @param [String] string the external program to run to get the
@@ -111,6 +133,8 @@ module Facter
111
133
  # This block is run when the fact is evaluated. Errors raised from
112
134
  # inside the block are rescued and printed to stderr.
113
135
  #
136
+ # @return [Facter::Util::Resolution] Returns itself
137
+ #
114
138
  # @api public
115
139
  def setcode(string = nil, &block)
116
140
  if string
@@ -127,11 +151,16 @@ module Facter
127
151
  else
128
152
  raise ArgumentError, 'You must pass either code or a block'
129
153
  end
154
+ self
130
155
  end
131
156
 
132
- # Comparation is done based on weight and fact type.
133
- # The greatter the weight, the higher the priority.
134
- # If weights are equal, we consider external facts greater than custom facts
157
+ # Comparison is done based on weight and fact type.
158
+ # The greater the weight, the higher the priority.
159
+ # If weights are equal, we consider external facts greater than custom facts.
160
+ #
161
+ # @return [bool] Weight comparison result
162
+ #
163
+ # @api private
135
164
  def <=>(other)
136
165
  return compare_equal_weights(other) if weight == other.weight
137
166
  return 1 if weight > other.weight
@@ -24,7 +24,7 @@ module Facts
24
24
 
25
25
  def add_legacy_facts(disks, facts)
26
26
  disks.each do |disk_name, disk_info|
27
- facts.push(Facter::ResolvedFact.new("blockdevice_#{disk_name}_size", disk_info[:size_bytes].to_s, :legacy))
27
+ facts.push(Facter::ResolvedFact.new("blockdevice_#{disk_name}_size", disk_info[:size_bytes], :legacy))
28
28
  end
29
29
  end
30
30
  end
@@ -6,7 +6,7 @@ module Facts
6
6
  FACT_NAME = 'kernel'
7
7
 
8
8
  def call_the_resolver
9
- fact_value = Facter::Resolvers::OsLevel.resolve(:kernel)
9
+ fact_value = Facter::Resolvers::Aix::OsLevel.resolve(:kernel)
10
10
 
11
11
  Facter::ResolvedFact.new(FACT_NAME, fact_value)
12
12
  end
@@ -6,7 +6,7 @@ module Facts
6
6
  FACT_NAME = 'kernelmajversion'
7
7
 
8
8
  def call_the_resolver
9
- fact_value = Facter::Resolvers::OsLevel.resolve(:build)
9
+ fact_value = Facter::Resolvers::Aix::OsLevel.resolve(:build)
10
10
  kernelmajversion = fact_value.split('-')[0]
11
11
 
12
12
  Facter::ResolvedFact.new(FACT_NAME, kernelmajversion)
@@ -6,7 +6,7 @@ module Facts
6
6
  FACT_NAME = 'kernelrelease'
7
7
 
8
8
  def call_the_resolver
9
- fact_value = Facter::Resolvers::OsLevel.resolve(:build).strip
9
+ fact_value = Facter::Resolvers::Aix::OsLevel.resolve(:build).strip
10
10
 
11
11
  Facter::ResolvedFact.new(FACT_NAME, fact_value)
12
12
  end
@@ -6,7 +6,7 @@ module Facts
6
6
  FACT_NAME = 'kernelversion'
7
7
 
8
8
  def call_the_resolver
9
- fact_value = Facter::Resolvers::OsLevel.resolve(:build)
9
+ fact_value = Facter::Resolvers::Aix::OsLevel.resolve(:build)
10
10
  kernelversion = fact_value.split('-')[0]
11
11
 
12
12
  Facter::ResolvedFact.new(FACT_NAME, kernelversion)
@@ -8,7 +8,7 @@ module Facts
8
8
  ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
9
9
 
10
10
  def call_the_resolver
11
- fact_value = Facter::Resolvers::OsLevel.resolve(:build)
11
+ fact_value = Facter::Resolvers::Aix::OsLevel.resolve(:build)
12
12
  major = fact_value.split('-')[0]
13
13
 
14
14
  [Facter::ResolvedFact.new(FACT_NAME, full: fact_value.strip, major: major),
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Facts
4
+ module Linux
5
+ class Cloud
6
+ FACT_NAME = 'cloud'
7
+
8
+ def call_the_resolver
9
+ cloud_provider = Facter::Resolvers::Cloud.resolve(:cloud_provider)
10
+
11
+ Facter::ResolvedFact.new(FACT_NAME, cloud_provider)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -25,7 +25,7 @@ module Facts
25
25
  def add_legacy_facts(disks, facts)
26
26
  disks.each do |disk_name, disk_info|
27
27
  facts.push(Facter::ResolvedFact.new("blockdevice_#{disk_name}_model", disk_info[:model], :legacy))
28
- facts.push(Facter::ResolvedFact.new("blockdevice_#{disk_name}_size", disk_info[:size_bytes].to_s, :legacy))
28
+ facts.push(Facter::ResolvedFact.new("blockdevice_#{disk_name}_size", disk_info[:size_bytes], :legacy))
29
29
  facts.push(Facter::ResolvedFact.new("blockdevice_#{disk_name}_vendor", disk_info[:vendor], :legacy))
30
30
  end
31
31
  end
@@ -5,6 +5,10 @@ module Facts
5
5
  class Ec2Metadata
6
6
  FACT_NAME = 'ec2_metadata'
7
7
 
8
+ def initialize
9
+ @virtual = Facter::VirtualDetector.new
10
+ end
11
+
8
12
  def call_the_resolver
9
13
  return Facter::ResolvedFact.new(FACT_NAME, nil) unless aws_hypervisors?
10
14
 
@@ -16,33 +20,7 @@ module Facts
16
20
  private
17
21
 
18
22
  def aws_hypervisors?
19
- virtual = check_virt_what || check_xen || check_product_name || check_bios_vendor || check_lspci
20
-
21
- virtual == 'kvm' || virtual =~ /xen/
22
- end
23
-
24
- def check_virt_what
25
- Facter::Resolvers::VirtWhat.resolve(:vm)
26
- end
27
-
28
- def check_xen
29
- Facter::Resolvers::Xen.resolve(:vm)
30
- end
31
-
32
- def check_product_name
33
- product_name = Facter::Resolvers::Linux::DmiBios.resolve(:product_name)
34
- return unless product_name
35
-
36
- Facter::FactsUtils::HYPERVISORS_HASH.each { |key, value| return value if product_name.include?(key) }
37
- end
38
-
39
- def check_bios_vendor
40
- bios_vendor = Facter::Resolvers::Linux::DmiBios.resolve(:bios_vendor)
41
- return 'kvm' if bios_vendor&.include?('Amazon EC2')
42
- end
43
-
44
- def check_lspci
45
- Facter::Resolvers::Lspci.resolve(:vm)
23
+ @virtual.platform =~ /kvm|xen|aws/
46
24
  end
47
25
  end
48
26
  end